go.image/tiff: simplify encoding.
R=bsiegert CC=golang-dev https://golang.org/cl/5738044
This commit is contained in:
parent
f594c3aad5
commit
20e9620e99
108
tiff/writer.go
108
tiff/writer.go
|
@ -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.
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user