b71a28d3c0
support new types for import * tests pass * missing file * bug fixes
389 lines
10 KiB
Go
389 lines
10 KiB
Go
// Copyright 2014 <chaishushan{AT}gmail.com>. 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
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
"image/color"
|
|
"io"
|
|
)
|
|
|
|
func (p *IFD) BlocksAcross() int {
|
|
imageWidth, _ := p.TagGetter().GetImageWidth()
|
|
if imageWidth == 0 {
|
|
return 0
|
|
}
|
|
blockWidth, _ := p.TagGetter().GetTileWidth()
|
|
if blockWidth > 0 {
|
|
return int((imageWidth + blockWidth - 1) / blockWidth)
|
|
}
|
|
return 1
|
|
}
|
|
|
|
func (p *IFD) BlocksDown() int {
|
|
imageHeight, _ := p.TagGetter().GetImageLength()
|
|
if imageHeight == 0 {
|
|
return 0
|
|
}
|
|
blockHeight, _ := p.TagGetter().GetTileLength()
|
|
if blockHeight > 0 {
|
|
return int((imageHeight + blockHeight - 1) / blockHeight)
|
|
}
|
|
blockHeight, _ = p.TagGetter().GetRowsPerStrip()
|
|
if blockHeight == 0 || blockHeight > imageHeight {
|
|
blockHeight = imageHeight
|
|
}
|
|
return int((imageHeight + blockHeight - 1) / blockHeight)
|
|
}
|
|
|
|
func (p *IFD) BlockBounds(col, row int) image.Rectangle {
|
|
blocksAcross, blocksDown := p.BlocksAcross(), p.BlocksDown()
|
|
if col < 0 || row < 0 || col >= blocksAcross || row >= blocksDown {
|
|
return image.Rectangle{}
|
|
}
|
|
|
|
if _, ok := p.TagGetter().GetTileWidth(); ok {
|
|
blockWidth, _ := p.TagGetter().GetTileWidth()
|
|
blockHeight, _ := p.TagGetter().GetTileLength()
|
|
|
|
blkW := blockWidth
|
|
blkH := blockHeight
|
|
|
|
xmin := col * int(blockWidth)
|
|
ymin := row * int(blockHeight)
|
|
xmax := xmin + int(blkW)
|
|
ymax := ymin + int(blkH)
|
|
|
|
return image.Rect(xmin, ymin, xmax, ymax)
|
|
|
|
} else {
|
|
imageWidth, _ := p.TagGetter().GetImageWidth()
|
|
imageHeight, _ := p.TagGetter().GetImageLength()
|
|
|
|
blockWidth := imageWidth
|
|
blockHeight, ok := p.TagGetter().GetRowsPerStrip()
|
|
if !ok || blockHeight == 0 {
|
|
blockHeight = imageHeight
|
|
}
|
|
|
|
blkW := blockWidth
|
|
blkH := blockHeight
|
|
if row == blocksDown-1 && imageHeight%blockHeight != 0 {
|
|
blkH = imageHeight % blockHeight
|
|
}
|
|
|
|
xmin := col * int(blockWidth)
|
|
ymin := row * int(blockHeight)
|
|
xmax := xmin + int(blkW)
|
|
ymax := ymin + int(blkH)
|
|
|
|
return image.Rect(xmin, ymin, xmax, ymax)
|
|
}
|
|
}
|
|
|
|
func (p *IFD) BlockOffset(col, row int) int64 {
|
|
blocksAcross, blocksDown := p.BlocksAcross(), p.BlocksDown()
|
|
if col < 0 || row < 0 || col >= blocksAcross || row >= blocksDown {
|
|
return 0
|
|
}
|
|
if _, ok := p.TagGetter().GetTileWidth(); ok {
|
|
offsets, ok := p.TagGetter().GetTileOffsets()
|
|
if !ok || len(offsets) != blocksAcross*blocksDown {
|
|
return 0
|
|
}
|
|
return offsets[row*blocksAcross+col]
|
|
} else {
|
|
offsets, ok := p.TagGetter().GetStripOffsets()
|
|
if !ok || len(offsets) != blocksAcross*blocksDown {
|
|
return 0
|
|
}
|
|
return offsets[row*blocksAcross+col]
|
|
}
|
|
}
|
|
|
|
func (p *IFD) BlockCount(col, row int) int64 {
|
|
blocksAcross, blocksDown := p.BlocksAcross(), p.BlocksDown()
|
|
if col < 0 || row < 0 || col >= blocksAcross || row >= blocksDown {
|
|
return 0
|
|
}
|
|
if _, ok := p.TagGetter().GetTileWidth(); ok {
|
|
counts, ok := p.TagGetter().GetTileByteCounts()
|
|
if !ok || len(counts) != blocksAcross*blocksDown {
|
|
return 0
|
|
}
|
|
return counts[row*blocksAcross+col]
|
|
} else {
|
|
counts, ok := p.TagGetter().GetStripByteCounts()
|
|
if !ok || len(counts) != blocksAcross*blocksDown {
|
|
return 0
|
|
}
|
|
return counts[row*blocksAcross+col]
|
|
}
|
|
}
|
|
|
|
func (p *IFD) DecodeBlock(r io.ReadSeeker, col, row int, dst image.Image) (err error) {
|
|
blocksAcross, blocksDown := p.BlocksAcross(), p.BlocksDown()
|
|
if col < 0 || row < 0 || col >= blocksAcross || row >= blocksDown {
|
|
err = fmt.Errorf("tiff: IFD.DecodeBlock, bad col/row = %d/%d", col, row)
|
|
return
|
|
}
|
|
|
|
bounds := p.BlockBounds(col, row)
|
|
offset := p.BlockOffset(col, row)
|
|
count := p.BlockCount(col, row)
|
|
|
|
if _, err = r.Seek(offset, 0); err != nil {
|
|
return
|
|
}
|
|
limitReader := io.LimitReader(r, count)
|
|
|
|
var data []byte
|
|
if data, err = p.Compression().Decode(limitReader, bounds.Dx(), bounds.Dy()); err != nil {
|
|
return
|
|
}
|
|
|
|
predictor, ok := p.TagGetter().GetPredictor()
|
|
if ok && predictor == TagValue_PredictorType_Horizontal {
|
|
if data, err = p.decodePredictor(data, bounds); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
err = p.decodeBlock(data, dst, bounds)
|
|
return
|
|
}
|
|
|
|
func (p *IFD) decodePredictor(data []byte, r image.Rectangle) (out []byte, err error) {
|
|
bpp := p.Depth()
|
|
spp := p.Channels()
|
|
|
|
switch bpp {
|
|
case 16:
|
|
var off int
|
|
for y := r.Min.Y; y < r.Max.Y; y++ {
|
|
off += spp * 2
|
|
for x := 0; x < (r.Dx()-1)*spp*2; x += 2 {
|
|
if off+2 > len(data) {
|
|
err = fmt.Errorf("tiff: IFD.decodePredictor, not enough pixel data")
|
|
return
|
|
}
|
|
v0 := p.Header.ByteOrder.Uint16(data[off-spp*2 : off-spp*2+2])
|
|
v1 := p.Header.ByteOrder.Uint16(data[off : off+2])
|
|
p.Header.ByteOrder.PutUint16(data[off:off+2], v1+v0)
|
|
off += 2
|
|
}
|
|
}
|
|
case 8:
|
|
var off int
|
|
for y := r.Min.Y; y < r.Max.Y; y++ {
|
|
off += spp
|
|
for x := 0; x < (r.Dx()-1)*spp; x++ {
|
|
if off >= len(data) {
|
|
err = fmt.Errorf("tiff: IFD.decodePredictor, not enough pixel data")
|
|
return
|
|
}
|
|
data[off] += data[off-spp]
|
|
off++
|
|
}
|
|
}
|
|
default:
|
|
err = fmt.Errorf("tiff: IFD.decodePredictor, bad BitsPerSample = %d", bpp)
|
|
return
|
|
}
|
|
out = data
|
|
return
|
|
}
|
|
|
|
func (p *IFD) decodeBlock(buf []byte, dst image.Image, r image.Rectangle) (err error) {
|
|
xmin, ymin := r.Min.X, r.Min.Y
|
|
xmax, ymax := r.Max.X, r.Max.Y
|
|
|
|
rMaxX := minInt(xmax, dst.Bounds().Max.X)
|
|
rMaxY := minInt(ymax, dst.Bounds().Max.Y)
|
|
|
|
b := p.Bounds()
|
|
rMaxX = minInt(rMaxX, b.Max.X)
|
|
rMaxY = minInt(rMaxY, b.Max.Y)
|
|
|
|
switch p.ImageType() {
|
|
case ImageType_Gray, ImageType_GrayInvert, ImageType_Bilevel, ImageType_BilevelInvert:
|
|
if x, bpp := p.Compression(), p.Depth(); bpp == 1 && (x == TagValue_CompressionType_G3 || x == TagValue_CompressionType_G4) {
|
|
img := dst.(*image.Gray)
|
|
for y := ymin; y < rMaxY; y++ {
|
|
min := img.PixOffset(xmin, y)
|
|
max := img.PixOffset(rMaxX, y)
|
|
off := (y - ymin) * (xmax - xmin) * 1
|
|
for i := min; i < max; i++ {
|
|
img.Pix[i+0] = buf[off+0]
|
|
off++
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
if p.Depth() == 16 {
|
|
var off int
|
|
img := dst.(*image.Gray16)
|
|
for y := ymin; y < rMaxY; y++ {
|
|
for x := xmin; x < rMaxX; x++ {
|
|
if off+2 > len(buf) {
|
|
err = fmt.Errorf("tiff: IFD.decodeBlock, not enough pixel data")
|
|
return
|
|
}
|
|
v := p.Header.ByteOrder.Uint16(buf[off : off+2])
|
|
off += 2
|
|
if p.ImageType() == ImageType_GrayInvert {
|
|
v = 0xffff - v
|
|
}
|
|
img.SetGray16(x, y, color.Gray16{v})
|
|
}
|
|
}
|
|
} else {
|
|
bpp := uint(p.Depth())
|
|
bitReader := newBitsReader(buf)
|
|
img := dst.(*image.Gray)
|
|
max := uint32((1 << uint(p.Depth())) - 1)
|
|
for y := ymin; y < rMaxY; y++ {
|
|
for x := xmin; x < rMaxX; x++ {
|
|
v, ok := bitReader.ReadBits(bpp)
|
|
if !ok {
|
|
err = fmt.Errorf("tiff: IFD.decodeBlock, not enough pixel data")
|
|
return
|
|
}
|
|
v = v * 0xff / max
|
|
if p.ImageType() == ImageType_GrayInvert {
|
|
v = 0xff - v
|
|
}
|
|
img.SetGray(x, y, color.Gray{uint8(v)})
|
|
}
|
|
bitReader.flushBits()
|
|
}
|
|
}
|
|
case ImageType_Paletted:
|
|
bpp := uint(p.Depth())
|
|
bitReader := newBitsReader(buf)
|
|
img := dst.(*image.Paletted)
|
|
for y := ymin; y < rMaxY; y++ {
|
|
for x := xmin; x < rMaxX; x++ {
|
|
v, ok := bitReader.ReadBits(bpp)
|
|
if !ok {
|
|
err = fmt.Errorf("tiff: IFD.decodeBlock, not enough pixel data")
|
|
return
|
|
}
|
|
img.SetColorIndex(x, y, uint8(v))
|
|
}
|
|
bitReader.flushBits()
|
|
}
|
|
case ImageType_RGB:
|
|
if p.Depth() == 16 {
|
|
var off int
|
|
img := dst.(*image.RGBA64)
|
|
for y := ymin; y < rMaxY; y++ {
|
|
for x := xmin; x < rMaxX; x++ {
|
|
if off+6 > len(buf) {
|
|
err = fmt.Errorf("tiff: IFD.decodeBlock, not enough pixel data")
|
|
return
|
|
}
|
|
r := p.Header.ByteOrder.Uint16(buf[off+0 : off+2])
|
|
g := p.Header.ByteOrder.Uint16(buf[off+2 : off+4])
|
|
b := p.Header.ByteOrder.Uint16(buf[off+4 : off+6])
|
|
off += 6
|
|
img.SetRGBA64(x, y, color.RGBA64{r, g, b, 0xffff})
|
|
}
|
|
}
|
|
} else {
|
|
img := dst.(*image.RGBA)
|
|
for y := ymin; y < rMaxY; y++ {
|
|
min := img.PixOffset(xmin, y)
|
|
max := img.PixOffset(rMaxX, y)
|
|
off := (y - ymin) * (xmax - xmin) * 3
|
|
for i := min; i < max; i += 4 {
|
|
if off+3 > len(buf) {
|
|
err = fmt.Errorf("tiff: IFD.decodeBlock, not enough pixel data")
|
|
return
|
|
}
|
|
img.Pix[i+0] = buf[off+0]
|
|
img.Pix[i+1] = buf[off+1]
|
|
img.Pix[i+2] = buf[off+2]
|
|
img.Pix[i+3] = 0xff
|
|
off += 3
|
|
}
|
|
}
|
|
}
|
|
case ImageType_NRGBA:
|
|
if p.Depth() == 16 {
|
|
var off int
|
|
img := dst.(*image.NRGBA64)
|
|
for y := ymin; y < rMaxY; y++ {
|
|
for x := xmin; x < rMaxX; x++ {
|
|
if off+8 > len(buf) {
|
|
err = fmt.Errorf("tiff: IFD.decodeBlock, not enough pixel data")
|
|
return
|
|
}
|
|
r := p.Header.ByteOrder.Uint16(buf[off+0 : off+2])
|
|
g := p.Header.ByteOrder.Uint16(buf[off+2 : off+4])
|
|
b := p.Header.ByteOrder.Uint16(buf[off+4 : off+6])
|
|
a := p.Header.ByteOrder.Uint16(buf[off+6 : off+8])
|
|
off += 8
|
|
img.SetNRGBA64(x, y, color.NRGBA64{r, g, b, a})
|
|
}
|
|
}
|
|
} else {
|
|
img := dst.(*image.NRGBA)
|
|
for y := ymin; y < rMaxY; y++ {
|
|
min := img.PixOffset(xmin, y)
|
|
max := img.PixOffset(rMaxX, y)
|
|
i0, i1 := (y-ymin)*(xmax-xmin)*4, (y-ymin+1)*(xmax-xmin)*4
|
|
if i1 > len(buf) {
|
|
err = fmt.Errorf("tiff: IFD.decodeBlock, not enough pixel data")
|
|
return
|
|
}
|
|
copy(img.Pix[min:max], buf[i0:i1])
|
|
}
|
|
}
|
|
case ImageType_RGBA:
|
|
if p.Depth() == 16 {
|
|
var off int
|
|
img := dst.(*image.RGBA64)
|
|
for y := ymin; y < rMaxY; y++ {
|
|
for x := xmin; x < rMaxX; x++ {
|
|
if off+8 > len(buf) {
|
|
err = fmt.Errorf("tiff: IFD.decodeBlock, not enough pixel data")
|
|
return
|
|
}
|
|
r := p.Header.ByteOrder.Uint16(buf[off+0 : off+2])
|
|
g := p.Header.ByteOrder.Uint16(buf[off+2 : off+4])
|
|
b := p.Header.ByteOrder.Uint16(buf[off+4 : off+6])
|
|
a := p.Header.ByteOrder.Uint16(buf[off+6 : off+8])
|
|
off += 8
|
|
img.SetRGBA64(x, y, color.RGBA64{r, g, b, a})
|
|
}
|
|
}
|
|
} else {
|
|
img := dst.(*image.RGBA)
|
|
for y := ymin; y < rMaxY; y++ {
|
|
min := img.PixOffset(xmin, y)
|
|
max := img.PixOffset(rMaxX, y)
|
|
i0, i1 := (y-ymin)*(xmax-xmin)*4, (y-ymin+1)*(xmax-xmin)*4
|
|
if i1 > len(buf) {
|
|
err = fmt.Errorf("tiff: IFD.decodeBlock, not enough pixel data")
|
|
return
|
|
}
|
|
copy(img.Pix[min:max], buf[i0:i1])
|
|
}
|
|
}
|
|
default:
|
|
err = fmt.Errorf("tiff: IFD.decodeBlock, unknown imageType: %v", p.ImageType())
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (p *IFD) EncodeBlock(w io.Writer, col, row int, dst *MemPImage) (err error) {
|
|
return
|
|
}
|