Basic image/jpeg decoder.

This is not a complete JPEG implementation (e.g. it does not handle
progressive JPEGs or restart markers), but I was able to take a photo
with my phone, and view the resultant JPEG in pure Go.

The decoder is simple, but slow. The Huffman decoder in particular
should be easily improvable, but optimization is left to future
changelists. Being able to inline functions in the inner loop should
also help performance.

The output is not pixel-for-pixel identical to libjpeg, although
identical behavior isn't necessarily a goal, since JPEG is a lossy
codec. There are at least two reasons for the discrepancy.

First, the inverse DCT algorithm used is the same as Plan9's
src/cmd/jpg, which has different rounding errors from libjpeg's
default IDCT implementation. Note that libjpeg actually has three
different IDCT implementations: one floating point, and two fixed
point. Out of those four, Plan9's seemed the simplest to understand,
partly because it has no #ifdef's or C macros.

Second, for 4:2:2 or 4:2:0 chroma sampling, this implementation does
nearest neighbor upsampling, compared to libjpeg's triangle filter
(e.g. see h2v1_fancy_upsample in jdsample.c).

The difference from the first reason is typically zero, but sometimes
1 (out of 256) in YCbCr space, or double that in RGB space. The
difference from the second reason can be as large as 8/256 in YCbCr
space, in regions of steep chroma gradients. Informal eyeballing
suggests that the net difference is typically imperceptible, though.

R=r
CC=golang-dev, rsc
https://golang.org/cl/164056
This commit is contained in:
Nigel Tao 2009-12-17 10:32:17 +11:00
parent 2e5a720647
commit 8bf58725b2
5 changed files with 787 additions and 0 deletions

View File

@ -72,6 +72,7 @@ DIRS=\
hash/crc32\ hash/crc32\
http\ http\
image\ image\
image/jpeg\
image/png\ image/png\
io\ io\
io/ioutil\ io/ioutil\
@ -117,6 +118,7 @@ NOTEST=\
go/token\ go/token\
hash\ hash\
image\ image\
image/jpeg\
malloc\ malloc\
rand\ rand\
runtime\ runtime\

View File

@ -0,0 +1,13 @@
# Copyright 2009 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.
include $(GOROOT)/src/Make.$(GOARCH)
TARG=image/jpeg
GOFILES=\
huffman.go\
idct.go\
reader.go\
include $(GOROOT)/src/Make.pkg

View File

@ -0,0 +1,190 @@
// Copyright 2009 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 (
"io"
"os"
)
// Each code is at most 16 bits long.
const maxCodeLength = 16
// Each decoded value is a uint8, so there are at most 256 such values.
const maxNumValues = 256
// Bit stream for the Huffman decoder.
// The n least significant bits of a form the unread bits, to be read in MSB to LSB order.
type bits struct {
a int // accumulator.
n int // the number of unread bits in a.
m int // mask. m==1<<(n-1) when n>0, with m==0 when n==0.
}
// Huffman table decoder, specified in section C.
type huffman struct {
l [maxCodeLength]int
length int // sum of l[i].
val [maxNumValues]uint8 // the decoded values, as sorted by their encoding.
size [maxNumValues]int // size[i] is the number of bits to encode val[i].
code [maxNumValues]int // code[i] is the encoding of val[i].
minCode [maxCodeLength]int // min codes of length i, or -1 if no codes of that length.
maxCode [maxCodeLength]int // max codes of length i, or -1 if no codes of that length.
valIndex [maxCodeLength]int // index into val of minCode[i].
}
// Reads bytes from the io.Reader to ensure that bits.n is at least n.
func (d *decoder) ensureNBits(n int) os.Error {
for d.b.n < n {
c, err := d.r.ReadByte()
if err != nil {
return err
}
d.b.a = d.b.a<<8 | int(c)
d.b.n += 8
if d.b.m == 0 {
d.b.m = 1 << 7
} else {
d.b.m <<= 8
}
// Byte stuffing, specified in section F.1.2.3.
if c == 0xff {
c, err = d.r.ReadByte()
if err != nil {
return err
}
if c != 0x00 {
return FormatError("missing 0xff00 sequence")
}
}
}
return nil
}
// The composition of RECEIVE and EXTEND, specified in section F.2.2.1.
func (d *decoder) receiveExtend(t uint8) (int, os.Error) {
err := d.ensureNBits(int(t))
if err != nil {
return 0, err
}
d.b.n -= int(t)
d.b.m >>= t
s := 1 << t
x := (d.b.a >> uint8(d.b.n)) & (s - 1)
if x < s>>1 {
x += ((-1) << t) + 1
}
return x, nil
}
// Processes a Define Huffman Table marker, and initializes a huffman struct from its contents.
// Specified in section B.2.4.2.
func (d *decoder) processDHT(n int) os.Error {
for n > 0 {
if n < 17 {
return FormatError("DHT has wrong length")
}
_, err := io.ReadFull(d.r, d.tmp[0:17])
if err != nil {
return err
}
tc := d.tmp[0] >> 4
if tc > maxTc {
return FormatError("bad Tc value")
}
th := d.tmp[0] & 0x0f
const isBaseline = true // Progressive mode is not yet supported.
if th > maxTh || isBaseline && th > 1 {
return FormatError("bad Th value")
}
h := &d.huff[tc][th]
// Read l and val (and derive length).
h.length = 0
for i := 0; i < maxCodeLength; i++ {
h.l[i] = int(d.tmp[i+1])
h.length += h.l[i]
}
if h.length == 0 {
return FormatError("Huffman table has zero length")
}
if h.length > maxNumValues {
return FormatError("Huffman table has excessive length")
}
n -= h.length + 17
if n < 0 {
return FormatError("DHT has wrong length")
}
_, err = io.ReadFull(d.r, h.val[0:h.length])
if err != nil {
return err
}
// Derive size.
k := 0
for i := 0; i < maxCodeLength; i++ {
for j := 0; j < h.l[i]; j++ {
h.size[k] = i + 1
k++
}
}
// Derive code.
code := 0
size := h.size[0]
for i := 0; i < h.length; i++ {
if size != h.size[i] {
code <<= uint8(h.size[i] - size)
size = h.size[i]
}
h.code[i] = code
code++
}
// Derive minCode, maxCode, and valIndex.
k = 0
index := 0
for i := 0; i < maxCodeLength; i++ {
if h.l[i] == 0 {
h.minCode[i] = -1
h.maxCode[i] = -1
h.valIndex[i] = -1
} else {
h.minCode[i] = k
h.maxCode[i] = k + h.l[i] - 1
h.valIndex[i] = index
k += h.l[i]
index += h.l[i]
}
k <<= 1
}
}
return nil
}
// Returns the next Huffman-coded value from the bit stream, decoded according to h.
// TODO(nigeltao): This decoding algorithm is simple, but slow. A lookahead table, instead of always
// peeling off only 1 bit at at time, ought to be faster.
func (d *decoder) decodeHuffman(h *huffman) (uint8, os.Error) {
if h.length == 0 {
return 0, FormatError("uninitialized Huffman table")
}
for i, code := 0, 0; i < maxCodeLength; i++ {
err := d.ensureNBits(1)
if err != nil {
return 0, err
}
if d.b.a&d.b.m != 0 {
code |= 1
}
d.b.n--
d.b.m >>= 1
if code <= h.maxCode[i] {
return h.val[h.valIndex[i]+code-h.minCode[i]], nil
}
code <<= 1
}
return 0, FormatError("bad Huffman code")
}

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

@ -0,0 +1,190 @@
// Copyright 2009 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.
// This is a Go translation of idct.c from
//
// http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_IEC_13818-4_2004_Conformance_Testing/Video/verifier/mpeg2decode_960109.tar.gz
//
// which carries the following notice:
/* Copyright (C) 1996, MPEG Software Simulation Group. All Rights Reserved. */
/*
* Disclaimer of Warranty
*
* These software programs are available to the user without any license fee or
* royalty on an "as is" basis. The MPEG Software Simulation Group disclaims
* any and all warranties, whether express, implied, or statuary, including any
* implied warranties or merchantability or of fitness for a particular
* purpose. In no event shall the copyright-holder be liable for any
* incidental, punitive, or consequential damages of any kind whatsoever
* arising from the use of these programs.
*
* This disclaimer of warranty extends to the user of these programs and user's
* customers, employees, agents, transferees, successors, and assigns.
*
* The MPEG Software Simulation Group does not represent or warrant that the
* programs furnished hereunder are free of infringement of any third-party
* patents.
*
* Commercial implementations of MPEG-1 and MPEG-2 video, including shareware,
* are subject to royalty fees to patent holders. Many of these patents are
* general enough such that they are unavoidable regardless of implementation
* design.
*
*/
package jpeg
const (
w1 = 2841 // 2048*sqrt(2)*cos(1*pi/16)
w2 = 2676 // 2048*sqrt(2)*cos(2*pi/16)
w3 = 2408 // 2048*sqrt(2)*cos(3*pi/16)
w5 = 1609 // 2048*sqrt(2)*cos(5*pi/16)
w6 = 1108 // 2048*sqrt(2)*cos(6*pi/16)
w7 = 565 // 2048*sqrt(2)*cos(7*pi/16)
w1pw7 = w1 + w7
w1mw7 = w1 - w7
w2pw6 = w2 + w6
w2mw6 = w2 - w6
w3pw5 = w3 + w5
w3mw5 = w3 - w5
r2 = 181 // 256/sqrt(2)
)
// 2-D Inverse Discrete Cosine Transformation, followed by a +128 level shift.
//
// The input coefficients should already have been multiplied by the appropriate quantization table.
// We use fixed-point computation, with the number of bits for the fractional component varying over the
// intermediate stages. The final values are expected to range within [0, 255], after a +128 level shift.
//
// 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) {
// Horizontal 1-D IDCT.
for y := 0; y < 8; y++ {
// If all the AC components are zero, then the IDCT is trivial.
if b[y*8+1] == 0 && b[y*8+2] == 0 && b[y*8+3] == 0 &&
b[y*8+4] == 0 && b[y*8+5] == 0 && b[y*8+6] == 0 && b[y*8+7] == 0 {
dc := b[y*8+0] << 3
b[y*8+0] = dc
b[y*8+1] = dc
b[y*8+2] = dc
b[y*8+3] = dc
b[y*8+4] = dc
b[y*8+5] = dc
b[y*8+6] = dc
b[y*8+7] = dc
continue
}
// Prescale.
x0 := (b[y*8+0] << 11) + 128
x1 := b[y*8+4] << 11
x2 := b[y*8+6]
x3 := b[y*8+2]
x4 := b[y*8+1]
x5 := b[y*8+7]
x6 := b[y*8+5]
x7 := b[y*8+3]
// Stage 1.
x8 := w7 * (x4 + x5)
x4 = x8 + w1mw7*x4
x5 = x8 - w1pw7*x5
x8 = w3 * (x6 + x7)
x6 = x8 - w3mw5*x6
x7 = x8 - w3pw5*x7
// Stage 2.
x8 = x0 + x1
x0 -= x1
x1 = w6 * (x3 + x2)
x2 = x1 - w2pw6*x2
x3 = x1 + w2mw6*x3
x1 = x4 + x6
x4 -= x6
x6 = x5 + x7
x5 -= x7
// Stage 3.
x7 = x8 + x3
x8 -= x3
x3 = x0 + x2
x0 -= x2
x2 = (r2*(x4+x5) + 128) >> 8
x4 = (r2*(x4-x5) + 128) >> 8
// Stage 4.
b[8*y+0] = (x7 + x1) >> 8
b[8*y+1] = (x3 + x2) >> 8
b[8*y+2] = (x0 + x4) >> 8
b[8*y+3] = (x8 + x6) >> 8
b[8*y+4] = (x8 - x6) >> 8
b[8*y+5] = (x0 - x4) >> 8
b[8*y+6] = (x3 - x2) >> 8
b[8*y+7] = (x7 - x1) >> 8
}
// Vertical 1-D IDCT.
for x := 0; x < 8; x++ {
// Similar to the horizontal 1-D IDCT case, if all the AC components are zero, then the IDCT is trivial.
// However, after performing the horizontal 1-D IDCT, there are typically non-zero AC components, so
// we do not bother to check for the all-zero case.
// Prescale.
y0 := (b[8*0+x] << 8) + 8192
y1 := b[8*4+x] << 8
y2 := b[8*6+x]
y3 := b[8*2+x]
y4 := b[8*1+x]
y5 := b[8*7+x]
y6 := b[8*5+x]
y7 := b[8*3+x]
// Stage 1.
y8 := w7*(y4+y5) + 4
y4 = (y8 + w1mw7*y4) >> 3
y5 = (y8 - w1pw7*y5) >> 3
y8 = w3*(y6+y7) + 4
y6 = (y8 - w3mw5*y6) >> 3
y7 = (y8 - w3pw5*y7) >> 3
// Stage 2.
y8 = y0 + y1
y0 -= y1
y1 = w6*(y3+y2) + 4
y2 = (y1 - w2pw6*y2) >> 3
y3 = (y1 + w2mw6*y3) >> 3
y1 = y4 + y6
y4 -= y6
y6 = y5 + y7
y5 -= y7
// Stage 3.
y7 = y8 + y3
y8 -= y3
y3 = y0 + y2
y0 -= y2
y2 = (r2*(y4+y5) + 128) >> 8
y4 = (r2*(y4-y5) + 128) >> 8
// Stage 4.
b[8*0+x] = (y7 + y1) >> 14
b[8*1+x] = (y3 + y2) >> 14
b[8*2+x] = (y0 + y4) >> 14
b[8*3+x] = (y8 + y6) >> 14
b[8*4+x] = (y8 - y6) >> 14
b[8*5+x] = (y0 - y4) >> 14
b[8*6+x] = (y3 - y2) >> 14
b[8*7+x] = (y7 - y1) >> 14
}
// Level shift.
for i := range *b {
b[i] += 128
}
}

View File

@ -0,0 +1,392 @@
// Copyright 2009 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.
// The jpeg package implements a decoder for JPEG images, as defined in ITU-T T.81.
package jpeg
// See http://www.w3.org/Graphics/JPEG/itu-t81.pdf
import (
"bufio"
"image"
"io"
"os"
)
// A FormatError reports that the input is not a valid JPEG.
type FormatError string
func (e FormatError) String() string { return "invalid JPEG format: " + string(e) }
// An UnsupportedError reports that the input uses a valid but unimplemented JPEG feature.
type UnsupportedError string
func (e UnsupportedError) String() string { return "unsupported JPEG feature: " + string(e) }
// Component specification, specified in section B.2.2.
type component struct {
c uint8 // Component identifier.
h uint8 // Horizontal sampling factor.
v uint8 // Vertical sampling factor.
tq uint8 // Quantization table destination selector.
}
const (
blockSize = 64 // A DCT block is 8x8.
dcTableClass = 0
acTableClass = 1
maxTc = 1
maxTh = 3
maxTq = 3
// We only support 4:4:4, 4:2:2 and 4:2:0 downsampling, and assume that the components are Y, Cb, Cr.
nComponent = 3
maxH = 2
maxV = 2
)
const (
soiMarker = 0xd8 // Start Of Image.
eoiMarker = 0xd9 // End Of Image.
sof0Marker = 0xc0 // Start Of Frame (Baseline).
sof2Marker = 0xc2 // Start Of Frame (Progressive).
dhtMarker = 0xc4 // Define Huffman Table.
dqtMarker = 0xdb // Define Quantization Table.
sosMarker = 0xda // Start Of Scan.
app0Marker = 0xe0 // APPlication specific (0).
app15Marker = 0xef // APPlication specific (15).
comMarker = 0xfe // COMment.
)
// Maps from the zig-zag ordering to the natural ordering.
var unzig = [blockSize]int{
0, 1, 8, 16, 9, 2, 3, 10,
17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34,
27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36,
29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46,
53, 60, 61, 54, 47, 55, 62, 63,
}
// If the passed in io.Reader does not also have ReadByte, then Decode will introduce its own buffering.
type Reader interface {
io.Reader
ReadByte() (c byte, err os.Error)
}
type decoder struct {
r Reader
width, height int
image *image.RGBA
comps [nComponent]component
huff [maxTc + 1][maxTh + 1]huffman
quant [maxTq + 1][blockSize]int
b bits
blocks [nComponent][maxH * maxV][blockSize]int
tmp [1024]byte
}
// Reads and ignores the next n bytes.
func (d *decoder) ignore(n int) os.Error {
for n > 0 {
m := len(d.tmp)
if m > n {
m = n
}
_, err := io.ReadFull(d.r, d.tmp[0:m])
if err != nil {
return err
}
n -= m
}
return nil
}
// Specified in section B.2.2.
func (d *decoder) processSOF(n int) os.Error {
if n != 6+3*nComponent {
return UnsupportedError("SOF has wrong length")
}
_, err := io.ReadFull(d.r, d.tmp[0:6+3*nComponent])
if err != nil {
return err
}
// We only support 8-bit precision.
if d.tmp[0] != 8 {
return UnsupportedError("precision")
}
d.height = int(d.tmp[1])<<8 + int(d.tmp[2])
d.width = int(d.tmp[3])<<8 + int(d.tmp[4])
if d.tmp[5] != nComponent {
return UnsupportedError("SOF has wrong number of image components")
}
for i := 0; i < nComponent; i++ {
hv := d.tmp[7+3*i]
d.comps[i].c = d.tmp[6+3*i]
d.comps[i].h = hv >> 4
d.comps[i].v = hv & 0x0f
d.comps[i].tq = d.tmp[8+3*i]
// We only support YCbCr images, and 4:4:4, 4:2:2 or 4:2:0 chroma downsampling ratios. This implies that
// the (h, v) values for the Y component are either (1, 1), (2, 1) or (2, 2), and the
// (h, v) values for the Cr and Cb components must be (1, 1).
if i == 0 {
if hv != 0x11 && hv != 0x21 && hv != 0x22 {
return UnsupportedError("luma downsample ratio")
}
} else {
if hv != 0x11 {
return UnsupportedError("chroma downsample ratio")
}
}
}
d.image = image.NewRGBA(d.width, d.height)
return nil
}
// Specified in section B.2.4.1.
func (d *decoder) processDQT(n int) os.Error {
const qtLength = 1 + blockSize
for ; n >= qtLength; n -= qtLength {
_, err := io.ReadFull(d.r, d.tmp[0:qtLength])
if err != nil {
return err
}
pq := d.tmp[0] >> 4
if pq != 0 {
return UnsupportedError("bad Pq value")
}
tq := d.tmp[0] & 0x0f
if tq > maxTq {
return FormatError("bad Tq value")
}
for i := range d.quant[tq] {
d.quant[tq][i] = int(d.tmp[i+1])
}
}
if n != 0 {
return FormatError("DQT has wrong length")
}
return nil
}
// Set the Pixel (px, py)'s RGB value, based on its YCbCr value.
func (d *decoder) calcPixel(px, py, lumaBlock, lumaIndex, chromaIndex int) {
y, cb, cr := d.blocks[0][lumaBlock][lumaIndex], d.blocks[1][0][chromaIndex], d.blocks[2][0][chromaIndex]
// The JFIF specification (http://www.w3.org/Graphics/JPEG/jfif3.pdf, page 3) gives the formula
// for translating YCbCr to RGB as:
// R = Y + 1.402 (Cr-128)
// G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
// B = Y + 1.772 (Cb-128)
yPlusHalf := 100000*y + 50000
cb -= 128
cr -= 128
r := (yPlusHalf + 140200*cr) / 100000
g := (yPlusHalf - 34414*cb - 71414*cr) / 100000
b := (yPlusHalf + 177200*cb) / 100000
if r < 0 {
r = 0
} else if r > 255 {
r = 255
}
if g < 0 {
g = 0
} else if g > 255 {
g = 255
}
if b < 0 {
b = 0
} else if b > 255 {
b = 255
}
d.image.Pixel[py][px] = image.RGBAColor{uint8(r), uint8(g), uint8(b), 0xff}
}
// Convert the MCU from YCbCr to RGB.
func (d *decoder) convertMCU(mx, my, h0, v0 int) {
lumaBlock := 0
for v := 0; v < v0; v++ {
for h := 0; h < h0; h++ {
chromaBase := 8*4*v + 4*h
py := 8 * (v0*my + v)
for y := 0; y < 8 && py < d.height; y++ {
px := 8 * (h0*mx + h)
lumaIndex := 8 * y
chromaIndex := chromaBase + 8*(y/v0)
for x := 0; x < 8 && px < d.width; x++ {
d.calcPixel(px, py, lumaBlock, lumaIndex, chromaIndex)
if h0 == 1 {
chromaIndex += 1
} else {
chromaIndex += x % 2
}
lumaIndex++
px++
}
py++
}
lumaBlock++
}
}
}
// Specified in section B.2.3.
func (d *decoder) processSOS(n int) os.Error {
if d.image == nil {
return FormatError("missing SOF segment")
}
if n != 4+2*nComponent {
return UnsupportedError("SOS has wrong length")
}
_, err := io.ReadFull(d.r, d.tmp[0:4+2*nComponent])
if err != nil {
return err
}
if d.tmp[0] != nComponent {
return UnsupportedError("SOS has wrong number of image components")
}
var scanComps [nComponent]struct {
td uint8 // DC table selector.
ta uint8 // AC table selector.
}
h0, v0 := int(d.comps[0].h), int(d.comps[0].v) // The h and v values from the Y components.
for i := 0; i < nComponent; i++ {
cs := d.tmp[1+2*i] // Component selector.
if cs != d.comps[i].c {
return UnsupportedError("scan components out of order")
}
scanComps[i].td = d.tmp[2+2*i] >> 4
scanComps[i].ta = d.tmp[2+2*i] & 0x0f
}
// mxx and myy are the number of MCUs (Minimum Coded Units) in the image.
mxx := (d.width + 8*int(h0) - 1) / (8 * int(h0))
myy := (d.height + 8*int(v0) - 1) / (8 * int(v0))
var allZeroes [blockSize]int
var dc [nComponent]int
for my := 0; my < myy; my++ {
for mx := 0; mx < mxx; mx++ {
for i := 0; i < nComponent; i++ {
qt := &d.quant[d.comps[i].tq]
for j := 0; j < int(d.comps[i].h*d.comps[i].v); j++ {
d.blocks[i][j] = allZeroes
// Decode the DC coefficient, as specified in section F.2.2.1.
value, err := d.decodeHuffman(&d.huff[dcTableClass][scanComps[i].td])
if err != nil {
return err
}
if value > 16 {
return UnsupportedError("excessive DC component")
}
dcDelta, err := d.receiveExtend(value)
if err != nil {
return err
}
dc[i] += dcDelta
d.blocks[i][j][0] = dc[i] * qt[0]
// Decode the AC coefficients, as specified in section F.2.2.2.
for k := 1; k < blockSize; k++ {
value, err := d.decodeHuffman(&d.huff[acTableClass][scanComps[i].ta])
if err != nil {
return err
}
v0 := value >> 4
v1 := value & 0x0f
if v1 != 0 {
k += int(v0)
if k > blockSize {
return FormatError("bad DCT index")
}
ac, err := d.receiveExtend(v1)
if err != nil {
return err
}
d.blocks[i][j][unzig[k]] = ac * qt[k]
} else {
if v0 != 0x0f {
break
}
k += 0x0f
}
}
idct(&d.blocks[i][j])
} // for j
} // for i
d.convertMCU(mx, my, int(d.comps[0].h), int(d.comps[0].v))
} // for mx
} // for my
return nil
}
// Decode reads a JPEG formatted image from r and returns it as an image.Image.
func Decode(r io.Reader) (image.Image, os.Error) {
var d decoder
if rr, ok := r.(Reader); ok {
d.r = rr
} else {
d.r = bufio.NewReader(r)
}
// Check for the Start Of Image marker.
_, err := io.ReadFull(r, d.tmp[0:2])
if err != nil {
return nil, err
}
if d.tmp[0] != 0xff || d.tmp[1] != soiMarker {
return nil, FormatError("missing SOI marker")
}
// Process the remaining segments until the End Of Image marker.
for {
_, err := io.ReadFull(r, d.tmp[0:2])
if err != nil {
return nil, err
}
if d.tmp[0] != 0xff {
return nil, FormatError("missing 0xff marker start")
}
marker := d.tmp[1]
if marker == eoiMarker { // End Of Image.
break
}
// Read the 16-bit length of the segment. The value includes the 2 bytes for the
// length itself, so we subtract 2 to get the number of remaining bytes.
_, err = io.ReadFull(r, d.tmp[0:2])
if err != nil {
return nil, err
}
n := int(d.tmp[0])<<8 + int(d.tmp[1]) - 2
if n < 0 {
return nil, FormatError("short segment length")
}
switch {
case marker == sof0Marker: // Start Of Frame (Baseline).
err = d.processSOF(n)
case marker == sof2Marker: // Start Of Frame (Progressive).
err = UnsupportedError("progressive mode")
case marker == dhtMarker: // Define Huffman Table.
err = d.processDHT(n)
case marker == dqtMarker: // Define Quantization Table.
err = d.processDQT(n)
case marker == sosMarker: // Start Of Scan.
err = d.processSOS(n)
case marker >= app0Marker && marker <= app15Marker || marker == comMarker: // APPlication specific, or COMment.
err = d.ignore(n)
default:
err = UnsupportedError("unknown marker")
}
if err != nil {
return nil, err
}
}
return d.image, nil
}