summaryrefslogtreecommitdiff
path: root/libgo/go/image/jpeg/writer.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/image/jpeg/writer.go')
-rw-r--r--libgo/go/image/jpeg/writer.go133
1 files changed, 97 insertions, 36 deletions
diff --git a/libgo/go/image/jpeg/writer.go b/libgo/go/image/jpeg/writer.go
index c58fbf3055..91bbde3bf8 100644
--- a/libgo/go/image/jpeg/writer.go
+++ b/libgo/go/image/jpeg/writer.go
@@ -249,7 +249,7 @@ func (e *encoder) writeByte(b byte) {
e.err = e.w.WriteByte(b)
}
-// emit emits the least significant nBits bits of bits to the bitstream.
+// emit emits the least significant nBits bits of bits to the bit-stream.
// The precondition is bits < 1<<nBits && nBits <= 16.
func (e *encoder) emit(bits, nBits uint32) {
nBits += e.nBits
@@ -312,32 +312,44 @@ func (e *encoder) writeDQT() {
}
// writeSOF0 writes the Start Of Frame (Baseline) marker.
-func (e *encoder) writeSOF0(size image.Point) {
- const markerlen = 8 + 3*nColorComponent
+func (e *encoder) writeSOF0(size image.Point, nComponent int) {
+ markerlen := 8 + 3*nComponent
e.writeMarkerHeader(sof0Marker, markerlen)
e.buf[0] = 8 // 8-bit color.
e.buf[1] = uint8(size.Y >> 8)
e.buf[2] = uint8(size.Y & 0xff)
e.buf[3] = uint8(size.X >> 8)
e.buf[4] = uint8(size.X & 0xff)
- e.buf[5] = nColorComponent
- for i := 0; i < nColorComponent; i++ {
- e.buf[3*i+6] = uint8(i + 1)
- // We use 4:2:0 chroma subsampling.
- e.buf[3*i+7] = "\x22\x11\x11"[i]
- e.buf[3*i+8] = "\x00\x01\x01"[i]
+ e.buf[5] = uint8(nComponent)
+ if nComponent == 1 {
+ e.buf[6] = 1
+ // No subsampling for grayscale image.
+ e.buf[7] = 0x11
+ e.buf[8] = 0x00
+ } else {
+ for i := 0; i < nComponent; i++ {
+ e.buf[3*i+6] = uint8(i + 1)
+ // We use 4:2:0 chroma subsampling.
+ e.buf[3*i+7] = "\x22\x11\x11"[i]
+ e.buf[3*i+8] = "\x00\x01\x01"[i]
+ }
}
- e.write(e.buf[:3*(nColorComponent-1)+9])
+ e.write(e.buf[:3*(nComponent-1)+9])
}
// writeDHT writes the Define Huffman Table marker.
-func (e *encoder) writeDHT() {
+func (e *encoder) writeDHT(nComponent int) {
markerlen := 2
- for _, s := range theHuffmanSpec {
+ specs := theHuffmanSpec[:]
+ if nComponent == 1 {
+ // Drop the Chrominance tables.
+ specs = specs[:2]
+ }
+ for _, s := range specs {
markerlen += 1 + 16 + len(s.value)
}
e.writeMarkerHeader(dhtMarker, markerlen)
- for i, s := range theHuffmanSpec {
+ for i, s := range specs {
e.writeByte("\x00\x10\x01\x11"[i])
e.write(s.count[:])
e.write(s.value)
@@ -345,8 +357,8 @@ func (e *encoder) writeDHT() {
}
// writeBlock writes a block of pixel data using the given quantization table,
-// returning the post-quantized DC value of the DCT-transformed block.
-// b is in natural (not zig-zag) order.
+// returning the post-quantized DC value of the DCT-transformed block. b is in
+// natural (not zig-zag) order.
func (e *encoder) writeBlock(b *block, q quantIndex, prevDC int32) int32 {
fdct(b)
// Emit the DC delta.
@@ -390,6 +402,20 @@ func toYCbCr(m image.Image, p image.Point, yBlock, cbBlock, crBlock *block) {
}
}
+// grayToY stores the 8x8 region of m whose top-left corner is p in yBlock.
+func grayToY(m *image.Gray, p image.Point, yBlock *block) {
+ b := m.Bounds()
+ xmax := b.Max.X - 1
+ ymax := b.Max.Y - 1
+ pix := m.Pix
+ for j := 0; j < 8; j++ {
+ for i := 0; i < 8; i++ {
+ idx := m.PixOffset(min(p.X+i, xmax), min(p.Y+j, ymax))
+ yBlock[8*j+i] = int32(pix[idx])
+ }
+ }
+}
+
// rgbaToYCbCr is a specialized version of toYCbCr for image.RGBA images.
func rgbaToYCbCr(m *image.RGBA, p image.Point, yBlock, cbBlock, crBlock *block) {
b := m.Bounds()
@@ -430,7 +456,18 @@ func scale(dst *block, src *[4]block) {
}
}
-// sosHeader is the SOS marker "\xff\xda" followed by 12 bytes:
+// sosHeaderY is the SOS marker "\xff\xda" followed by 8 bytes:
+// - the marker length "\x00\x08",
+// - the number of components "\x01",
+// - component 1 uses DC table 0 and AC table 0 "\x01\x00",
+// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
+// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
+// should be 0x00, 0x3f, 0x00<<4 | 0x00.
+var sosHeaderY = []byte{
+ 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00,
+}
+
+// sosHeaderYCbCr is the SOS marker "\xff\xda" followed by 12 bytes:
// - the marker length "\x00\x0c",
// - the number of components "\x03",
// - component 1 uses DC table 0 and AC table 0 "\x01\x00",
@@ -439,14 +476,19 @@ func scale(dst *block, src *[4]block) {
// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
// should be 0x00, 0x3f, 0x00<<4 | 0x00.
-var sosHeader = []byte{
+var sosHeaderYCbCr = []byte{
0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02,
0x11, 0x03, 0x11, 0x00, 0x3f, 0x00,
}
// writeSOS writes the StartOfScan marker.
func (e *encoder) writeSOS(m image.Image) {
- e.write(sosHeader)
+ switch m.(type) {
+ case *image.Gray:
+ e.write(sosHeaderY)
+ default:
+ e.write(sosHeaderYCbCr)
+ }
var (
// Scratch buffers to hold the YCbCr values.
// The blocks are in natural (not zig-zag) order.
@@ -456,24 +498,36 @@ func (e *encoder) writeSOS(m image.Image) {
prevDCY, prevDCCb, prevDCCr int32
)
bounds := m.Bounds()
- rgba, _ := m.(*image.RGBA)
- for y := bounds.Min.Y; y < bounds.Max.Y; y += 16 {
- for x := bounds.Min.X; x < bounds.Max.X; x += 16 {
- for i := 0; i < 4; i++ {
- xOff := (i & 1) * 8
- yOff := (i & 2) * 4
- p := image.Pt(x+xOff, y+yOff)
- if rgba != nil {
- rgbaToYCbCr(rgba, p, &b, &cb[i], &cr[i])
- } else {
- toYCbCr(m, p, &b, &cb[i], &cr[i])
- }
+ switch m := m.(type) {
+ // TODO(wathiede): switch on m.ColorModel() instead of type.
+ case *image.Gray:
+ for y := bounds.Min.Y; y < bounds.Max.Y; y += 8 {
+ for x := bounds.Min.X; x < bounds.Max.X; x += 8 {
+ p := image.Pt(x, y)
+ grayToY(m, p, &b)
prevDCY = e.writeBlock(&b, 0, prevDCY)
}
- scale(&b, &cb)
- prevDCCb = e.writeBlock(&b, 1, prevDCCb)
- scale(&b, &cr)
- prevDCCr = e.writeBlock(&b, 1, prevDCCr)
+ }
+ default:
+ rgba, _ := m.(*image.RGBA)
+ for y := bounds.Min.Y; y < bounds.Max.Y; y += 16 {
+ for x := bounds.Min.X; x < bounds.Max.X; x += 16 {
+ for i := 0; i < 4; i++ {
+ xOff := (i & 1) * 8
+ yOff := (i & 2) * 4
+ p := image.Pt(x+xOff, y+yOff)
+ if rgba != nil {
+ rgbaToYCbCr(rgba, p, &b, &cb[i], &cr[i])
+ } else {
+ toYCbCr(m, p, &b, &cb[i], &cr[i])
+ }
+ prevDCY = e.writeBlock(&b, 0, prevDCY)
+ }
+ scale(&b, &cb)
+ prevDCCb = e.writeBlock(&b, 1, prevDCCb)
+ scale(&b, &cr)
+ prevDCCr = e.writeBlock(&b, 1, prevDCCr)
+ }
}
}
// Pad the last byte with 1's.
@@ -532,6 +586,13 @@ func Encode(w io.Writer, m image.Image, o *Options) error {
e.quant[i][j] = uint8(x)
}
}
+ // Compute number of components based on input image type.
+ nComponent := 3
+ switch m.(type) {
+ // TODO(wathiede): switch on m.ColorModel() instead of type.
+ case *image.Gray:
+ nComponent = 1
+ }
// Write the Start Of Image marker.
e.buf[0] = 0xff
e.buf[1] = 0xd8
@@ -539,9 +600,9 @@ func Encode(w io.Writer, m image.Image, o *Options) error {
// Write the quantization tables.
e.writeDQT()
// Write the image dimensions.
- e.writeSOF0(b.Size())
+ e.writeSOF0(b.Size(), nComponent)
// Write the Huffman tables.
- e.writeDHT()
+ e.writeDHT(nComponent)
// Write the image data.
e.writeSOS(m)
// Write the End Of Image marker.