go.image/tiff: simplify encoding.

R=bsiegert
CC=golang-dev
https://golang.org/cl/5738044
This commit is contained in:
Nigel Tao 2012-03-07 10:32:33 +11:00
parent f594c3aad5
commit 20e9620e99

View File

@ -47,63 +47,19 @@ func (e ifdEntry) putData(p []byte) {
} }
} }
type ifd []ifdEntry type byTag []ifdEntry
func (d ifd) Len() int { func (d byTag) Len() int { return len(d) }
return len(d) func (d byTag) Less(i, j int) bool { return d[i].tag < d[j].tag }
} func (d byTag) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
func (d ifd) Less(i, j int) bool { func writeImgData(w io.Writer, m image.Image) error {
return d[i].tag < d[j].tag bounds := m.Bounds()
} buf := make([]byte, 4*bounds.Dx())
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
func (d ifd) Swap(i, j int) {
d[i], d[j] = d[j], d[i]
}
type encoder struct {
ifd ifd
img image.Image
imageLen int // Length of the image in bytes.
}
func newEncoder(m image.Image) *encoder {
width := m.Bounds().Dx()
height := m.Bounds().Dy()
imageLen := width * height * 4
return &encoder{
img: m,
// For uncompressed images, imageLen is known in advance.
// For compressed images, we would need to write the image
// data in a buffer here to get its length.
imageLen: imageLen,
ifd: ifd{
{tImageWidth, dtShort, []uint32{uint32(width)}},
{tImageLength, dtShort, []uint32{uint32(height)}},
{tBitsPerSample, dtShort, []uint32{8, 8, 8, 8}},
{tCompression, dtShort, []uint32{cNone}},
{tPhotometricInterpretation, dtShort, []uint32{pRGB}},
{tStripOffsets, dtLong, []uint32{8}},
{tSamplesPerPixel, dtShort, []uint32{4}},
{tRowsPerStrip, dtShort, []uint32{uint32(height)}},
{tStripByteCounts, dtLong, []uint32{uint32(imageLen)}},
// There is currently no support for storing the image
// resolution, so give a bogus value of 72x72 dpi.
{tXResolution, dtRational, []uint32{72, 1}},
{tYResolution, dtRational, []uint32{72, 1}},
{tResolutionUnit, dtShort, []uint32{resPerInch}},
{tExtraSamples, dtShort, []uint32{1}}, // RGBA.
},
}
}
func (e *encoder) writeImgData(w io.Writer) error {
b := e.img.Bounds()
buf := make([]byte, 4*b.Dx())
for y := b.Min.Y; y < b.Max.Y; y++ {
i := 0 i := 0
for x := b.Min.X; x < b.Max.X; x++ { for x := bounds.Min.X; x < bounds.Max.X; x++ {
r, g, b, a := e.img.At(x, y).RGBA() r, g, b, a := m.At(x, y).RGBA()
buf[i+0] = uint8(r >> 8) buf[i+0] = uint8(r >> 8)
buf[i+1] = uint8(g >> 8) buf[i+1] = uint8(g >> 8)
buf[i+2] = uint8(b >> 8) buf[i+2] = uint8(b >> 8)
@ -117,22 +73,22 @@ func (e *encoder) writeImgData(w io.Writer) error {
return nil return nil
} }
func (e *encoder) writeIFD(w io.Writer) error { func writeIFD(w io.Writer, ifdOffset int, d []ifdEntry) error {
var buf [ifdLen]byte var buf [ifdLen]byte
// Make space for "pointer area" containing IFD entry data // Make space for "pointer area" containing IFD entry data
// longer than 4 bytes. // longer than 4 bytes.
parea := make([]byte, 1024) parea := make([]byte, 1024)
pstart := int(e.imageLen) + 8 + (ifdLen * len(e.ifd)) + 6 pstart := ifdOffset + ifdLen*len(d) + 6
var o int // Current offset in parea. var o int // Current offset in parea.
// The IFD has to be written with the tags in ascending order. // The IFD has to be written with the tags in ascending order.
sort.Sort(e.ifd) sort.Sort(byTag(d))
// Write the number of entries in this IFD. // Write the number of entries in this IFD.
if err := binary.Write(w, enc, uint16(len(e.ifd))); err != nil { if err := binary.Write(w, enc, uint16(len(d))); err != nil {
return err return err
} }
for _, ent := range e.ifd { for _, ent := range d {
enc.PutUint16(buf[0:2], uint16(ent.tag)) enc.PutUint16(buf[0:2], uint16(ent.tag))
enc.PutUint16(buf[2:4], uint16(ent.datatype)) enc.PutUint16(buf[2:4], uint16(ent.datatype))
count := uint32(len(ent.data)) count := uint32(len(ent.data))
@ -170,25 +126,41 @@ func (e *encoder) writeIFD(w io.Writer) error {
return err return err
} }
func (e *encoder) encode(w io.Writer) error { // Encode writes the image m to w in uncompressed RGBA format.
func Encode(w io.Writer, m image.Image) error {
_, err := io.WriteString(w, leHeader) _, err := io.WriteString(w, leHeader)
if err != nil { if err != nil {
return err return err
} }
ifdOffset := e.imageLen + 8 // 8 bytes for TIFF header. bounds := m.Bounds()
width, height := bounds.Dx(), bounds.Dy()
// imageLen is the length of the image data in bytes.
imageLen := width * height * 4
ifdOffset := imageLen + 8 // 8 bytes for TIFF header.
err = binary.Write(w, enc, uint32(ifdOffset)) err = binary.Write(w, enc, uint32(ifdOffset))
if err != nil { if err != nil {
return err return err
} }
err = e.writeImgData(w) err = writeImgData(w, m)
if err != nil { if err != nil {
return err return err
} }
return e.writeIFD(w) return writeIFD(w, ifdOffset, []ifdEntry{
} {tImageWidth, dtShort, []uint32{uint32(width)}},
{tImageLength, dtShort, []uint32{uint32(height)}},
// Encode writes the image m to w in uncompressed RGBA format. {tBitsPerSample, dtShort, []uint32{8, 8, 8, 8}},
func Encode(w io.Writer, m image.Image) error { {tCompression, dtShort, []uint32{cNone}},
return newEncoder(m).encode(w) {tPhotometricInterpretation, dtShort, []uint32{pRGB}},
{tStripOffsets, dtLong, []uint32{8}},
{tSamplesPerPixel, dtShort, []uint32{4}},
{tRowsPerStrip, dtShort, []uint32{uint32(height)}},
{tStripByteCounts, dtLong, []uint32{uint32(imageLen)}},
// There is currently no support for storing the image
// resolution, so give a bogus value of 72x72 dpi.
{tXResolution, dtRational, []uint32{72, 1}},
{tYResolution, dtRational, []uint32{72, 1}},
{tResolutionUnit, dtShort, []uint32{resPerInch}},
{tExtraSamples, dtShort, []uint32{1}}, // RGBA.
})
} }