mirror of https://github.com/perkeep/perkeep.git
340 lines
8.3 KiB
Go
340 lines
8.3 KiB
Go
// Copyright 2013 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package cr2 implements rudimentary support for reading Canon Camera Raw 2
|
|
// (CR2) files.
|
|
//
|
|
// CR2 is a bastardized TIFF file with a JPEG file inside it (yeah, thanks Canon).
|
|
// This package is a stripped back version of code.google.com/p/go.image/tiff.
|
|
//
|
|
// Known limitations:
|
|
//
|
|
// Because TIFF files and CR2 files share the same first few bytes, the image
|
|
// package's file type detection will fail to recognize a cr2 if the tiff
|
|
// reader is also imported.
|
|
package cr2
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"image"
|
|
"image/color"
|
|
"io"
|
|
|
|
"camlistore.org/third_party/go/pkg/image/jpeg"
|
|
)
|
|
|
|
// A FormatError reports that the input is not a valid TIFF image.
|
|
type FormatError string
|
|
|
|
func (e FormatError) Error() string {
|
|
return "cr2: invalid format: " + string(e)
|
|
}
|
|
|
|
// An UnsupportedError reports that the input uses a valid but
|
|
// unimplemented feature.
|
|
type UnsupportedError string
|
|
|
|
func (e UnsupportedError) Error() string {
|
|
return "cr2: unsupported feature: " + string(e)
|
|
}
|
|
|
|
// An InternalError reports that an internal error was encountered.
|
|
type InternalError string
|
|
|
|
func (e InternalError) Error() string {
|
|
return "cr2: internal error: " + string(e)
|
|
}
|
|
|
|
type decoder struct {
|
|
r io.ReaderAt
|
|
byteOrder binary.ByteOrder
|
|
config image.Config
|
|
mode imageMode
|
|
bpp uint
|
|
features map[int][]uint
|
|
palette []color.Color
|
|
|
|
buf []byte
|
|
off int // Current offset in buf.
|
|
v uint32 // Buffer value for reading with arbitrary bit depths.
|
|
nbits uint // Remaining number of bits in v.
|
|
}
|
|
|
|
// firstVal returns the first uint of the features entry with the given tag,
|
|
// or 0 if the tag does not exist.
|
|
func (d *decoder) firstVal(tag int) uint {
|
|
f := d.features[tag]
|
|
if len(f) == 0 {
|
|
return 0
|
|
}
|
|
return f[0]
|
|
}
|
|
|
|
// ifdUint decodes the IFD entry in p, which must be of the Byte, Short
|
|
// or Long type, and returns the decoded uint values.
|
|
func (d *decoder) ifdUint(p []byte) (u []uint, err error) {
|
|
var raw []byte
|
|
datatype := d.byteOrder.Uint16(p[2:4])
|
|
count := d.byteOrder.Uint32(p[4:8])
|
|
if datalen := lengths[datatype] * count; datalen > 4 {
|
|
// The IFD contains a pointer to the real value.
|
|
raw = make([]byte, datalen)
|
|
_, err = d.r.ReadAt(raw, int64(d.byteOrder.Uint32(p[8:12])))
|
|
} else {
|
|
raw = p[8 : 8+datalen]
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
u = make([]uint, count)
|
|
switch datatype {
|
|
case dtByte:
|
|
for i := uint32(0); i < count; i++ {
|
|
u[i] = uint(raw[i])
|
|
}
|
|
case dtShort:
|
|
for i := uint32(0); i < count; i++ {
|
|
u[i] = uint(d.byteOrder.Uint16(raw[2*i : 2*(i+1)]))
|
|
}
|
|
case dtLong:
|
|
for i := uint32(0); i < count; i++ {
|
|
u[i] = uint(d.byteOrder.Uint32(raw[4*i : 4*(i+1)]))
|
|
}
|
|
default:
|
|
return nil, UnsupportedError("data type")
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
// parseIFD decides whether the the IFD entry in p is "interesting" and
|
|
// stows away the data in the decoder.
|
|
func (d *decoder) parseIFD(p []byte) error {
|
|
tag := d.byteOrder.Uint16(p[0:2])
|
|
switch tag {
|
|
case tBitsPerSample,
|
|
tExtraSamples,
|
|
tPhotometricInterpretation,
|
|
tCompression,
|
|
tPredictor,
|
|
tStripOffsets,
|
|
tStripByteCounts,
|
|
tRowsPerStrip,
|
|
tTileWidth,
|
|
tTileLength,
|
|
tTileOffsets,
|
|
tTileByteCounts,
|
|
tImageLength,
|
|
tImageWidth:
|
|
val, err := d.ifdUint(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
d.features[int(tag)] = val
|
|
case tColorMap:
|
|
val, err := d.ifdUint(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
numcolors := len(val) / 3
|
|
if len(val)%3 != 0 || numcolors <= 0 || numcolors > 256 {
|
|
return FormatError("bad ColorMap length")
|
|
}
|
|
d.palette = make([]color.Color, numcolors)
|
|
for i := 0; i < numcolors; i++ {
|
|
d.palette[i] = color.RGBA64{
|
|
uint16(val[i]),
|
|
uint16(val[i+numcolors]),
|
|
uint16(val[i+2*numcolors]),
|
|
0xffff,
|
|
}
|
|
}
|
|
case tSampleFormat:
|
|
// Page 27 of the spec: If the SampleFormat is present and
|
|
// the value is not 1 [= unsigned integer data], a Baseline
|
|
// TIFF reader that cannot handle the SampleFormat value
|
|
// must terminate the import process gracefully.
|
|
val, err := d.ifdUint(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, v := range val {
|
|
if v != 1 {
|
|
return UnsupportedError("sample format")
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// readBits reads n bits from the internal buffer starting at the current offset.
|
|
func (d *decoder) readBits(n uint) uint32 {
|
|
for d.nbits < n {
|
|
d.v <<= 8
|
|
d.v |= uint32(d.buf[d.off])
|
|
d.off++
|
|
d.nbits += 8
|
|
}
|
|
d.nbits -= n
|
|
rv := d.v >> d.nbits
|
|
d.v &^= rv << d.nbits
|
|
return rv
|
|
}
|
|
|
|
// flushBits discards the unread bits in the buffer used by readBits.
|
|
// It is used at the end of a line.
|
|
func (d *decoder) flushBits() {
|
|
d.v = 0
|
|
d.nbits = 0
|
|
}
|
|
|
|
// minInt returns the smaller of x or y.
|
|
func minInt(a, b int) int {
|
|
if a <= b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func newDecoder(r io.Reader) (*decoder, error) {
|
|
d := &decoder{
|
|
r: newReaderAt(r),
|
|
features: make(map[int][]uint),
|
|
}
|
|
|
|
p := make([]byte, len(leHeader))
|
|
if _, err := d.r.ReadAt(p, 0); err != nil {
|
|
return nil, err
|
|
}
|
|
if string(p[0:len(leHeader)]) != leHeader {
|
|
return nil, FormatError("malformed header")
|
|
}
|
|
d.byteOrder = binary.LittleEndian
|
|
|
|
ifdOffset := int64(d.byteOrder.Uint32(p[4:8]))
|
|
|
|
// The first two bytes contain the number of entries (12 bytes each).
|
|
if _, err := d.r.ReadAt(p[0:2], ifdOffset); err != nil {
|
|
return nil, err
|
|
}
|
|
numItems := int(d.byteOrder.Uint16(p[0:2]))
|
|
|
|
// All IFD entries are read in one chunk.
|
|
p = make([]byte, ifdLen*numItems)
|
|
if _, err := d.r.ReadAt(p, ifdOffset+2); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for i := 0; i < len(p); i += ifdLen {
|
|
if err := d.parseIFD(p[i : i+ifdLen]); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
d.config.Width = int(d.firstVal(tImageWidth))
|
|
d.config.Height = int(d.firstVal(tImageLength))
|
|
|
|
if _, ok := d.features[tBitsPerSample]; !ok {
|
|
return nil, FormatError("BitsPerSample tag missing")
|
|
}
|
|
d.bpp = d.firstVal(tBitsPerSample)
|
|
|
|
// Determine the image mode.
|
|
switch d.firstVal(tPhotometricInterpretation) {
|
|
case pRGB:
|
|
for _, b := range d.features[tBitsPerSample] {
|
|
if b != 8 {
|
|
return nil, UnsupportedError("non-8-bit RGB image")
|
|
}
|
|
}
|
|
d.config.ColorModel = color.RGBAModel
|
|
// RGB images normally have 3 samples per pixel.
|
|
// If there are more, ExtraSamples (p. 31-32 of the spec)
|
|
// gives their meaning (usually an alpha channel).
|
|
//
|
|
// This implementation does not support extra samples
|
|
// of an unspecified type.
|
|
switch len(d.features[tBitsPerSample]) {
|
|
case 3:
|
|
d.mode = mRGB
|
|
case 4:
|
|
switch d.firstVal(tExtraSamples) {
|
|
case 1:
|
|
d.mode = mRGBA
|
|
case 2:
|
|
d.mode = mNRGBA
|
|
d.config.ColorModel = color.NRGBAModel
|
|
default:
|
|
return nil, FormatError("wrong number of samples for RGB")
|
|
}
|
|
default:
|
|
return nil, FormatError("wrong number of samples for RGB")
|
|
}
|
|
case pPaletted:
|
|
d.mode = mPaletted
|
|
d.config.ColorModel = color.Palette(d.palette)
|
|
case pWhiteIsZero:
|
|
d.mode = mGrayInvert
|
|
if d.bpp == 16 {
|
|
d.config.ColorModel = color.Gray16Model
|
|
} else {
|
|
d.config.ColorModel = color.GrayModel
|
|
}
|
|
case pBlackIsZero:
|
|
d.mode = mGray
|
|
if d.bpp == 16 {
|
|
d.config.ColorModel = color.Gray16Model
|
|
} else {
|
|
d.config.ColorModel = color.GrayModel
|
|
}
|
|
default:
|
|
return nil, UnsupportedError("color model")
|
|
}
|
|
|
|
return d, nil
|
|
}
|
|
|
|
// DecodeConfig returns the color model and dimensions of a TIFF image without
|
|
// decoding the entire image.
|
|
func DecodeConfig(r io.Reader) (image.Config, error) {
|
|
d, err := newDecoder(r)
|
|
if err != nil {
|
|
return image.Config{}, err
|
|
}
|
|
return d.config, nil
|
|
}
|
|
|
|
// NewReader returns an io.Reader to the JPEG thumbnail embedded in the CR2
|
|
// image in r. This allows access to the raw bytes of the JPEG thumbnail
|
|
// without the need to decompress it first.
|
|
func NewReader(r io.Reader) (io.Reader, error) {
|
|
d, err := newDecoder(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
offset := int64(d.features[tStripOffsets][0])
|
|
n := int64(d.features[tStripByteCounts][0])
|
|
switch d.firstVal(tCompression) {
|
|
case cJPEG, cJPEGOld:
|
|
default:
|
|
return nil, UnsupportedError("compression")
|
|
}
|
|
|
|
return io.NewSectionReader(d.r, offset, n), nil
|
|
}
|
|
|
|
// Decode reads a CR2 image from r and returns the embedded JPEG thumbnail as
|
|
// an image.Image.
|
|
func Decode(r io.Reader) (image.Image, error) {
|
|
r, err := NewReader(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return jpeg.Decode(r)
|
|
}
|
|
|
|
func init() {
|
|
image.RegisterFormat("cr2", leHeader, Decode, DecodeConfig)
|
|
}
|