Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(199)

Delta Between Two Patch Sets: src/pkg/image/jpeg/writer.go

Issue 4435051: code review 4435051: image/jpeg: add an encoder. (Closed)
Left Patch Set: diff -r 6994a5f47fb9 https://go.googlecode.com/hg/ Created 13 years, 11 months ago
Right Patch Set: diff -r da956632c61b https://go.googlecode.com/hg/ Created 13 years, 11 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « src/pkg/image/jpeg/reader.go ('k') | src/pkg/image/jpeg/writer_test.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 // Copyright 2011 The Go Authors. All rights reserved. 1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 package jpeg 5 package jpeg
6 6
7 import ( 7 import (
8 "bufio" 8 "bufio"
9 "image" 9 "image"
10 "image/ycbcr" 10 "image/ycbcr"
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after
164 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 164 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
165 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 165 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
166 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 166 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
167 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 167 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
168 0xf9, 0xfa, 168 0xf9, 0xfa,
169 }, 169 },
170 }, 170 },
171 } 171 }
172 172
173 // huffmanLUT is a compiled look-up table representation of a huffmanSpec. 173 // huffmanLUT is a compiled look-up table representation of a huffmanSpec.
174 // Each value maps to an uint32 of which the 8 most significant bits hold the 174 // Each value maps to a uint32 of which the 8 most significant bits hold the
r 2011/04/18 19:30:42 s/an/a/
nigeltao 2011/04/19 00:47:06 Done.
175 // codeword size in bits and the 24 least significant bits hold the codeword. 175 // codeword size in bits and the 24 least significant bits hold the codeword.
176 // The maximum codeword size is 16 bits. 176 // The maximum codeword size is 16 bits.
177 type huffmanLUT []uint32 177 type huffmanLUT []uint32
178 178
179 func (h *huffmanLUT) init(s huffmanSpec) { 179 func (h *huffmanLUT) init(s huffmanSpec) {
180 maxValue := 0 180 maxValue := 0
181 for _, v := range s.value { 181 for _, v := range s.value {
182 if int(v) > maxValue { 182 if int(v) > maxValue {
183 maxValue = int(v) 183 maxValue = int(v)
184 } 184 }
(...skipping 18 matching lines...) Expand all
203 for i, s := range theHuffmanSpec { 203 for i, s := range theHuffmanSpec {
204 theHuffmanLUT[i].init(s) 204 theHuffmanLUT[i].init(s)
205 } 205 }
206 } 206 }
207 207
208 // writer is a buffered writer. 208 // writer is a buffered writer.
209 type writer interface { 209 type writer interface {
210 Flush() os.Error 210 Flush() os.Error
211 Write([]byte) (int, os.Error) 211 Write([]byte) (int, os.Error)
212 WriteByte(byte) os.Error 212 WriteByte(byte) os.Error
213 WriteString(string) (int, os.Error)
214 }
215
216 // An encoder wraps a writer's methods to save the first returned error as
217 // e.err and return void.
r 2011/04/18 19:30:42 void?
nigeltao 2011/04/19 00:47:06 Rewritten.
218
219 func (e *encoder) flush() {
220 if e.err != nil {
221 return
222 }
223 e.err = e.w.Flush()
224 }
225
226 func (e *encoder) write(p []byte) {
227 if e.err != nil {
228 return
229 }
230 _, e.err = e.w.Write(p)
231 }
232
233 func (e *encoder) writeByte(b byte) {
234 if e.err != nil {
235 return
236 }
237 e.err = e.w.WriteByte(b)
238 }
239
240 func (e *encoder) writeString(s string) {
241 if e.err != nil {
242 return
243 }
244 _, e.err = e.w.WriteString(s)
245 } 213 }
246 214
247 // encoder encodes an image to the JPEG format. 215 // encoder encodes an image to the JPEG format.
r 2011/04/18 19:30:42 move this above its methods and combine the commen
nigeltao 2011/04/19 00:47:06 Done.
248 type encoder struct { 216 type encoder struct {
249 » // w is the writer to write to. err is the first error encountered 217 » // w is the writer to write to. err is the first error encountered durin g
250 » // during writing. 218 » // writing. All attempted writes after the first error become no-ops.
251 w writer 219 w writer
252 err os.Error 220 err os.Error
221 // buf is a scratch buffer.
222 buf [16]byte
253 // bits and nBits are accumulated bits to write to w. 223 // bits and nBits are accumulated bits to write to w.
254 bits uint32 224 bits uint32
255 nBits uint8 225 nBits uint8
256 // quant is the scaled quantization tables. 226 // quant is the scaled quantization tables.
257 quant [nQuantIndex][blockSize]byte 227 quant [nQuantIndex][blockSize]byte
228 }
229
230 func (e *encoder) flush() {
231 if e.err != nil {
232 return
233 }
234 e.err = e.w.Flush()
235 }
236
237 func (e *encoder) write(p []byte) {
238 if e.err != nil {
239 return
240 }
241 _, e.err = e.w.Write(p)
242 }
243
244 func (e *encoder) writeByte(b byte) {
245 if e.err != nil {
246 return
247 }
248 e.err = e.w.WriteByte(b)
258 } 249 }
259 250
260 // emit emits the least significant nBits bits of bits to the bitstream. 251 // emit emits the least significant nBits bits of bits to the bitstream.
r 2011/04/18 19:30:42 nBits bits of bits to the bits. wow.
261 // The precondition is bits < 1<<nBits && nBits <= 16. 252 // The precondition is bits < 1<<nBits && nBits <= 16.
262 func (e *encoder) emit(bits uint32, nBits uint8) { 253 func (e *encoder) emit(bits uint32, nBits uint8) {
263 nBits += e.nBits 254 nBits += e.nBits
264 bits <<= 32 - nBits 255 bits <<= 32 - nBits
265 bits |= e.bits 256 bits |= e.bits
266 for nBits >= 8 { 257 for nBits >= 8 {
267 b := uint8(bits >> 24) 258 b := uint8(bits >> 24)
268 e.writeByte(b) 259 e.writeByte(b)
269 » » if b == 255 { 260 » » if b == 0xff {
270 e.writeByte(0x00) 261 e.writeByte(0x00)
271 } 262 }
272 bits <<= 8 263 bits <<= 8
273 nBits -= 8 264 nBits -= 8
274 } 265 }
275 e.bits, e.nBits = bits, nBits 266 e.bits, e.nBits = bits, nBits
276 } 267 }
277 268
278 // emitHuff emits the given value with the given Huffman encoder. 269 // emitHuff emits the given value with the given Huffman encoder.
279 func (e *encoder) emitHuff(h huffIndex, value int) { 270 func (e *encoder) emitHuff(h huffIndex, value int) {
(...skipping 13 matching lines...) Expand all
293 nBits = bitCount[a] 284 nBits = bitCount[a]
294 } else { 285 } else {
295 nBits = 8 + bitCount[a>>8] 286 nBits = 8 + bitCount[a>>8]
296 } 287 }
297 e.emitHuff(h, runLength<<4|int(nBits)) 288 e.emitHuff(h, runLength<<4|int(nBits))
298 if nBits > 0 { 289 if nBits > 0 {
299 e.emit(uint32(b)&(1<<nBits-1), nBits) 290 e.emit(uint32(b)&(1<<nBits-1), nBits)
300 } 291 }
301 } 292 }
302 293
303 // writeMarkerHeader writes the header for a maker with the given length. 294 // writeMarkerHeader writes the header for a marker with the given length.
r 2011/04/18 19:30:42 s/maker/marker/
nigeltao 2011/04/19 00:47:06 Done.
304 func (e *encoder) writeMarkerHeader(marker uint8, markerlen int) { 295 func (e *encoder) writeMarkerHeader(marker uint8, markerlen int) {
305 » e.writeByte(0xff) 296 » e.buf[0] = 0xff
306 » e.writeByte(marker) 297 » e.buf[1] = marker
307 » e.writeByte(uint8(markerlen >> 8)) 298 » e.buf[2] = uint8(markerlen >> 8)
308 » e.writeByte(uint8(markerlen & 0xff)) 299 » e.buf[3] = uint8(markerlen & 0xff)
r 2011/04/18 19:30:42 this is a fair bit of overhead. we could put a [4]
nigeltao 2011/04/19 00:47:06 Done.
300 » e.write(e.buf[:4])
309 } 301 }
310 302
311 // writeDQT writes the Define Quantization Table marker. 303 // writeDQT writes the Define Quantization Table marker.
312 func (e *encoder) writeDQT() { 304 func (e *encoder) writeDQT() {
313 markerlen := 2 305 markerlen := 2
314 for _, q := range e.quant { 306 for _, q := range e.quant {
315 markerlen += 1 + len(q) 307 markerlen += 1 + len(q)
316 } 308 }
317 e.writeMarkerHeader(dqtMarker, markerlen) 309 e.writeMarkerHeader(dqtMarker, markerlen)
318 for i, q := range e.quant { 310 for i, q := range e.quant {
319 e.writeByte(uint8(i)) 311 e.writeByte(uint8(i))
320 e.write(q[:]) 312 e.write(q[:])
321 } 313 }
322 } 314 }
323 315
324 // writeSOF0 writes the Start Of Frame (Baseline) marker. 316 // writeSOF0 writes the Start Of Frame (Baseline) marker.
325 func (e *encoder) writeSOF0(size image.Point) { 317 func (e *encoder) writeSOF0(size image.Point) {
326 markerlen := 8 + 3*nComponent 318 markerlen := 8 + 3*nComponent
327 e.writeMarkerHeader(sof0Marker, markerlen) 319 e.writeMarkerHeader(sof0Marker, markerlen)
328 » e.writeByte(8) // 8-bit color. 320 » e.buf[0] = 8 // 8-bit color.
329 » e.writeByte(uint8(size.Y >> 8)) 321 » e.buf[1] = uint8(size.Y >> 8)
330 » e.writeByte(uint8(size.Y & 0xff)) 322 » e.buf[2] = uint8(size.Y & 0xff)
331 » e.writeByte(uint8(size.X >> 8)) 323 » e.buf[3] = uint8(size.X >> 8)
332 » e.writeByte(uint8(size.X & 0xff)) 324 » e.buf[4] = uint8(size.X & 0xff)
r 2011/04/18 19:30:42 ditto.
nigeltao 2011/04/19 00:47:06 Done.
333 » e.writeByte(nComponent) 325 » e.buf[5] = nComponent
334 for i := 0; i < nComponent; i++ { 326 for i := 0; i < nComponent; i++ {
335 » » e.writeByte(uint8(i + 1)) 327 » » e.buf[3*i+6] = uint8(i + 1)
336 // We use 4:2:0 chroma subsampling. 328 // We use 4:2:0 chroma subsampling.
337 » » e.writeByte("\x22\x11\x11"[i]) 329 » » e.buf[3*i+7] = "\x22\x11\x11"[i]
338 » » e.writeByte("\x00\x01\x01"[i]) 330 » » e.buf[3*i+8] = "\x00\x01\x01"[i]
r 2011/04/18 19:30:42 ditto
nigeltao 2011/04/19 00:47:06 Done.
339 » } 331 » }
332 » e.write(e.buf[:3*(nComponent-1)+9])
340 } 333 }
341 334
342 // writeDHT writes the Define Huffman Table marker. 335 // writeDHT writes the Define Huffman Table marker.
343 func (e *encoder) writeDHT() { 336 func (e *encoder) writeDHT() {
344 markerlen := 2 337 markerlen := 2
345 for _, s := range theHuffmanSpec { 338 for _, s := range theHuffmanSpec {
346 markerlen += 1 + 16 + len(s.value) 339 markerlen += 1 + 16 + len(s.value)
347 } 340 }
348 e.writeMarkerHeader(dhtMarker, markerlen) 341 e.writeMarkerHeader(dhtMarker, markerlen)
349 for i, s := range theHuffmanSpec { 342 for i, s := range theHuffmanSpec {
350 e.writeByte("\x00\x10\x01\x11"[i]) 343 e.writeByte("\x00\x10\x01\x11"[i])
351 e.write(s.count[:]) 344 e.write(s.count[:])
352 e.write(s.value) 345 e.write(s.value)
r 2011/04/18 19:30:42 ditto
nigeltao 2011/04/19 00:47:06 I don't think this applies here; s.value is a slic
353 } 346 }
354 } 347 }
355 348
356 // writeBlock writes a block of pixel data using the given quantization table, 349 // writeBlock writes a block of pixel data using the given quantization table,
357 // returning the post-quantized DC value of the DCT-transformed block. 350 // returning the post-quantized DC value of the DCT-transformed block.
358 func (e *encoder) writeBlock(b *block, q quantIndex, prevDC int) int { 351 func (e *encoder) writeBlock(b *block, q quantIndex, prevDC int) int {
359 fdct(b) 352 fdct(b)
360 // Emit the DC delta. 353 // Emit the DC delta.
361 dc := div(b[0], (8 * int(e.quant[q][0]))) 354 dc := div(b[0], (8 * int(e.quant[q][0])))
362 e.emitHuffRLE(huffIndex(2*q+0), 0, dc-prevDC) 355 e.emitHuffRLE(huffIndex(2*q+0), 0, dc-prevDC)
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
406 for y := 0; y < 4; y++ { 399 for y := 0; y < 4; y++ {
407 for x := 0; x < 4; x++ { 400 for x := 0; x < 4; x++ {
408 j := 16*y + 2*x 401 j := 16*y + 2*x
409 sum := src[i][j] + src[i][j+1] + src[i][j+8] + s rc[i][j+9] 402 sum := src[i][j] + src[i][j+1] + src[i][j+8] + s rc[i][j+9]
410 dst[8*y+x+dstOff] = (sum + 2) >> 2 403 dst[8*y+x+dstOff] = (sum + 2) >> 2
411 } 404 }
412 } 405 }
413 } 406 }
414 } 407 }
415 408
409 // sosHeader is the SOS marker "\xff\xda" followed by 12 bytes:
410 // - the marker length "\x00\x0c",
411 // - the number of components "\x03",
412 // - component 1 uses DC table 0 and AC table 0 "\x01\x00",
413 // - component 2 uses DC table 1 and AC table 1 "\x02\x11",
414 // - component 3 uses DC table 1 and AC table 1 "\x03\x11",
415 // - padding "\x00\x00\x00".
416 var sosHeader = []byte{
417 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02,
418 0x11, 0x03, 0x11, 0x00, 0x00, 0x00,
419 }
420
416 // writeSOS writes the StartOfScan marker. 421 // writeSOS writes the StartOfScan marker.
417 func (e *encoder) writeSOS(m image.Image) { 422 func (e *encoder) writeSOS(m image.Image) {
418 » // Write the SOS marker "\xff\xda" followed by 12 bytes: 423 » e.write(sosHeader)
419 » //» - the marker length "\x00\x0c",
420 » //» - the number of components "\x03",
421 » //» - component 1 uses DC table 0 and AC table 0 "\x01\x00",
422 » //» - component 2 uses DC table 1 and AC table 1 "\x02\x11",
423 » //» - component 3 uses DC table 1 and AC table 1 "\x03\x11",
424 » //» - padding "\x00\x00\x00".
425 » e.writeString("\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00\x00\x00" )
r 2011/04/18 19:30:42 depending on the implementation of the writer, wri
nigeltao 2011/04/19 00:47:06 Done.
426 var ( 424 var (
427 // Scratch buffers to hold the YCbCr values. 425 // Scratch buffers to hold the YCbCr values.
428 yBlock block 426 yBlock block
429 cbBlock [4]block 427 cbBlock [4]block
430 crBlock [4]block 428 crBlock [4]block
431 cBlock block 429 cBlock block
r 2011/04/18 19:30:42 ok for now, but these are more unnecessary allocat
nigeltao 2011/04/19 00:47:06 There is only one SOS per image. Also, eventually,
432 // DC components are delta-encoded. 430 // DC components are delta-encoded.
433 » » lastY, lastCb, lastCr int 431 » » prevDCY, prevDCCb, prevDCCr int
434 ) 432 )
435 bounds := m.Bounds() 433 bounds := m.Bounds()
436 for y := bounds.Min.Y; y < bounds.Max.Y; y += 16 { 434 for y := bounds.Min.Y; y < bounds.Max.Y; y += 16 {
437 for x := bounds.Min.X; x < bounds.Max.X; x += 16 { 435 for x := bounds.Min.X; x < bounds.Max.X; x += 16 {
438 for i := 0; i < 4; i++ { 436 for i := 0; i < 4; i++ {
439 xOff := (i & 1) * 8 437 xOff := (i & 1) * 8
440 yOff := (i & 2) * 4 438 yOff := (i & 2) * 4
441 p := image.Point{x + xOff, y + yOff} 439 p := image.Point{x + xOff, y + yOff}
442 toYCbCr(m, p, &yBlock, &cbBlock[i], &crBlock[i]) 440 toYCbCr(m, p, &yBlock, &cbBlock[i], &crBlock[i])
443 » » » » lastY = e.writeBlock(&yBlock, 0, lastY) 441 » » » » prevDCY = e.writeBlock(&yBlock, 0, prevDCY)
444 } 442 }
445 scale(&cBlock, &cbBlock) 443 scale(&cBlock, &cbBlock)
446 » » » lastCb = e.writeBlock(&cBlock, 1, lastCb) 444 » » » prevDCCb = e.writeBlock(&cBlock, 1, prevDCCb)
447 scale(&cBlock, &crBlock) 445 scale(&cBlock, &crBlock)
448 » » » lastCr = e.writeBlock(&cBlock, 1, lastCr) 446 » » » prevDCCr = e.writeBlock(&cBlock, 1, prevDCCr)
449 } 447 }
450 } 448 }
451 // Pad the last byte with 1's. 449 // Pad the last byte with 1's.
452 e.emit(0x7f, 7) 450 e.emit(0x7f, 7)
453 } 451 }
454 452
455 // DefaultQuality is the default quality encoding parameter. 453 // DefaultQuality is the default quality encoding parameter.
456 const DefaultQuality = 75 454 const DefaultQuality = 75
457 455
458 // Options are the encoding parameters. 456 // Options are the encoding parameters.
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
498 x = (x*scale + 50) / 100 496 x = (x*scale + 50) / 100
499 if x < 1 { 497 if x < 1 {
500 x = 1 498 x = 1
501 } else if x > 255 { 499 } else if x > 255 {
502 x = 255 500 x = 255
503 } 501 }
504 e.quant[i][j] = uint8(x) 502 e.quant[i][j] = uint8(x)
505 } 503 }
506 } 504 }
507 // Write the Start Of Image marker. 505 // Write the Start Of Image marker.
508 » e.writeString("\xff\xd8") 506 » e.buf[0] = 0xff
507 » e.buf[1] = 0xd8
508 » e.write(e.buf[:2])
509 // Write the quantization tables. 509 // Write the quantization tables.
510 e.writeDQT() 510 e.writeDQT()
511 // Write the image dimensions. 511 // Write the image dimensions.
512 e.writeSOF0(b.Size()) 512 e.writeSOF0(b.Size())
513 // Write the Huffman tables. 513 // Write the Huffman tables.
514 e.writeDHT() 514 e.writeDHT()
515 // Write the image data. 515 // Write the image data.
516 e.writeSOS(m) 516 e.writeSOS(m)
517 // Write the End Of Image marker. 517 // Write the End Of Image marker.
518 » e.writeString("\xff\xd9") 518 » e.buf[0] = 0xff
519 » e.buf[1] = 0xd9
520 » e.write(e.buf[:2])
519 e.flush() 521 e.flush()
520 return e.err 522 return e.err
521 } 523 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b