]> Cypherpunks repositories - gostls13.git/commitdiff
image/jpeg: add an encoder.
authorNigel Tao <nigeltao@golang.org>
Tue, 19 Apr 2011 01:00:47 +0000 (11:00 +1000)
committerNigel Tao <nigeltao@golang.org>
Tue, 19 Apr 2011 01:00:47 +0000 (11:00 +1000)
It is based on changeset 4186064 by Raph Levien <raph@google.com>.

R=r, nigeltao_gnome
CC=golang-dev
https://golang.org/cl/4435051

src/pkg/Makefile
src/pkg/image/jpeg/Makefile
src/pkg/image/jpeg/fdct.go [new file with mode: 0644]
src/pkg/image/jpeg/idct.go
src/pkg/image/jpeg/reader.go
src/pkg/image/jpeg/writer.go [new file with mode: 0644]
src/pkg/image/jpeg/writer_test.go [new file with mode: 0644]

index de96229723bc2b73582d49eccad36a65ff77903a..d3ec7dd290549cfdd80668e7610f287ff46aaa3b 100644 (file)
@@ -183,7 +183,6 @@ NOTEST+=\
        hash\
        http/pprof\
        http/httptest\
-       image/jpeg\
        net/dict\
        rand\
        runtime/cgo\
index 5c5f97e7182be4ab238bae89905769a28c4f64ba..d9d830f2ff60a80112927f0fe074b31c60aea694 100644 (file)
@@ -6,8 +6,10 @@ include ../../../Make.inc
 
 TARG=image/jpeg
 GOFILES=\
+       fdct.go\
        huffman.go\
        idct.go\
        reader.go\
+       writer.go\
 
 include ../../../Make.pkg
diff --git a/src/pkg/image/jpeg/fdct.go b/src/pkg/image/jpeg/fdct.go
new file mode 100644 (file)
index 0000000..3f8be4e
--- /dev/null
@@ -0,0 +1,190 @@
+// 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 jpeg
+
+// This file implements a Forward Discrete Cosine Transformation.
+
+/*
+It is based on the code in jfdctint.c from the Independent JPEG Group,
+found at http://www.ijg.org/files/jpegsrc.v8c.tar.gz.
+
+The "LEGAL ISSUES" section of the README in that archive says:
+
+In plain English:
+
+1. We don't promise that this software works.  (But if you find any bugs,
+   please let us know!)
+2. You can use this software for whatever you want.  You don't have to pay us.
+3. You may not pretend that you wrote this software.  If you use it in a
+   program, you must acknowledge somewhere in your documentation that
+   you've used the IJG code.
+
+In legalese:
+
+The authors make NO WARRANTY or representation, either express or implied,
+with respect to this software, its quality, accuracy, merchantability, or
+fitness for a particular purpose.  This software is provided "AS IS", and you,
+its user, assume the entire risk as to its quality and accuracy.
+
+This software is copyright (C) 1991-2011, Thomas G. Lane, Guido Vollbeding.
+All Rights Reserved except as specified below.
+
+Permission is hereby granted to use, copy, modify, and distribute this
+software (or portions thereof) for any purpose, without fee, subject to these
+conditions:
+(1) If any part of the source code for this software is distributed, then this
+README file must be included, with this copyright and no-warranty notice
+unaltered; and any additions, deletions, or changes to the original files
+must be clearly indicated in accompanying documentation.
+(2) If only executable code is distributed, then the accompanying
+documentation must state that "this software is based in part on the work of
+the Independent JPEG Group".
+(3) Permission for use of this software is granted only if the user accepts
+full responsibility for any undesirable consequences; the authors accept
+NO LIABILITY for damages of any kind.
+
+These conditions apply to any software derived from or based on the IJG code,
+not just to the unmodified library.  If you use our work, you ought to
+acknowledge us.
+
+Permission is NOT granted for the use of any IJG author's name or company name
+in advertising or publicity relating to this software or products derived from
+it.  This software may be referred to only as "the Independent JPEG Group's
+software".
+
+We specifically permit and encourage the use of this software as the basis of
+commercial products, provided that all warranty or liability claims are
+assumed by the product vendor.
+*/
+
+// Trigonometric constants in 13-bit fixed point format.
+const (
+       fix_0_298631336 = 2446
+       fix_0_390180644 = 3196
+       fix_0_541196100 = 4433
+       fix_0_765366865 = 6270
+       fix_0_899976223 = 7373
+       fix_1_175875602 = 9633
+       fix_1_501321110 = 12299
+       fix_1_847759065 = 15137
+       fix_1_961570560 = 16069
+       fix_2_053119869 = 16819
+       fix_2_562915447 = 20995
+       fix_3_072711026 = 25172
+)
+
+const (
+       constBits     = 13
+       pass1Bits     = 2
+       centerJSample = 128
+)
+
+// fdct performs a forward DCT on an 8x8 block of coefficients, including a
+// level shift.
+func fdct(b *block) {
+       // Pass 1: process rows.
+       for y := 0; y < 8; y++ {
+               x0 := b[y*8+0]
+               x1 := b[y*8+1]
+               x2 := b[y*8+2]
+               x3 := b[y*8+3]
+               x4 := b[y*8+4]
+               x5 := b[y*8+5]
+               x6 := b[y*8+6]
+               x7 := b[y*8+7]
+
+               tmp0 := x0 + x7
+               tmp1 := x1 + x6
+               tmp2 := x2 + x5
+               tmp3 := x3 + x4
+
+               tmp10 := tmp0 + tmp3
+               tmp12 := tmp0 - tmp3
+               tmp11 := tmp1 + tmp2
+               tmp13 := tmp1 - tmp2
+
+               tmp0 = x0 - x7
+               tmp1 = x1 - x6
+               tmp2 = x2 - x5
+               tmp3 = x3 - x4
+
+               b[y*8+0] = (tmp10 + tmp11 - 8*centerJSample) << pass1Bits
+               b[y*8+4] = (tmp10 - tmp11) << pass1Bits
+               z1 := (tmp12 + tmp13) * fix_0_541196100
+               z1 += 1 << (constBits - pass1Bits - 1)
+               b[y*8+2] = (z1 + tmp12*fix_0_765366865) >> (constBits - pass1Bits)
+               b[y*8+6] = (z1 - tmp13*fix_1_847759065) >> (constBits - pass1Bits)
+
+               tmp10 = tmp0 + tmp3
+               tmp11 = tmp1 + tmp2
+               tmp12 = tmp0 + tmp2
+               tmp13 = tmp1 + tmp3
+               z1 = (tmp12 + tmp13) * fix_1_175875602
+               z1 += 1 << (constBits - pass1Bits - 1)
+               tmp0 = tmp0 * fix_1_501321110
+               tmp1 = tmp1 * fix_3_072711026
+               tmp2 = tmp2 * fix_2_053119869
+               tmp3 = tmp3 * fix_0_298631336
+               tmp10 = tmp10 * -fix_0_899976223
+               tmp11 = tmp11 * -fix_2_562915447
+               tmp12 = tmp12 * -fix_0_390180644
+               tmp13 = tmp13 * -fix_1_961570560
+
+               tmp12 += z1
+               tmp13 += z1
+               b[y*8+1] = (tmp0 + tmp10 + tmp12) >> (constBits - pass1Bits)
+               b[y*8+3] = (tmp1 + tmp11 + tmp13) >> (constBits - pass1Bits)
+               b[y*8+5] = (tmp2 + tmp11 + tmp12) >> (constBits - pass1Bits)
+               b[y*8+7] = (tmp3 + tmp10 + tmp13) >> (constBits - pass1Bits)
+       }
+       // Pass 2: process columns.
+       // We remove pass1Bits scaling, but leave results scaled up by an overall factor of 8.
+       for x := 0; x < 8; x++ {
+               tmp0 := b[0*8+x] + b[7*8+x]
+               tmp1 := b[1*8+x] + b[6*8+x]
+               tmp2 := b[2*8+x] + b[5*8+x]
+               tmp3 := b[3*8+x] + b[4*8+x]
+
+               tmp10 := tmp0 + tmp3 + 1<<(pass1Bits-1)
+               tmp12 := tmp0 - tmp3
+               tmp11 := tmp1 + tmp2
+               tmp13 := tmp1 - tmp2
+
+               tmp0 = b[0*8+x] - b[7*8+x]
+               tmp1 = b[1*8+x] - b[6*8+x]
+               tmp2 = b[2*8+x] - b[5*8+x]
+               tmp3 = b[3*8+x] - b[4*8+x]
+
+               b[0*8+x] = (tmp10 + tmp11) >> pass1Bits
+               b[4*8+x] = (tmp10 - tmp11) >> pass1Bits
+
+               z1 := (tmp12 + tmp13) * fix_0_541196100
+               z1 += 1 << (constBits + pass1Bits - 1)
+               b[2*8+x] = (z1 + tmp12*fix_0_765366865) >> (constBits + pass1Bits)
+               b[6*8+x] = (z1 - tmp13*fix_1_847759065) >> (constBits + pass1Bits)
+
+               tmp10 = tmp0 + tmp3
+               tmp11 = tmp1 + tmp2
+               tmp12 = tmp0 + tmp2
+               tmp13 = tmp1 + tmp3
+               z1 = (tmp12 + tmp13) * fix_1_175875602
+               z1 += 1 << (constBits + pass1Bits - 1)
+               tmp0 = tmp0 * fix_1_501321110
+               tmp1 = tmp1 * fix_3_072711026
+               tmp2 = tmp2 * fix_2_053119869
+               tmp3 = tmp3 * fix_0_298631336
+               tmp10 = tmp10 * -fix_0_899976223
+               tmp11 = tmp11 * -fix_2_562915447
+               tmp12 = tmp12 * -fix_0_390180644
+               tmp13 = tmp13 * -fix_1_961570560
+
+               tmp12 += z1
+               tmp13 += z1
+               b[1*8+x] = (tmp0 + tmp10 + tmp12) >> (constBits + pass1Bits)
+               b[3*8+x] = (tmp1 + tmp11 + tmp13) >> (constBits + pass1Bits)
+               b[5*8+x] = (tmp2 + tmp11 + tmp12) >> (constBits + pass1Bits)
+               b[7*8+x] = (tmp3 + tmp10 + tmp13) >> (constBits + pass1Bits)
+       }
+}
index 5189931105b085ee8312217df3226e61132fc20a..e5a2f40f5db7bcce89e9ddc715c2e408c4ab7dbd 100644 (file)
@@ -63,7 +63,7 @@ const (
 //
 // For more on the actual algorithm, see Z. Wang, "Fast algorithms for the discrete W transform and
 // for the discrete Fourier transform", IEEE Trans. on ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984.
-func idct(b *[blockSize]int) {
+func idct(b *block) {
        // Horizontal 1-D IDCT.
        for y := 0; y < 8; y++ {
                // If all the AC components are zero, then the IDCT is trivial.
index fb9cb11bb7facdbcf8b3a553bbf7d9487fb4e0e0..63b594e91c77a035438d17515f091fbfb01f3e35 100644 (file)
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// The jpeg package implements a decoder for JPEG images, as defined in ITU-T T.81.
+// Package jpeg implements a JPEG image decoder and encoder.
+//
+// JPEG is defined in ITU-T T.81: http://www.w3.org/Graphics/JPEG/itu-t81.pdf.
 package jpeg
 
-// See http://www.w3.org/Graphics/JPEG/itu-t81.pdf
-
 import (
        "bufio"
        "image"
@@ -32,6 +32,8 @@ type component struct {
        tq uint8 // Quantization table destination selector.
 }
 
+type block [blockSize]int
+
 const (
        blockSize = 64 // A DCT block is 8x8.
 
@@ -88,9 +90,9 @@ type decoder struct {
        ri            int // Restart Interval.
        comps         [nComponent]component
        huff          [maxTc + 1][maxTh + 1]huffman
-       quant         [maxTq + 1][blockSize]int
+       quant         [maxTq + 1]block
        b             bits
-       blocks        [nComponent][maxH * maxV][blockSize]int
+       blocks        [nComponent][maxH * maxV]block
        tmp           [1024]byte
 }
 
@@ -269,7 +271,7 @@ func (d *decoder) processSOS(n int) os.Error {
        myy := (d.height + 8*int(v0) - 1) / (8 * int(v0))
 
        mcu, expectedRST := 0, uint8(rst0Marker)
-       var allZeroes [blockSize]int
+       var allZeroes block
        var dc [nComponent]int
        for my := 0; my < myy; my++ {
                for mx := 0; mx < mxx; mx++ {
diff --git a/src/pkg/image/jpeg/writer.go b/src/pkg/image/jpeg/writer.go
new file mode 100644 (file)
index 0000000..505cce0
--- /dev/null
@@ -0,0 +1,523 @@
+// 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 jpeg
+
+import (
+       "bufio"
+       "image"
+       "image/ycbcr"
+       "io"
+       "os"
+)
+
+// min returns the minimum of two integers.
+func min(x, y int) int {
+       if x < y {
+               return x
+       }
+       return y
+}
+
+// div returns a/b rounded to the nearest integer, instead of rounded to zero.
+func div(a int, b int) int {
+       if a >= 0 {
+               return (a + (b >> 1)) / b
+       }
+       return -((-a + (b >> 1)) / b)
+}
+
+// bitCount counts the number of bits needed to hold an integer.
+var bitCount = [256]byte{
+       0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+       6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+       6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+       7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+       7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+       7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+       7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+       8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+       8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+       8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+       8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+       8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+       8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+       8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+       8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+}
+
+type quantIndex int
+
+const (
+       quantIndexLuminance quantIndex = iota
+       quantIndexChrominance
+       nQuantIndex
+)
+
+// unscaledQuant are the unscaled quantization tables. Each encoder copies and
+// scales the tables according to its quality parameter.
+var unscaledQuant = [nQuantIndex][blockSize]byte{
+       // Luminance.
+       {
+               16, 11, 10, 16, 24, 40, 51, 61,
+               12, 12, 14, 19, 26, 58, 60, 55,
+               14, 13, 16, 24, 40, 57, 69, 56,
+               14, 17, 22, 29, 51, 87, 80, 62,
+               18, 22, 37, 56, 68, 109, 103, 77,
+               24, 35, 55, 64, 81, 104, 113, 92,
+               49, 64, 78, 87, 103, 121, 120, 101,
+               72, 92, 95, 98, 112, 100, 103, 99,
+       },
+       // Chrominance.
+       {
+               17, 18, 24, 47, 99, 99, 99, 99,
+               18, 21, 26, 66, 99, 99, 99, 99,
+               24, 26, 56, 99, 99, 99, 99, 99,
+               47, 66, 99, 99, 99, 99, 99, 99,
+               99, 99, 99, 99, 99, 99, 99, 99,
+               99, 99, 99, 99, 99, 99, 99, 99,
+               99, 99, 99, 99, 99, 99, 99, 99,
+               99, 99, 99, 99, 99, 99, 99, 99,
+       },
+}
+
+type huffIndex int
+
+const (
+       huffIndexLuminanceDC huffIndex = iota
+       huffIndexLuminanceAC
+       huffIndexChrominanceDC
+       huffIndexChrominanceAC
+       nHuffIndex
+)
+
+// huffmanSpec specifies a Huffman encoding.
+type huffmanSpec struct {
+       // count[i] is the number of codes of length i bits.
+       count [16]byte
+       // value[i] is the decoded value of the i'th codeword.
+       value []byte
+}
+
+// theHuffmanSpec is the Huffman encoding specifications.
+// This encoder uses the same Huffman encoding for all images.
+var theHuffmanSpec = [nHuffIndex]huffmanSpec{
+       // Luminance DC.
+       {
+               [16]byte{0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0},
+               []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
+       },
+       // Luminance AC.
+       {
+               [16]byte{0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125},
+               []byte{
+                       0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
+                       0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
+                       0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
+                       0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
+                       0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
+                       0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
+                       0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+                       0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+                       0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+                       0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+                       0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+                       0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+                       0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+                       0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+                       0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+                       0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
+                       0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
+                       0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+                       0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
+                       0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+                       0xf9, 0xfa,
+               },
+       },
+       // Chrominance DC.
+       {
+               [16]byte{0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
+               []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
+       },
+       // Chrominance AC.
+       {
+               [16]byte{0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119},
+               []byte{
+                       0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
+                       0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+                       0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+                       0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
+                       0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
+                       0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
+                       0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
+                       0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+                       0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+                       0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+                       0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+                       0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+                       0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
+                       0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
+                       0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
+                       0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
+                       0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
+                       0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
+                       0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+                       0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+                       0xf9, 0xfa,
+               },
+       },
+}
+
+// huffmanLUT is a compiled look-up table representation of a huffmanSpec.
+// Each value maps to a uint32 of which the 8 most significant bits hold the
+// codeword size in bits and the 24 least significant bits hold the codeword.
+// The maximum codeword size is 16 bits.
+type huffmanLUT []uint32
+
+func (h *huffmanLUT) init(s huffmanSpec) {
+       maxValue := 0
+       for _, v := range s.value {
+               if int(v) > maxValue {
+                       maxValue = int(v)
+               }
+       }
+       *h = make([]uint32, maxValue+1)
+       code, k := uint32(0), 0
+       for i := 0; i < len(s.count); i++ {
+               nBits := uint32(i+1) << 24
+               for j := uint8(0); j < s.count[i]; j++ {
+                       (*h)[s.value[k]] = nBits | code
+                       code++
+                       k++
+               }
+               code <<= 1
+       }
+}
+
+// theHuffmanLUT are compiled representations of theHuffmanSpec.
+var theHuffmanLUT [4]huffmanLUT
+
+func init() {
+       for i, s := range theHuffmanSpec {
+               theHuffmanLUT[i].init(s)
+       }
+}
+
+// writer is a buffered writer.
+type writer interface {
+       Flush() os.Error
+       Write([]byte) (int, os.Error)
+       WriteByte(byte) os.Error
+}
+
+// encoder encodes an image to the JPEG format.
+type encoder struct {
+       // w is the writer to write to. err is the first error encountered during
+       // writing. All attempted writes after the first error become no-ops.
+       w   writer
+       err os.Error
+       // buf is a scratch buffer.
+       buf [16]byte
+       // bits and nBits are accumulated bits to write to w.
+       bits  uint32
+       nBits uint8
+       // quant is the scaled quantization tables.
+       quant [nQuantIndex][blockSize]byte
+}
+
+func (e *encoder) flush() {
+       if e.err != nil {
+               return
+       }
+       e.err = e.w.Flush()
+}
+
+func (e *encoder) write(p []byte) {
+       if e.err != nil {
+               return
+       }
+       _, e.err = e.w.Write(p)
+}
+
+func (e *encoder) writeByte(b byte) {
+       if e.err != nil {
+               return
+       }
+       e.err = e.w.WriteByte(b)
+}
+
+// emit emits the least significant nBits bits of bits to the bitstream.
+// The precondition is bits < 1<<nBits && nBits <= 16.
+func (e *encoder) emit(bits uint32, nBits uint8) {
+       nBits += e.nBits
+       bits <<= 32 - nBits
+       bits |= e.bits
+       for nBits >= 8 {
+               b := uint8(bits >> 24)
+               e.writeByte(b)
+               if b == 0xff {
+                       e.writeByte(0x00)
+               }
+               bits <<= 8
+               nBits -= 8
+       }
+       e.bits, e.nBits = bits, nBits
+}
+
+// emitHuff emits the given value with the given Huffman encoder.
+func (e *encoder) emitHuff(h huffIndex, value int) {
+       x := theHuffmanLUT[h][value]
+       e.emit(x&(1<<24-1), uint8(x>>24))
+}
+
+// emitHuffRLE emits a run of runLength copies of value encoded with the given
+// Huffman encoder.
+func (e *encoder) emitHuffRLE(h huffIndex, runLength, value int) {
+       a, b := value, value
+       if a < 0 {
+               a, b = -value, value-1
+       }
+       var nBits uint8
+       if a < 0x100 {
+               nBits = bitCount[a]
+       } else {
+               nBits = 8 + bitCount[a>>8]
+       }
+       e.emitHuff(h, runLength<<4|int(nBits))
+       if nBits > 0 {
+               e.emit(uint32(b)&(1<<nBits-1), nBits)
+       }
+}
+
+// writeMarkerHeader writes the header for a marker with the given length.
+func (e *encoder) writeMarkerHeader(marker uint8, markerlen int) {
+       e.buf[0] = 0xff
+       e.buf[1] = marker
+       e.buf[2] = uint8(markerlen >> 8)
+       e.buf[3] = uint8(markerlen & 0xff)
+       e.write(e.buf[:4])
+}
+
+// writeDQT writes the Define Quantization Table marker.
+func (e *encoder) writeDQT() {
+       markerlen := 2
+       for _, q := range e.quant {
+               markerlen += 1 + len(q)
+       }
+       e.writeMarkerHeader(dqtMarker, markerlen)
+       for i, q := range e.quant {
+               e.writeByte(uint8(i))
+               e.write(q[:])
+       }
+}
+
+// writeSOF0 writes the Start Of Frame (Baseline) marker.
+func (e *encoder) writeSOF0(size image.Point) {
+       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] = nComponent
+       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*(nComponent-1)+9])
+}
+
+// writeDHT writes the Define Huffman Table marker.
+func (e *encoder) writeDHT() {
+       markerlen := 2
+       for _, s := range theHuffmanSpec {
+               markerlen += 1 + 16 + len(s.value)
+       }
+       e.writeMarkerHeader(dhtMarker, markerlen)
+       for i, s := range theHuffmanSpec {
+               e.writeByte("\x00\x10\x01\x11"[i])
+               e.write(s.count[:])
+               e.write(s.value)
+       }
+}
+
+// writeBlock writes a block of pixel data using the given quantization table,
+// returning the post-quantized DC value of the DCT-transformed block.
+func (e *encoder) writeBlock(b *block, q quantIndex, prevDC int) int {
+       fdct(b)
+       // Emit the DC delta.
+       dc := div(b[0], (8 * int(e.quant[q][0])))
+       e.emitHuffRLE(huffIndex(2*q+0), 0, dc-prevDC)
+       // Emit the AC components.
+       h, runLength := huffIndex(2*q+1), 0
+       for k := 1; k < blockSize; k++ {
+               ac := div(b[unzig[k]], (8 * int(e.quant[q][k])))
+               if ac == 0 {
+                       runLength++
+               } else {
+                       for runLength > 15 {
+                               e.emitHuff(h, 0xf0)
+                               runLength -= 16
+                       }
+                       e.emitHuffRLE(h, runLength, ac)
+                       runLength = 0
+               }
+       }
+       if runLength > 0 {
+               e.emitHuff(h, 0x00)
+       }
+       return dc
+}
+
+// toYCbCr converts the 8x8 region of m whose top-left corner is p to its
+// YCbCr values.
+func toYCbCr(m image.Image, p image.Point, yBlock, cbBlock, crBlock *block) {
+       b := m.Bounds()
+       xmax := b.Max.X - 1
+       ymax := b.Max.Y - 1
+       for j := 0; j < 8; j++ {
+               for i := 0; i < 8; i++ {
+                       r, g, b, _ := m.At(min(p.X+i, xmax), min(p.Y+j, ymax)).RGBA()
+                       yy, cb, cr := ycbcr.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8))
+                       yBlock[8*j+i] = int(yy)
+                       cbBlock[8*j+i] = int(cb)
+                       crBlock[8*j+i] = int(cr)
+               }
+       }
+}
+
+// scale scales the 16x16 region represented by the 4 src blocks to the 8x8
+// dst block.
+func scale(dst *block, src *[4]block) {
+       for i := 0; i < 4; i++ {
+               dstOff := (i&2)<<4 | (i&1)<<2
+               for y := 0; y < 4; y++ {
+                       for x := 0; x < 4; x++ {
+                               j := 16*y + 2*x
+                               sum := src[i][j] + src[i][j+1] + src[i][j+8] + src[i][j+9]
+                               dst[8*y+x+dstOff] = (sum + 2) >> 2
+                       }
+               }
+       }
+}
+
+// sosHeader 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",
+//     - component 2 uses DC table 1 and AC table 1 "\x02\x11",
+//     - component 3 uses DC table 1 and AC table 1 "\x03\x11",
+//     - padding "\x00\x00\x00".
+var sosHeader = []byte{
+       0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02,
+       0x11, 0x03, 0x11, 0x00, 0x00, 0x00,
+}
+
+// writeSOS writes the StartOfScan marker.
+func (e *encoder) writeSOS(m image.Image) {
+       e.write(sosHeader)
+       var (
+               // Scratch buffers to hold the YCbCr values.
+               yBlock  block
+               cbBlock [4]block
+               crBlock [4]block
+               cBlock  block
+               // DC components are delta-encoded.
+               prevDCY, prevDCCb, prevDCCr int
+       )
+       bounds := m.Bounds()
+       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.Point{x + xOff, y + yOff}
+                               toYCbCr(m, p, &yBlock, &cbBlock[i], &crBlock[i])
+                               prevDCY = e.writeBlock(&yBlock, 0, prevDCY)
+                       }
+                       scale(&cBlock, &cbBlock)
+                       prevDCCb = e.writeBlock(&cBlock, 1, prevDCCb)
+                       scale(&cBlock, &crBlock)
+                       prevDCCr = e.writeBlock(&cBlock, 1, prevDCCr)
+               }
+       }
+       // Pad the last byte with 1's.
+       e.emit(0x7f, 7)
+}
+
+// DefaultQuality is the default quality encoding parameter.
+const DefaultQuality = 75
+
+// Options are the encoding parameters.
+// Quality ranges from 1 to 100 inclusive, higher is better.
+type Options struct {
+       Quality int
+}
+
+// Encode writes the Image m to w in JPEG 4:2:0 baseline format with the given
+// options. Default parameters are used if a nil *Options is passed.
+func Encode(w io.Writer, m image.Image, o *Options) os.Error {
+       b := m.Bounds()
+       if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 {
+               return os.NewError("jpeg: image is too large to encode")
+       }
+       var e encoder
+       if ww, ok := w.(writer); ok {
+               e.w = ww
+       } else {
+               e.w = bufio.NewWriter(w)
+       }
+       // Clip quality to [1, 100].
+       quality := DefaultQuality
+       if o != nil {
+               quality = o.Quality
+               if quality < 1 {
+                       quality = 1
+               } else if quality > 100 {
+                       quality = 100
+               }
+       }
+       // Convert from a quality rating to a scaling factor.
+       var scale int
+       if quality < 50 {
+               scale = 5000 / quality
+       } else {
+               scale = 200 - quality*2
+       }
+       // Initialize the quantization tables.
+       for i := range e.quant {
+               for j := range e.quant[i] {
+                       x := int(unscaledQuant[i][j])
+                       x = (x*scale + 50) / 100
+                       if x < 1 {
+                               x = 1
+                       } else if x > 255 {
+                               x = 255
+                       }
+                       e.quant[i][j] = uint8(x)
+               }
+       }
+       // Write the Start Of Image marker.
+       e.buf[0] = 0xff
+       e.buf[1] = 0xd8
+       e.write(e.buf[:2])
+       // Write the quantization tables.
+       e.writeDQT()
+       // Write the image dimensions.
+       e.writeSOF0(b.Size())
+       // Write the Huffman tables.
+       e.writeDHT()
+       // Write the image data.
+       e.writeSOS(m)
+       // Write the End Of Image marker.
+       e.buf[0] = 0xff
+       e.buf[1] = 0xd9
+       e.write(e.buf[:2])
+       e.flush()
+       return e.err
+}
diff --git a/src/pkg/image/jpeg/writer_test.go b/src/pkg/image/jpeg/writer_test.go
new file mode 100644 (file)
index 0000000..00922dd
--- /dev/null
@@ -0,0 +1,87 @@
+// 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 jpeg
+
+import (
+       "bytes"
+       "image"
+       "image/png"
+       "os"
+       "testing"
+)
+
+var testCase = []struct {
+       filename  string
+       quality   int
+       tolerance int64
+}{
+       {"../testdata/video-001.png", 1, 24 << 8},
+       {"../testdata/video-001.png", 20, 12 << 8},
+       {"../testdata/video-001.png", 60, 8 << 8},
+       {"../testdata/video-001.png", 80, 6 << 8},
+       {"../testdata/video-001.png", 90, 4 << 8},
+       {"../testdata/video-001.png", 100, 2 << 8},
+}
+
+func delta(u0, u1 uint32) int64 {
+       d := int64(u0) - int64(u1)
+       if d < 0 {
+               return -d
+       }
+       return d
+}
+
+func readPng(filename string) (image.Image, os.Error) {
+       f, err := os.Open(filename)
+       if err != nil {
+               return nil, err
+       }
+       defer f.Close()
+       return png.Decode(f)
+}
+
+func TestWriter(t *testing.T) {
+       for _, tc := range testCase {
+               // Read the image.
+               m0, err := readPng(tc.filename)
+               if err != nil {
+                       t.Error(tc.filename, err)
+                       continue
+               }
+               // Encode that image as JPEG.
+               buf := bytes.NewBuffer(nil)
+               err = Encode(buf, m0, &Options{Quality: tc.quality})
+               if err != nil {
+                       t.Error(tc.filename, err)
+                       continue
+               }
+               // Decode that JPEG.
+               m1, err := Decode(buf)
+               if err != nil {
+                       t.Error(tc.filename, err)
+                       continue
+               }
+               // Compute the average delta in RGB space.
+               b := m0.Bounds()
+               var sum, n int64
+               for y := b.Min.Y; y < b.Max.Y; y++ {
+                       for x := b.Min.X; x < b.Max.X; x++ {
+                               c0 := m0.At(x, y)
+                               c1 := m1.At(x, y)
+                               r0, g0, b0, _ := c0.RGBA()
+                               r1, g1, b1, _ := c1.RGBA()
+                               sum += delta(r0, r1)
+                               sum += delta(g0, g1)
+                               sum += delta(b0, b1)
+                               n += 3
+                       }
+               }
+               // Compare the average delta to the tolerance level.
+               if sum/n > tc.tolerance {
+                       t.Errorf("%s, quality=%d: average delta is too high", tc.filename, tc.quality)
+                       continue
+               }
+       }
+}