go-exif/v2/undefined/exif_8828_oecf.go
Dustin Oprea 08ac8466dd ifd.go: Add type system for IFD IDs, names, and paths
This allows us to pass around individual tokens that we can use to
generate IDs, names, and qualified and non-qualified IFD paths, rather
than [necessarily] having to pass or store them individually.

The previous global variables were strings. Now they are IfdIdentity structs
and have been renamed to be more accurate (in general, as well as for this).
The previous variables have been kept but are now just assigned to the newer
variables. This is backwards-compatibility that will be removed in the future.
2020-05-20 14:07:16 -04:00

149 lines
3.1 KiB
Go

package exifundefined
import (
"bytes"
"fmt"
"encoding/binary"
"github.com/dsoprea/go-logging"
"github.com/dsoprea/go-exif/v2/common"
)
type Tag8828Oecf struct {
Columns uint16
Rows uint16
ColumnNames []string
Values []exifcommon.SignedRational
}
func (oecf Tag8828Oecf) String() string {
return fmt.Sprintf("Tag8828Oecf<COLUMNS=(%d) ROWS=(%d)>", oecf.Columns, oecf.Rows)
}
func (oecf Tag8828Oecf) EncoderName() string {
return "Codec8828Oecf"
}
type Codec8828Oecf struct {
}
func (Codec8828Oecf) Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
// TODO(dustin): Add test
oecf, ok := value.(Tag8828Oecf)
if ok == false {
log.Panicf("can only encode a Tag8828Oecf")
}
b := new(bytes.Buffer)
err = binary.Write(b, byteOrder, oecf.Columns)
log.PanicIf(err)
err = binary.Write(b, byteOrder, oecf.Rows)
log.PanicIf(err)
for _, name := range oecf.ColumnNames {
_, err := b.Write([]byte(name))
log.PanicIf(err)
_, err = b.Write([]byte{0})
log.PanicIf(err)
}
ve := exifcommon.NewValueEncoder(byteOrder)
ed, err := ve.Encode(oecf.Values)
log.PanicIf(err)
_, err = b.Write(ed.Encoded)
log.PanicIf(err)
return b.Bytes(), uint32(b.Len()), nil
}
func (Codec8828Oecf) Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
// TODO(dustin): Add test using known good data.
valueContext.SetUndefinedValueType(exifcommon.TypeByte)
valueBytes, err := valueContext.ReadBytes()
log.PanicIf(err)
oecf := Tag8828Oecf{}
oecf.Columns = valueContext.ByteOrder().Uint16(valueBytes[0:2])
oecf.Rows = valueContext.ByteOrder().Uint16(valueBytes[2:4])
columnNames := make([]string, oecf.Columns)
// startAt is where the current column name starts.
startAt := 4
// offset is our current position.
offset := startAt
currentColumnNumber := uint16(0)
for currentColumnNumber < oecf.Columns {
if valueBytes[offset] == 0 {
columnName := string(valueBytes[startAt:offset])
if len(columnName) == 0 {
log.Panicf("SFR column (%d) has zero length", currentColumnNumber)
}
columnNames[currentColumnNumber] = columnName
currentColumnNumber++
offset++
startAt = offset
continue
}
offset++
}
oecf.ColumnNames = columnNames
rawRationalBytes := valueBytes[offset:]
rationalSize := exifcommon.TypeSignedRational.Size()
if len(rawRationalBytes)%rationalSize > 0 {
log.Panicf("OECF signed-rationals not aligned: (%d) %% (%d) > 0", len(rawRationalBytes), rationalSize)
}
rationalCount := len(rawRationalBytes) / rationalSize
parser := new(exifcommon.Parser)
byteOrder := valueContext.ByteOrder()
items, err := parser.ParseSignedRationals(rawRationalBytes, uint32(rationalCount), byteOrder)
log.PanicIf(err)
oecf.Values = items
return oecf, nil
}
func init() {
registerDecoder(
exifcommon.IfdExifStandardIfdIdentity.UnindexedString(),
0x8828,
Codec8828Oecf{})
}