1
0
mirror of https://github.com/golang/go synced 2024-11-25 02:07:58 -07:00

image/jpeg: add an encoder.

It is based on changeset 4186064 by Raph Levien <raph@google.com>.

R=r, nigeltao_gnome
CC=golang-dev
https://golang.org/cl/4435051
This commit is contained in:
Nigel Tao 2011-04-19 11:00:47 +10:00
parent 3bac16a6bf
commit 5500f027f7
7 changed files with 811 additions and 8 deletions

View File

@ -183,7 +183,6 @@ NOTEST+=\
hash\ hash\
http/pprof\ http/pprof\
http/httptest\ http/httptest\
image/jpeg\
net/dict\ net/dict\
rand\ rand\
runtime/cgo\ runtime/cgo\

View File

@ -6,8 +6,10 @@ include ../../../Make.inc
TARG=image/jpeg TARG=image/jpeg
GOFILES=\ GOFILES=\
fdct.go\
huffman.go\ huffman.go\
idct.go\ idct.go\
reader.go\ reader.go\
writer.go\
include ../../../Make.pkg include ../../../Make.pkg

190
src/pkg/image/jpeg/fdct.go Normal file
View File

@ -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)
}
}

View File

@ -63,7 +63,7 @@ const (
// //
// For more on the actual algorithm, see Z. Wang, "Fast algorithms for the discrete W transform and // 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. // 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. // Horizontal 1-D IDCT.
for y := 0; y < 8; y++ { for y := 0; y < 8; y++ {
// If all the AC components are zero, then the IDCT is trivial. // If all the AC components are zero, then the IDCT is trivial.

View File

@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // 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 package jpeg
// See http://www.w3.org/Graphics/JPEG/itu-t81.pdf
import ( import (
"bufio" "bufio"
"image" "image"
@ -32,6 +32,8 @@ type component struct {
tq uint8 // Quantization table destination selector. tq uint8 // Quantization table destination selector.
} }
type block [blockSize]int
const ( const (
blockSize = 64 // A DCT block is 8x8. blockSize = 64 // A DCT block is 8x8.
@ -88,9 +90,9 @@ type decoder struct {
ri int // Restart Interval. ri int // Restart Interval.
comps [nComponent]component comps [nComponent]component
huff [maxTc + 1][maxTh + 1]huffman huff [maxTc + 1][maxTh + 1]huffman
quant [maxTq + 1][blockSize]int quant [maxTq + 1]block
b bits b bits
blocks [nComponent][maxH * maxV][blockSize]int blocks [nComponent][maxH * maxV]block
tmp [1024]byte 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)) myy := (d.height + 8*int(v0) - 1) / (8 * int(v0))
mcu, expectedRST := 0, uint8(rst0Marker) mcu, expectedRST := 0, uint8(rst0Marker)
var allZeroes [blockSize]int var allZeroes block
var dc [nComponent]int var dc [nComponent]int
for my := 0; my < myy; my++ { for my := 0; my < myy; my++ {
for mx := 0; mx < mxx; mx++ { for mx := 0; mx < mxx; mx++ {

View File

@ -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
}

View File

@ -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
}
}
}