diff --git a/src/image/decode_test.go b/src/image/decode_test.go index d16ef8a1a4..85e235e729 100644 --- a/src/image/decode_test.go +++ b/src/image/decode_test.go @@ -36,6 +36,7 @@ var imageTests = []imageTest{ {"testdata/video-001.221212.png", "testdata/video-001.221212.jpeg", 8 << 8}, {"testdata/video-001.cmyk.png", "testdata/video-001.cmyk.jpeg", 8 << 8}, {"testdata/video-001.rgb.png", "testdata/video-001.rgb.jpeg", 8 << 8}, + {"testdata/video-001.progressive.truncated.png", "testdata/video-001.progressive.truncated.jpeg", 8 << 8}, // Grayscale images. {"testdata/video-005.gray.png", "testdata/video-005.gray.jpeg", 8 << 8}, {"testdata/video-005.gray.png", "testdata/video-005.gray.png", 0}, diff --git a/src/image/jpeg/reader.go b/src/image/jpeg/reader.go index adf97abbd1..c5834219a3 100644 --- a/src/image/jpeg/reader.go +++ b/src/image/jpeg/reader.go @@ -641,6 +641,12 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) { return nil, err } } + + if d.progressive { + if err := d.reconstructProgressiveImage(); err != nil { + return nil, err + } + } if d.img1 != nil { return d.img1, nil } diff --git a/src/image/jpeg/scan.go b/src/image/jpeg/scan.go index 99734c01af..e1104d27c2 100644 --- a/src/image/jpeg/scan.go +++ b/src/image/jpeg/scan.go @@ -173,7 +173,6 @@ func (d *decoder) processSOS(n int) error { compIndex := scan[i].compIndex hi := d.comp[compIndex].h vi := d.comp[compIndex].v - qt := &d.quant[d.comp[compIndex].tq] for j := 0; j < hi*vi; j++ { // The blocks are traversed one MCU at a time. For 4:2:0 chroma // subsampling, there are four Y 8x8 blocks in every 16x16 MCU. @@ -286,55 +285,19 @@ func (d *decoder) processSOS(n int) error { } if d.progressive { - if zigEnd != blockSize-1 || al != 0 { - // We haven't completely decoded this 8x8 block. Save the coefficients. - d.progCoeffs[compIndex][by*mxx*hi+bx] = b - // At this point, we could execute the rest of the loop body to dequantize and - // perform the inverse DCT, to save early stages of a progressive image to the - // *image.YCbCr buffers (the whole point of progressive encoding), but in Go, - // the jpeg.Decode function does not return until the entire image is decoded, - // so we "continue" here to avoid wasted computation. - continue - } + // Save the coefficients. + d.progCoeffs[compIndex][by*mxx*hi+bx] = b + // At this point, we could call reconstructBlock to dequantize and perform the + // inverse DCT, to save early stages of a progressive image to the *image.YCbCr + // buffers (the whole point of progressive encoding), but in Go, the jpeg.Decode + // function does not return until the entire image is decoded, so we "continue" + // here to avoid wasted computation. Instead, reconstructBlock is called on each + // accumulated block by the reconstructProgressiveImage method after all of the + // SOS markers are processed. + continue } - - // Dequantize, perform the inverse DCT and store the block to the image. - for zig := 0; zig < blockSize; zig++ { - b[unzig[zig]] *= qt[zig] - } - idct(&b) - dst, stride := []byte(nil), 0 - if d.nComp == 1 { - dst, stride = d.img1.Pix[8*(by*d.img1.Stride+bx):], d.img1.Stride - } else { - switch compIndex { - case 0: - dst, stride = d.img3.Y[8*(by*d.img3.YStride+bx):], d.img3.YStride - case 1: - dst, stride = d.img3.Cb[8*(by*d.img3.CStride+bx):], d.img3.CStride - case 2: - dst, stride = d.img3.Cr[8*(by*d.img3.CStride+bx):], d.img3.CStride - case 3: - dst, stride = d.blackPix[8*(by*d.blackStride+bx):], d.blackStride - default: - return UnsupportedError("too many components") - } - } - // Level shift by +128, clip to [0, 255], and write to dst. - for y := 0; y < 8; y++ { - y8 := y * 8 - yStride := y * stride - for x := 0; x < 8; x++ { - c := b[y8+x] - if c < -128 { - c = 0 - } else if c > 127 { - c = 255 - } else { - c += 128 - } - dst[yStride+x] = uint8(c) - } + if err := d.reconstructBlock(&b, bx, by, int(compIndex)); err != nil { + return err } } // for j } // for i @@ -470,3 +433,70 @@ func (d *decoder) refineNonZeroes(b *block, zig, zigEnd, nz, delta int32) (int32 } return zig, nil } + +func (d *decoder) reconstructProgressiveImage() error { + // The h0, mxx, by and bx variables have the same meaning as in the + // processSOS method. + h0 := d.comp[0].h + mxx := (d.width + 8*h0 - 1) / (8 * h0) + for i := 0; i < d.nComp; i++ { + if d.progCoeffs[i] == nil { + continue + } + v := 8 * d.comp[0].v / d.comp[i].v + h := 8 * d.comp[0].h / d.comp[i].h + stride := mxx * d.comp[i].h + for by := 0; by*v < d.height; by++ { + for bx := 0; bx*h < d.width; bx++ { + if err := d.reconstructBlock(&d.progCoeffs[i][by*stride+bx], bx, by, i); err != nil { + return err + } + } + } + } + return nil +} + +// reconstructBlock dequantizes, performs the inverse DCT and stores the block +// to the image. +func (d *decoder) reconstructBlock(b *block, bx, by, compIndex int) error { + qt := &d.quant[d.comp[compIndex].tq] + for zig := 0; zig < blockSize; zig++ { + b[unzig[zig]] *= qt[zig] + } + idct(b) + dst, stride := []byte(nil), 0 + if d.nComp == 1 { + dst, stride = d.img1.Pix[8*(by*d.img1.Stride+bx):], d.img1.Stride + } else { + switch compIndex { + case 0: + dst, stride = d.img3.Y[8*(by*d.img3.YStride+bx):], d.img3.YStride + case 1: + dst, stride = d.img3.Cb[8*(by*d.img3.CStride+bx):], d.img3.CStride + case 2: + dst, stride = d.img3.Cr[8*(by*d.img3.CStride+bx):], d.img3.CStride + case 3: + dst, stride = d.blackPix[8*(by*d.blackStride+bx):], d.blackStride + default: + return UnsupportedError("too many components") + } + } + // Level shift by +128, clip to [0, 255], and write to dst. + for y := 0; y < 8; y++ { + y8 := y * 8 + yStride := y * stride + for x := 0; x < 8; x++ { + c := b[y8+x] + if c < -128 { + c = 0 + } else if c > 127 { + c = 255 + } else { + c += 128 + } + dst[yStride+x] = uint8(c) + } + } + return nil +} diff --git a/src/image/testdata/video-001.progressive.truncated.jpeg b/src/image/testdata/video-001.progressive.truncated.jpeg new file mode 100644 index 0000000000..b5be8bc763 Binary files /dev/null and b/src/image/testdata/video-001.progressive.truncated.jpeg differ diff --git a/src/image/testdata/video-001.progressive.truncated.png b/src/image/testdata/video-001.progressive.truncated.png new file mode 100644 index 0000000000..baf1981226 Binary files /dev/null and b/src/image/testdata/video-001.progressive.truncated.png differ