mirror of
https://github.com/fumiama/imgsz.git
synced 2026-06-27 15:40:34 +08:00
feat: support bmp & tiff format
This commit is contained in:
137
bmp.go
Normal file
137
bmp.go
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
// Copyright 2011 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 bmp implements a BMP image decoder and encoder.
|
||||||
|
//
|
||||||
|
// The BMP specification is at http://www.digicamsoft.com/bmp/bmp.html.
|
||||||
|
package imgsz // import "golang.org/x/image/bmp"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"image/color"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrUnsupported means that the input BMP image uses a valid but unsupported
|
||||||
|
// feature.
|
||||||
|
var ErrUnsupported = errors.New("bmp: unsupported BMP image")
|
||||||
|
|
||||||
|
func readUint16(b []byte) uint16 {
|
||||||
|
return uint16(b[0]) | uint16(b[1])<<8
|
||||||
|
}
|
||||||
|
|
||||||
|
func readUint32(b []byte) uint32 {
|
||||||
|
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodebmp(r io.Reader) (size Size, err error) {
|
||||||
|
// We only support those BMP images with one of the following DIB headers:
|
||||||
|
// - BITMAPINFOHEADER (40 bytes)
|
||||||
|
// - BITMAPV4HEADER (108 bytes)
|
||||||
|
// - BITMAPV5HEADER (124 bytes)
|
||||||
|
const (
|
||||||
|
fileHeaderLen = 14
|
||||||
|
infoHeaderLen = 40
|
||||||
|
v4InfoHeaderLen = 108
|
||||||
|
v5InfoHeaderLen = 124
|
||||||
|
)
|
||||||
|
var b [1024]byte
|
||||||
|
if _, err := io.ReadFull(r, b[:fileHeaderLen+4]); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return Size{}, err
|
||||||
|
}
|
||||||
|
if string(b[:2]) != "BM" {
|
||||||
|
return Size{}, errors.New("bmp: invalid format")
|
||||||
|
}
|
||||||
|
offset := readUint32(b[10:14])
|
||||||
|
infoLen := readUint32(b[14:18])
|
||||||
|
if infoLen != infoHeaderLen && infoLen != v4InfoHeaderLen && infoLen != v5InfoHeaderLen {
|
||||||
|
return Size{}, ErrUnsupported
|
||||||
|
}
|
||||||
|
if _, err := io.ReadFull(r, b[fileHeaderLen+4:fileHeaderLen+infoLen]); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return Size{}, err
|
||||||
|
}
|
||||||
|
width := int(int32(readUint32(b[18:22])))
|
||||||
|
height := int(int32(readUint32(b[22:26])))
|
||||||
|
if height < 0 {
|
||||||
|
height = -height
|
||||||
|
}
|
||||||
|
if width < 0 || height < 0 {
|
||||||
|
return Size{}, ErrUnsupported
|
||||||
|
}
|
||||||
|
// We only support 1 plane and 8, 24 or 32 bits per pixel and no
|
||||||
|
// compression.
|
||||||
|
planes, bpp, compression := readUint16(b[26:28]), readUint16(b[28:30]), readUint32(b[30:34])
|
||||||
|
// if compression is set to BI_BITFIELDS, but the bitmask is set to the default bitmask
|
||||||
|
// that would be used if compression was set to 0, we can continue as if compression was 0
|
||||||
|
if compression == 3 && infoLen > infoHeaderLen &&
|
||||||
|
readUint32(b[54:58]) == 0xff0000 && readUint32(b[58:62]) == 0xff00 &&
|
||||||
|
readUint32(b[62:66]) == 0xff && readUint32(b[66:70]) == 0xff000000 {
|
||||||
|
compression = 0
|
||||||
|
}
|
||||||
|
if planes != 1 || compression != 0 {
|
||||||
|
return Size{}, ErrUnsupported
|
||||||
|
}
|
||||||
|
switch bpp {
|
||||||
|
case 8:
|
||||||
|
colorUsed := readUint32(b[46:50])
|
||||||
|
// If colorUsed is 0, it is set to the maximum number of colors for the given bpp, which is 2^bpp.
|
||||||
|
if colorUsed == 0 {
|
||||||
|
colorUsed = 256
|
||||||
|
} else if colorUsed > 256 {
|
||||||
|
return Size{}, ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset != fileHeaderLen+infoLen+colorUsed*4 {
|
||||||
|
return Size{}, ErrUnsupported
|
||||||
|
}
|
||||||
|
_, err = io.ReadFull(r, b[:colorUsed*4])
|
||||||
|
if err != nil {
|
||||||
|
return Size{}, err
|
||||||
|
}
|
||||||
|
pcm := make(color.Palette, colorUsed)
|
||||||
|
for i := range pcm {
|
||||||
|
// BMP images are stored in BGR order rather than RGB order.
|
||||||
|
// Every 4th byte is padding.
|
||||||
|
pcm[i] = color.RGBA{b[4*i+2], b[4*i+1], b[4*i+0], 0xFF}
|
||||||
|
}
|
||||||
|
return Size{Width: width, Height: height}, nil
|
||||||
|
case 24:
|
||||||
|
if offset != fileHeaderLen+infoLen {
|
||||||
|
return Size{}, ErrUnsupported
|
||||||
|
}
|
||||||
|
return Size{Width: width, Height: height}, nil
|
||||||
|
case 32:
|
||||||
|
if offset != fileHeaderLen+infoLen {
|
||||||
|
return Size{}, ErrUnsupported
|
||||||
|
}
|
||||||
|
// 32 bits per pixel is possibly RGBX (X is padding) or RGBA (A is
|
||||||
|
// alpha transparency). However, for BMP images, "Alpha is a
|
||||||
|
// poorly-documented and inconsistently-used feature" says
|
||||||
|
// https://source.chromium.org/chromium/chromium/src/+/bc0a792d7ebc587190d1a62ccddba10abeea274b:third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.cc;l=621
|
||||||
|
//
|
||||||
|
// That goes on to say "BITMAPV3HEADER+ have an alpha bitmask in the
|
||||||
|
// info header... so we respect it at all times... [For earlier
|
||||||
|
// (smaller) headers we] ignore alpha in Windows V3 BMPs except inside
|
||||||
|
// ICO files".
|
||||||
|
//
|
||||||
|
// "Ignore" means to always set alpha to 0xFF (fully opaque):
|
||||||
|
// https://source.chromium.org/chromium/chromium/src/+/bc0a792d7ebc587190d1a62ccddba10abeea274b:third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.h;l=272
|
||||||
|
//
|
||||||
|
// Confusingly, "Windows V3" does not correspond to BITMAPV3HEADER, but
|
||||||
|
// instead corresponds to the earlier (smaller) BITMAPINFOHEADER:
|
||||||
|
// https://source.chromium.org/chromium/chromium/src/+/bc0a792d7ebc587190d1a62ccddba10abeea274b:third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.cc;l=258
|
||||||
|
//
|
||||||
|
// This Go package does not support ICO files and the (infoLen >
|
||||||
|
// infoHeaderLen) condition distinguishes BITMAPINFOHEADER (40 bytes)
|
||||||
|
// vs later (larger) headers.
|
||||||
|
return Size{Width: width, Height: height}, nil
|
||||||
|
}
|
||||||
|
return Size{}, ErrUnsupported
|
||||||
|
}
|
||||||
@@ -57,4 +57,30 @@ func TestSizes(t *testing.T) {
|
|||||||
t.Fatal(sz, n)
|
t.Fatal(sz, n)
|
||||||
}
|
}
|
||||||
f.Close()
|
f.Close()
|
||||||
|
|
||||||
|
f, err = os.Open("testdata/test.bmp")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sz, n, err = DecodeSize(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if sz.Width != 677 || sz.Height != 487 || n != "bmp" {
|
||||||
|
t.Fatal(sz, n)
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
f, err = os.Open("testdata/test.tiff")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sz, n, err = DecodeSize(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if sz.Width != 1032 || sz.Height != 1457 || n != "tiff" {
|
||||||
|
t.Fatal(sz, n)
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
3
init.go
3
init.go
@@ -16,4 +16,7 @@ func init() {
|
|||||||
return Size{d.width, d.height}, nil
|
return Size{d.width, d.height}, nil
|
||||||
})
|
})
|
||||||
RegisterFormat("webp", "RIFF????WEBPVP8", decodewebp)
|
RegisterFormat("webp", "RIFF????WEBPVP8", decodewebp)
|
||||||
|
RegisterFormat("bmp", "BM????\x00\x00\x00\x00", decodebmp)
|
||||||
|
RegisterFormat("tiff", leHeader, decodetiff)
|
||||||
|
RegisterFormat("tiff", beHeader, decodetiff)
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
testdata/test.bmp
vendored
Normal file
BIN
testdata/test.bmp
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 966 KiB |
BIN
testdata/test.tiff
vendored
Normal file
BIN
testdata/test.tiff
vendored
Normal file
Binary file not shown.
255
tiff.go
Normal file
255
tiff.go
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
// Copyright 2011 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 tiff implements a TIFF image tiffdecoder and encoder.
|
||||||
|
//
|
||||||
|
// The TIFF specification is at http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
|
||||||
|
package imgsz // import "golang.org/x/image/tiff"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"image/color"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
const maxChunkSize = 10 << 20 // 10M
|
||||||
|
|
||||||
|
// safeReadAt is a verbatim copy of internal/saferio.ReadDataAt from the
|
||||||
|
// standard library, which is used to read data from a reader using a length
|
||||||
|
// provided by untrusted data, without allocating the entire slice ahead of time
|
||||||
|
// if it is large (>maxChunkSize). This allows us to avoid allocating giant
|
||||||
|
// slices before learning that we can't actually read that much data from the
|
||||||
|
// reader.
|
||||||
|
func safeReadAt(r io.ReaderAt, n uint64, off int64) ([]byte, error) {
|
||||||
|
if int64(n) < 0 || n != uint64(int(n)) {
|
||||||
|
// n is too large to fit in int, so we can't allocate
|
||||||
|
// a buffer large enough. Treat this as a read failure.
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < maxChunkSize {
|
||||||
|
buf := make([]byte, n)
|
||||||
|
_, err := r.ReadAt(buf, off)
|
||||||
|
if err != nil {
|
||||||
|
// io.SectionReader can return EOF for n == 0,
|
||||||
|
// but for our purposes that is a success.
|
||||||
|
if err != io.EOF || n > 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf []byte
|
||||||
|
buf1 := make([]byte, maxChunkSize)
|
||||||
|
for n > 0 {
|
||||||
|
next := n
|
||||||
|
if next > maxChunkSize {
|
||||||
|
next = maxChunkSize
|
||||||
|
}
|
||||||
|
_, err := r.ReadAt(buf1[:next], off)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf = append(buf, buf1[:next]...)
|
||||||
|
n -= next
|
||||||
|
off += int64(next)
|
||||||
|
}
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type tiffdecoder struct {
|
||||||
|
r io.ReaderAt
|
||||||
|
byteOrder binary.ByteOrder
|
||||||
|
config Size
|
||||||
|
features map[int][]uint
|
||||||
|
palette []color.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
// firstVal returns the first uint of the features entry with the given tag,
|
||||||
|
// or 0 if the tag does not exist.
|
||||||
|
func (d *tiffdecoder) 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 *tiffdecoder) ifdUint(p []byte) (u []uint, err error) {
|
||||||
|
var raw []byte
|
||||||
|
if len(p) < ifdLen {
|
||||||
|
return nil, FormatError("bad IFD entry")
|
||||||
|
}
|
||||||
|
|
||||||
|
datatype := d.byteOrder.Uint16(p[2:4])
|
||||||
|
if dt := int(datatype); dt <= 0 || dt >= len(lengths) {
|
||||||
|
return nil, UnsupportedError("IFD entry datatype")
|
||||||
|
}
|
||||||
|
|
||||||
|
count := d.byteOrder.Uint32(p[4:8])
|
||||||
|
if count > math.MaxInt32/lengths[datatype] {
|
||||||
|
return nil, FormatError("IFD data too large")
|
||||||
|
}
|
||||||
|
if datalen := lengths[datatype] * count; datalen > 4 {
|
||||||
|
// The IFD contains a pointer to the real value.
|
||||||
|
raw, err = safeReadAt(d.r, uint64(datalen), 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 IFD entry in p is "interesting" and
|
||||||
|
// stows away the data in the tiffdecoder. It returns the tag number of the
|
||||||
|
// entry and an error, if any.
|
||||||
|
func (d *tiffdecoder) parseIFD(p []byte) (int, 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,
|
||||||
|
tFillOrder,
|
||||||
|
tT4Options,
|
||||||
|
tT6Options:
|
||||||
|
val, err := d.ifdUint(p)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
d.features[int(tag)] = val
|
||||||
|
case tColorMap:
|
||||||
|
val, err := d.ifdUint(p)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
numcolors := len(val) / 3
|
||||||
|
if len(val)%3 != 0 || numcolors <= 0 || numcolors > 256 {
|
||||||
|
return 0, 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 0, err
|
||||||
|
}
|
||||||
|
for _, v := range val {
|
||||||
|
if v != 1 {
|
||||||
|
return 0, UnsupportedError("sample format")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return int(tag), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newtiffDecoder(r io.Reader) (*tiffdecoder, error) {
|
||||||
|
d := &tiffdecoder{
|
||||||
|
r: newReaderAt(r),
|
||||||
|
features: make(map[int][]uint),
|
||||||
|
}
|
||||||
|
|
||||||
|
p := make([]byte, 8)
|
||||||
|
if _, err := d.r.ReadAt(p, 0); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch string(p[0:4]) {
|
||||||
|
case leHeader:
|
||||||
|
d.byteOrder = binary.LittleEndian
|
||||||
|
case beHeader:
|
||||||
|
d.byteOrder = binary.BigEndian
|
||||||
|
default:
|
||||||
|
return nil, FormatError("malformed header")
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
var err error
|
||||||
|
p, err = safeReadAt(d.r, uint64(ifdLen*numItems), ifdOffset+2)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
prevTag := -1
|
||||||
|
for i := 0; i < len(p); i += ifdLen {
|
||||||
|
tag, err := d.parseIFD(p[i : i+ifdLen])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tag <= prevTag {
|
||||||
|
return nil, FormatError("tags are not sorted in ascending order")
|
||||||
|
}
|
||||||
|
prevTag = tag
|
||||||
|
}
|
||||||
|
|
||||||
|
d.config.Width = int(d.firstVal(tImageWidth))
|
||||||
|
d.config.Height = int(d.firstVal(tImageLength))
|
||||||
|
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodetiff returns the color model and dimensions of a TIFF image without
|
||||||
|
// decoding the entire image.
|
||||||
|
func decodetiff(r io.Reader) (Size, error) {
|
||||||
|
d, err := newtiffDecoder(r)
|
||||||
|
if err != nil {
|
||||||
|
return Size{}, err
|
||||||
|
}
|
||||||
|
return d.config, nil
|
||||||
|
}
|
||||||
69
tiffbuf.go
Normal file
69
tiffbuf.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright 2011 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 imgsz
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
// buffer buffers an io.Reader to satisfy io.ReaderAt.
|
||||||
|
type buffer struct {
|
||||||
|
r io.Reader
|
||||||
|
buf []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill reads data from b.r until the buffer contains at least end bytes.
|
||||||
|
func (b *buffer) fill(end int) error {
|
||||||
|
m := len(b.buf)
|
||||||
|
if end > m {
|
||||||
|
if end > cap(b.buf) {
|
||||||
|
newcap := 1024
|
||||||
|
for newcap < end {
|
||||||
|
newcap *= 2
|
||||||
|
}
|
||||||
|
newbuf := make([]byte, end, newcap)
|
||||||
|
copy(newbuf, b.buf)
|
||||||
|
b.buf = newbuf
|
||||||
|
} else {
|
||||||
|
b.buf = b.buf[:end]
|
||||||
|
}
|
||||||
|
if n, err := io.ReadFull(b.r, b.buf[m:end]); err != nil {
|
||||||
|
end = m + n
|
||||||
|
b.buf = b.buf[:end]
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *buffer) ReadAt(p []byte, off int64) (int, error) {
|
||||||
|
o := int(off)
|
||||||
|
end := o + len(p)
|
||||||
|
if int64(end) != off+int64(len(p)) {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
err := b.fill(end)
|
||||||
|
return copy(p, b.buf[o:end]), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slice returns a slice of the underlying buffer. The slice contains
|
||||||
|
// n bytes starting at offset off.
|
||||||
|
func (b *buffer) Slice(off, n int) ([]byte, error) {
|
||||||
|
end := off + n
|
||||||
|
if err := b.fill(end); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b.buf[off:end], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newReaderAt converts an io.Reader into an io.ReaderAt.
|
||||||
|
func newReaderAt(r io.Reader) io.ReaderAt {
|
||||||
|
if ra, ok := r.(io.ReaderAt); ok {
|
||||||
|
return ra
|
||||||
|
}
|
||||||
|
return &buffer{
|
||||||
|
r: r,
|
||||||
|
buf: make([]byte, 0, 1024),
|
||||||
|
}
|
||||||
|
}
|
||||||
64
tiffconsts.go
Normal file
64
tiffconsts.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
// Copyright 2011 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 imgsz
|
||||||
|
|
||||||
|
// A tiff image file contains one or more images. The metadata
|
||||||
|
// of each image is contained in an Image File Directory (IFD),
|
||||||
|
// which contains entries of 12 bytes each and is described
|
||||||
|
// on page 14-16 of the specification. An IFD entry consists of
|
||||||
|
//
|
||||||
|
// - a tag, which describes the signification of the entry,
|
||||||
|
// - the data type and length of the entry,
|
||||||
|
// - the data itself or a pointer to it if it is more than 4 bytes.
|
||||||
|
//
|
||||||
|
// The presence of a length means that each IFD is effectively an array.
|
||||||
|
|
||||||
|
const (
|
||||||
|
leHeader = "II\x2A\x00" // Header for little-endian files.
|
||||||
|
beHeader = "MM\x00\x2A" // Header for big-endian files.
|
||||||
|
|
||||||
|
ifdLen = 12 // Length of an IFD entry in bytes.
|
||||||
|
)
|
||||||
|
|
||||||
|
// Data types (p. 14-16 of the spec).
|
||||||
|
const (
|
||||||
|
dtByte = 1
|
||||||
|
dtASCII = 2
|
||||||
|
dtShort = 3
|
||||||
|
dtLong = 4
|
||||||
|
dtRational = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
// The length of one instance of each data type in bytes.
|
||||||
|
var lengths = [...]uint32{0, 1, 1, 2, 4, 8}
|
||||||
|
|
||||||
|
// Tags (see p. 28-41 of the spec).
|
||||||
|
const (
|
||||||
|
tImageWidth = 256
|
||||||
|
tImageLength = 257
|
||||||
|
tBitsPerSample = 258
|
||||||
|
tCompression = 259
|
||||||
|
tPhotometricInterpretation = 262
|
||||||
|
|
||||||
|
tFillOrder = 266
|
||||||
|
|
||||||
|
tStripOffsets = 273
|
||||||
|
tSamplesPerPixel = 277
|
||||||
|
tRowsPerStrip = 278
|
||||||
|
tStripByteCounts = 279
|
||||||
|
|
||||||
|
tT4Options = 292 // CCITT Group 3 options, a set of 32 flag bits.
|
||||||
|
tT6Options = 293 // CCITT Group 4 options, a set of 32 flag bits.
|
||||||
|
|
||||||
|
tTileWidth = 322
|
||||||
|
tTileLength = 323
|
||||||
|
tTileOffsets = 324
|
||||||
|
tTileByteCounts = 325
|
||||||
|
|
||||||
|
tPredictor = 317
|
||||||
|
tColorMap = 320
|
||||||
|
tExtraSamples = 338
|
||||||
|
tSampleFormat = 339
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user