diff --git a/testdata/blue-purple-pink.lossy.webp b/testdata/blue-purple-pink.lossy.webp new file mode 100644 index 0000000..d5143c0 Binary files /dev/null and b/testdata/blue-purple-pink.lossy.webp differ diff --git a/testdata/blue-purple-pink.lossy.webp.ycbcr.png b/testdata/blue-purple-pink.lossy.webp.ycbcr.png new file mode 100644 index 0000000..eb51560 Binary files /dev/null and b/testdata/blue-purple-pink.lossy.webp.ycbcr.png differ diff --git a/testdata/video-001.webp b/testdata/video-001.lossy.webp similarity index 100% rename from testdata/video-001.webp rename to testdata/video-001.lossy.webp diff --git a/testdata/video-001.webp.ycbcr.png b/testdata/video-001.lossy.webp.ycbcr.png similarity index 100% rename from testdata/video-001.webp.ycbcr.png rename to testdata/video-001.lossy.webp.ycbcr.png diff --git a/testdata/yellow_rose.lossy.webp.ycbcr.png b/testdata/yellow_rose.lossy.webp.ycbcr.png new file mode 100644 index 0000000..5e3bcd8 Binary files /dev/null and b/testdata/yellow_rose.lossy.webp.ycbcr.png differ diff --git a/vp8/decode.go b/vp8/decode.go index ab1115c..41daee6 100644 --- a/vp8/decode.go +++ b/vp8/decode.go @@ -368,7 +368,7 @@ func (d *Decoder) DecodeFrame() (*image.YCbCr, error) { if d.filterHeader.simple { d.simpleFilter() } else { - // TODO(nigeltao): normal filtering. + d.normalFilter() } } return d.img, nil diff --git a/vp8/filter.go b/vp8/filter.go index aa7e119..9a52451 100644 --- a/vp8/filter.go +++ b/vp8/filter.go @@ -4,14 +4,14 @@ package vp8 -// filter2 modifies a 2-pixel-wide or 2-pixel-high band along an edge. -func filter2(pix []byte, thresh int, index int, iStep int, jStep int) { - for i := 0; i < 16; i, index = i+1, index+iStep { +// filter2 modifies a 2-pixel wide or 2-pixel high band along an edge. +func filter2(pix []byte, level, index, iStep, jStep int) { + for n := 16; n > 0; n, index = n-1, index+iStep { p1 := int(pix[index-2*jStep]) p0 := int(pix[index-1*jStep]) q0 := int(pix[index+0*jStep]) q1 := int(pix[index+1*jStep]) - if int(lutAbs[lutAbsBase+p0-q0])<<1+int(lutAbs[lutAbsBase+p1-q1])>>1 > thresh { + if int(lutAbs[lutAbsBase+p0-q0])<<1+int(lutAbs[lutAbsBase+p1-q1])>>1 > level { continue } a := 3*(q0-p0) + int(lutClamp127[lutClamp127Base+p1-q1]) @@ -22,32 +22,126 @@ func filter2(pix []byte, thresh int, index int, iStep int, jStep int) { } } -// TODO(nigeltao): filter4 and filter6 functions, for normal filtering. +// filter246 modifies a 2-, 4- or 6-pixel wide or high band along an edge. +func filter246(pix []byte, n, level, ilevel, hlevel, index, iStep, jStep int, fourNotSix bool) { + for ; n > 0; n, index = n-1, index+iStep { + p3 := int(pix[index-4*jStep]) + p2 := int(pix[index-3*jStep]) + p1 := int(pix[index-2*jStep]) + p0 := int(pix[index-1*jStep]) + q0 := int(pix[index+0*jStep]) + q1 := int(pix[index+1*jStep]) + q2 := int(pix[index+2*jStep]) + q3 := int(pix[index+3*jStep]) + if int(lutAbs[lutAbsBase+p0-q0])<<1+int(lutAbs[lutAbsBase+p1-q1])>>1 > level { + continue + } + if int(lutAbs[lutAbsBase+p3-p2]) > ilevel || + int(lutAbs[lutAbsBase+p2-p1]) > ilevel || + int(lutAbs[lutAbsBase+p1-p0]) > ilevel || + int(lutAbs[lutAbsBase+q1-q0]) > ilevel || + int(lutAbs[lutAbsBase+q2-q1]) > ilevel || + int(lutAbs[lutAbsBase+q3-q2]) > ilevel { + continue + } + if int(lutAbs[lutAbsBase+p1-p0]) > hlevel || int(lutAbs[lutAbsBase+q1-q0]) > hlevel { + // Filter 2 pixels. + a := 3*(q0-p0) + int(lutClamp127[lutClamp127Base+p1-q1]) + a1 := int(lutClamp15[lutClamp15Base+((a+4)>>3)]) + a2 := int(lutClamp15[lutClamp15Base+((a+3)>>3)]) + pix[index-1*jStep] = lutClamp255[lutClamp255Base+p0+a2] + pix[index+0*jStep] = lutClamp255[lutClamp255Base+q0-a1] + } else if fourNotSix { + // Filter 4 pixels. + a := 3 * (q0 - p0) + a1 := int(lutClamp15[lutClamp15Base+((a+4)>>3)]) + a2 := int(lutClamp15[lutClamp15Base+((a+3)>>3)]) + a3 := (a1 + 1) >> 1 + pix[index-2*jStep] = lutClamp255[lutClamp255Base+p1+a3] + pix[index-1*jStep] = lutClamp255[lutClamp255Base+p0+a2] + pix[index+0*jStep] = lutClamp255[lutClamp255Base+q0-a1] + pix[index+1*jStep] = lutClamp255[lutClamp255Base+q1-a3] + } else { + // Filter 6 pixels. + a := 3*(q0-p0) + int(lutClamp127[lutClamp127Base+p1-q1]) + a = int(lutClamp127[lutClamp127Base+a]) + a1 := (27*a + 63) >> 7 + a2 := (18*a + 63) >> 7 + a3 := (9*a + 63) >> 7 + pix[index-3*jStep] = lutClamp255[lutClamp255Base+p2+a3] + pix[index-2*jStep] = lutClamp255[lutClamp255Base+p1+a2] + pix[index-1*jStep] = lutClamp255[lutClamp255Base+p0+a1] + pix[index+0*jStep] = lutClamp255[lutClamp255Base+q0-a1] + pix[index+1*jStep] = lutClamp255[lutClamp255Base+q1-a2] + pix[index+2*jStep] = lutClamp255[lutClamp255Base+q2-a3] + } + } +} // simpleFilter implements the simple filter, as specified in section 15.2. func (d *Decoder) simpleFilter() { for mby := 0; mby < d.mbh; mby++ { for mbx := 0; mbx < d.mbw; mbx++ { f := d.perMBFilterParams[d.mbw*mby+mbx] - if f.limit == 0 { + if f.level == 0 { continue } - index := (mby*d.img.YStride + mbx) * 16 + l := int(f.level) + yIndex := (mby*d.img.YStride + mbx) * 16 if mbx > 0 { - filter2(d.img.Y, int(f.limit)+4, index, d.img.YStride, 1) + filter2(d.img.Y, l+4, yIndex, d.img.YStride, 1) } if f.inner { - filter2(d.img.Y, int(f.limit), index+4, d.img.YStride, 1) - filter2(d.img.Y, int(f.limit), index+8, d.img.YStride, 1) - filter2(d.img.Y, int(f.limit), index+12, d.img.YStride, 1) + filter2(d.img.Y, l, yIndex+0x4, d.img.YStride, 1) + filter2(d.img.Y, l, yIndex+0x8, d.img.YStride, 1) + filter2(d.img.Y, l, yIndex+0xc, d.img.YStride, 1) } if mby > 0 { - filter2(d.img.Y, int(f.limit)+4, index, 1, d.img.YStride) + filter2(d.img.Y, l+4, yIndex, 1, d.img.YStride) } if f.inner { - filter2(d.img.Y, int(f.limit), index+d.img.YStride*4, 1, d.img.YStride) - filter2(d.img.Y, int(f.limit), index+d.img.YStride*8, 1, d.img.YStride) - filter2(d.img.Y, int(f.limit), index+d.img.YStride*12, 1, d.img.YStride) + filter2(d.img.Y, l, yIndex+d.img.YStride*0x4, 1, d.img.YStride) + filter2(d.img.Y, l, yIndex+d.img.YStride*0x8, 1, d.img.YStride) + filter2(d.img.Y, l, yIndex+d.img.YStride*0xc, 1, d.img.YStride) + } + } + } +} + +// normalFilter implements the normal filter, as specified in section 15.3. +func (d *Decoder) normalFilter() { + for mby := 0; mby < d.mbh; mby++ { + for mbx := 0; mbx < d.mbw; mbx++ { + f := d.perMBFilterParams[d.mbw*mby+mbx] + if f.level == 0 { + continue + } + l, il, hl := int(f.level), int(f.ilevel), int(f.hlevel) + yIndex := (mby*d.img.YStride + mbx) * 16 + cIndex := (mby*d.img.CStride + mbx) * 8 + if mbx > 0 { + filter246(d.img.Y, 16, l+4, il, hl, yIndex, d.img.YStride, 1, false) + filter246(d.img.Cb, 8, l+4, il, hl, cIndex, d.img.CStride, 1, false) + filter246(d.img.Cr, 8, l+4, il, hl, cIndex, d.img.CStride, 1, false) + } + if f.inner { + filter246(d.img.Y, 16, l, il, hl, yIndex+0x4, d.img.YStride, 1, true) + filter246(d.img.Y, 16, l, il, hl, yIndex+0x8, d.img.YStride, 1, true) + filter246(d.img.Y, 16, l, il, hl, yIndex+0xc, d.img.YStride, 1, true) + filter246(d.img.Cb, 8, l, il, hl, cIndex+0x4, d.img.CStride, 1, true) + filter246(d.img.Cr, 8, l, il, hl, cIndex+0x4, d.img.CStride, 1, true) + } + if mby > 0 { + filter246(d.img.Y, 16, l+4, il, hl, yIndex, 1, d.img.YStride, false) + filter246(d.img.Cb, 8, l+4, il, hl, cIndex, 1, d.img.CStride, false) + filter246(d.img.Cr, 8, l+4, il, hl, cIndex, 1, d.img.CStride, false) + } + if f.inner { + filter246(d.img.Y, 16, l, il, hl, yIndex+d.img.YStride*0x4, 1, d.img.YStride, true) + filter246(d.img.Y, 16, l, il, hl, yIndex+d.img.YStride*0x8, 1, d.img.YStride, true) + filter246(d.img.Y, 16, l, il, hl, yIndex+d.img.YStride*0xc, 1, d.img.YStride, true) + filter246(d.img.Cb, 8, l, il, hl, cIndex+d.img.CStride*0x4, 1, d.img.CStride, true) + filter246(d.img.Cr, 8, l, il, hl, cIndex+d.img.CStride*0x4, 1, d.img.CStride, true) } } } @@ -55,10 +149,14 @@ func (d *Decoder) simpleFilter() { // filterParam holds the loop filter parameters for a macroblock. type filterParam struct { - limit uint8 - inner bool - innerLevel uint8 - hevThresh uint8 + // The first three fields are thresholds used by the loop filter to smooth + // over the edges and interior of a macroblock. level is used by both the + // simple and normal filters. The inner level and high edge variance level + // are only used by the normal filter. + level, ilevel, hlevel uint8 + // inner is whether the inner loop filter cannot be optimized out as a + // no-op for this particular macroblock. + inner bool } // computeFilterParams computes the loop filter parameters, as specified in @@ -85,7 +183,7 @@ func (d *Decoder) computeFilterParams() { } } if level <= 0 { - p.limit = 0 + p.level = 0 continue } if level > 63 { @@ -105,25 +203,25 @@ func (d *Decoder) computeFilterParams() { if ilevel < 1 { ilevel = 1 } - p.innerLevel = uint8(ilevel) - p.limit = uint8(2*level + ilevel) + p.ilevel = uint8(ilevel) + p.level = uint8(2*level + ilevel) if d.frameHeader.KeyFrame { if level < 15 { - p.hevThresh = 0 + p.hlevel = 0 } else if level < 40 { - p.hevThresh = 1 + p.hlevel = 1 } else { - p.hevThresh = 2 + p.hlevel = 2 } } else { if level < 15 { - p.hevThresh = 0 + p.hlevel = 0 } else if level < 20 { - p.hevThresh = 1 + p.hlevel = 1 } else if level < 40 { - p.hevThresh = 2 + p.hlevel = 2 } else { - p.hevThresh = 3 + p.hlevel = 3 } } } @@ -243,10 +341,10 @@ var lutClamp15 = [255 + 1 + 255]int8{ +0x0f, +0x0f, +0x0f, +0x0f, +0x0f, +0x0f, +0x0f, } -const lutClamp127Base = 255 +const lutClamp127Base = 1020 -// lutClamp127[lutClamp127Base+x] is equal to clamp(x, -128, +127), for x in [-255, 255]. -var lutClamp127 = [255 + 1 + 255]int8{ +// lutClamp127[lutClamp127Base+x] is equal to clamp(x, -128, +127), for x in [-1020, 1020]. +var lutClamp127 = [1020 + 1 + 1020]int8{ -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, @@ -263,38 +361,134 @@ var lutClamp127 = [255 + 1 + 255]int8{ -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, - -0x7f, -0x7e, -0x7d, -0x7c, -0x7b, -0x7a, -0x79, -0x78, - -0x77, -0x76, -0x75, -0x74, -0x73, -0x72, -0x71, -0x70, - -0x6f, -0x6e, -0x6d, -0x6c, -0x6b, -0x6a, -0x69, -0x68, - -0x67, -0x66, -0x65, -0x64, -0x63, -0x62, -0x61, -0x60, - -0x5f, -0x5e, -0x5d, -0x5c, -0x5b, -0x5a, -0x59, -0x58, - -0x57, -0x56, -0x55, -0x54, -0x53, -0x52, -0x51, -0x50, - -0x4f, -0x4e, -0x4d, -0x4c, -0x4b, -0x4a, -0x49, -0x48, - -0x47, -0x46, -0x45, -0x44, -0x43, -0x42, -0x41, -0x40, - -0x3f, -0x3e, -0x3d, -0x3c, -0x3b, -0x3a, -0x39, -0x38, - -0x37, -0x36, -0x35, -0x34, -0x33, -0x32, -0x31, -0x30, - -0x2f, -0x2e, -0x2d, -0x2c, -0x2b, -0x2a, -0x29, -0x28, - -0x27, -0x26, -0x25, -0x24, -0x23, -0x22, -0x21, -0x20, - -0x1f, -0x1e, -0x1d, -0x1c, -0x1b, -0x1a, -0x19, -0x18, - -0x17, -0x16, -0x15, -0x14, -0x13, -0x12, -0x11, -0x10, - -0x0f, -0x0e, -0x0d, -0x0c, -0x0b, -0x0a, -0x09, -0x08, - -0x07, -0x06, -0x05, -0x04, -0x03, -0x02, -0x01, +0x00, - +0x01, +0x02, +0x03, +0x04, +0x05, +0x06, +0x07, +0x08, - +0x09, +0x0a, +0x0b, +0x0c, +0x0d, +0x0e, +0x0f, +0x10, - +0x11, +0x12, +0x13, +0x14, +0x15, +0x16, +0x17, +0x18, - +0x19, +0x1a, +0x1b, +0x1c, +0x1d, +0x1e, +0x1f, +0x20, - +0x21, +0x22, +0x23, +0x24, +0x25, +0x26, +0x27, +0x28, - +0x29, +0x2a, +0x2b, +0x2c, +0x2d, +0x2e, +0x2f, +0x30, - +0x31, +0x32, +0x33, +0x34, +0x35, +0x36, +0x37, +0x38, - +0x39, +0x3a, +0x3b, +0x3c, +0x3d, +0x3e, +0x3f, +0x40, - +0x41, +0x42, +0x43, +0x44, +0x45, +0x46, +0x47, +0x48, - +0x49, +0x4a, +0x4b, +0x4c, +0x4d, +0x4e, +0x4f, +0x50, - +0x51, +0x52, +0x53, +0x54, +0x55, +0x56, +0x57, +0x58, - +0x59, +0x5a, +0x5b, +0x5c, +0x5d, +0x5e, +0x5f, +0x60, - +0x61, +0x62, +0x63, +0x64, +0x65, +0x66, +0x67, +0x68, - +0x69, +0x6a, +0x6b, +0x6c, +0x6d, +0x6e, +0x6f, +0x70, - +0x71, +0x72, +0x73, +0x74, +0x75, +0x76, +0x77, +0x78, - +0x79, +0x7a, +0x7b, +0x7c, +0x7d, +0x7e, +0x7f, +0x7f, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, -0x80, + -0x80, -0x80, -0x80, -0x80, -0x80, -0x7f, -0x7e, -0x7d, + -0x7c, -0x7b, -0x7a, -0x79, -0x78, -0x77, -0x76, -0x75, + -0x74, -0x73, -0x72, -0x71, -0x70, -0x6f, -0x6e, -0x6d, + -0x6c, -0x6b, -0x6a, -0x69, -0x68, -0x67, -0x66, -0x65, + -0x64, -0x63, -0x62, -0x61, -0x60, -0x5f, -0x5e, -0x5d, + -0x5c, -0x5b, -0x5a, -0x59, -0x58, -0x57, -0x56, -0x55, + -0x54, -0x53, -0x52, -0x51, -0x50, -0x4f, -0x4e, -0x4d, + -0x4c, -0x4b, -0x4a, -0x49, -0x48, -0x47, -0x46, -0x45, + -0x44, -0x43, -0x42, -0x41, -0x40, -0x3f, -0x3e, -0x3d, + -0x3c, -0x3b, -0x3a, -0x39, -0x38, -0x37, -0x36, -0x35, + -0x34, -0x33, -0x32, -0x31, -0x30, -0x2f, -0x2e, -0x2d, + -0x2c, -0x2b, -0x2a, -0x29, -0x28, -0x27, -0x26, -0x25, + -0x24, -0x23, -0x22, -0x21, -0x20, -0x1f, -0x1e, -0x1d, + -0x1c, -0x1b, -0x1a, -0x19, -0x18, -0x17, -0x16, -0x15, + -0x14, -0x13, -0x12, -0x11, -0x10, -0x0f, -0x0e, -0x0d, + -0x0c, -0x0b, -0x0a, -0x09, -0x08, -0x07, -0x06, -0x05, + -0x04, -0x03, -0x02, -0x01, +0x00, +0x01, +0x02, +0x03, + +0x04, +0x05, +0x06, +0x07, +0x08, +0x09, +0x0a, +0x0b, + +0x0c, +0x0d, +0x0e, +0x0f, +0x10, +0x11, +0x12, +0x13, + +0x14, +0x15, +0x16, +0x17, +0x18, +0x19, +0x1a, +0x1b, + +0x1c, +0x1d, +0x1e, +0x1f, +0x20, +0x21, +0x22, +0x23, + +0x24, +0x25, +0x26, +0x27, +0x28, +0x29, +0x2a, +0x2b, + +0x2c, +0x2d, +0x2e, +0x2f, +0x30, +0x31, +0x32, +0x33, + +0x34, +0x35, +0x36, +0x37, +0x38, +0x39, +0x3a, +0x3b, + +0x3c, +0x3d, +0x3e, +0x3f, +0x40, +0x41, +0x42, +0x43, + +0x44, +0x45, +0x46, +0x47, +0x48, +0x49, +0x4a, +0x4b, + +0x4c, +0x4d, +0x4e, +0x4f, +0x50, +0x51, +0x52, +0x53, + +0x54, +0x55, +0x56, +0x57, +0x58, +0x59, +0x5a, +0x5b, + +0x5c, +0x5d, +0x5e, +0x5f, +0x60, +0x61, +0x62, +0x63, + +0x64, +0x65, +0x66, +0x67, +0x68, +0x69, +0x6a, +0x6b, + +0x6c, +0x6d, +0x6e, +0x6f, +0x70, +0x71, +0x72, +0x73, + +0x74, +0x75, +0x76, +0x77, +0x78, +0x79, +0x7a, +0x7b, + +0x7c, +0x7d, +0x7e, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, @@ -310,7 +504,103 @@ var lutClamp127 = [255 + 1 + 255]int8{ +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, - +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, +0x7f, + +0x7f, } const lutClamp255Base = 255 diff --git a/webp/decode_test.go b/webp/decode_test.go index 37d6913..635c5d0 100644 --- a/webp/decode_test.go +++ b/webp/decode_test.go @@ -31,82 +31,85 @@ func hex(x []byte) string { } func TestDecodeVP8(t *testing.T) { - // The original video-001.png image is 150x103. - const w, h = 150, 103 - // w2 and h2 are the half-width and half-height, rounded up. - const w2, h2 = int((w + 1) / 2), int((h + 1) / 2) - - f0, err := os.Open("../testdata/video-001.webp.ycbcr.png") - if err != nil { - t.Fatal(err) - } - defer f0.Close() - img0, err := png.Decode(f0) - if err != nil { - t.Fatal(err) + testCases := []string{ + "blue-purple-pink", + "video-001", + "yellow_rose", } - // The split-into-YCbCr-planes golden image is a 2*w2 wide and h+h2 high - // gray image arranged in IMC4 format: - // YYYY - // YYYY - // BBRR - // See http://www.fourcc.org/yuv.php#IMC4 - if got, want := img0.Bounds(), image.Rect(0, 0, 2*w2, h+h2); got != want { - t.Fatalf("bounds0: got %v, want %v", got, want) - } - m0, ok := img0.(*image.Gray) - if !ok { - t.Fatal("decoded PNG image is not a Gray") - } + for _, tc := range testCases { + f0, err := os.Open("../testdata/" + tc + ".lossy.webp") + if err != nil { + t.Fatal(err) + } + defer f0.Close() + img0, err := Decode(f0) + if err != nil { + t.Fatal(err) + } - f1, err := os.Open("../testdata/video-001.webp") - if err != nil { - t.Fatal(err) - } - defer f1.Close() - img1, err := Decode(f1) - if err != nil { - t.Fatal(err) - } + m0, ok := img0.(*image.YCbCr) + if !ok || m0.SubsampleRatio != image.YCbCrSubsampleRatio420 { + t.Fatal("decoded WEBP image is not a 4:2:0 YCbCr") + } + // w2 and h2 are the half-width and half-height, rounded up. + w, h := m0.Bounds().Dx(), m0.Bounds().Dy() + w2, h2 := int((w+1)/2), int((h+1)/2) - if got, want := img1.Bounds(), image.Rect(0, 0, w, h); got != want { - t.Fatalf("bounds1: got %v, want %v", got, want) - } - m1, ok := img1.(*image.YCbCr) - if !ok || m1.SubsampleRatio != image.YCbCrSubsampleRatio420 { - t.Fatal("decoded WEBP image is not a 4:2:0 YCbCr") - } + f1, err := os.Open("../testdata/" + tc + ".lossy.webp.ycbcr.png") + if err != nil { + t.Fatal(err) + } + defer f1.Close() + img1, err := png.Decode(f1) + if err != nil { + t.Fatal(err) + } - planes := []struct { - name string - m1Pix []uint8 - m1Stride int - m0Rect image.Rectangle - }{ - {"Y", m1.Y, m1.YStride, image.Rect(0, 0, w, h)}, - {"Cb", m1.Cb, m1.CStride, image.Rect(0*w2, h, 1*w2, h+h2)}, - {"Cr", m1.Cr, m1.CStride, image.Rect(1*w2, h, 2*w2, h+h2)}, - } - for _, plane := range planes { - dx := plane.m0Rect.Dx() - nDiff, diff := 0, make([]byte, dx) - for j, y := 0, plane.m0Rect.Min.Y; y < plane.m0Rect.Max.Y; j, y = j+1, y+1 { - got := plane.m1Pix[j*plane.m1Stride:][:dx] - want := m0.Pix[y*m0.Stride+plane.m0Rect.Min.X:][:dx] - if bytes.Equal(got, want) { - continue + // The split-into-YCbCr-planes golden image is a 2*w2 wide and h+h2 high + // gray image arranged in IMC4 format: + // YYYY + // YYYY + // BBRR + // See http://www.fourcc.org/yuv.php#IMC4 + if got, want := img1.Bounds(), image.Rect(0, 0, 2*w2, h+h2); got != want { + t.Fatalf("bounds0: got %v, want %v", got, want) + } + m1, ok := img1.(*image.Gray) + if !ok { + t.Fatal("decoded PNG image is not a Gray") + } + + planes := []struct { + name string + m0Pix []uint8 + m0Stride int + m1Rect image.Rectangle + }{ + {"Y", m0.Y, m0.YStride, image.Rect(0, 0, w, h)}, + {"Cb", m0.Cb, m0.CStride, image.Rect(0*w2, h, 1*w2, h+h2)}, + {"Cr", m0.Cr, m0.CStride, image.Rect(1*w2, h, 2*w2, h+h2)}, + } + for _, plane := range planes { + dx := plane.m1Rect.Dx() + nDiff, diff := 0, make([]byte, dx) + for j, y := 0, plane.m1Rect.Min.Y; y < plane.m1Rect.Max.Y; j, y = j+1, y+1 { + got := plane.m0Pix[j*plane.m0Stride:][:dx] + want := m1.Pix[y*m1.Stride+plane.m1Rect.Min.X:][:dx] + if bytes.Equal(got, want) { + continue + } + nDiff++ + if nDiff > 10 { + t.Errorf("%s plane: more rows differ", plane.name) + break + } + for i := range got { + diff[i] = got[i] - want[i] + } + t.Errorf("%s plane: m0 row %d, m1 row %d\ngot %s\nwant%s\ndiff%s", + plane.name, j, y, hex(got), hex(want), hex(diff)) } - nDiff++ - if nDiff > 10 { - t.Errorf("%s plane: more rows differ", plane.name) - break - } - for i := range got { - diff[i] = got[i] - want[i] - } - t.Errorf("%s plane: m0 row %d, m1 row %d\ngot %s\nwant%s\ndiff%s", - plane.name, y, j, hex(got), hex(want), hex(diff)) } } } @@ -210,5 +213,6 @@ func benchmarkDecode(b *testing.B, filename string) { } } -func BenchmarkDecodeVP8(b *testing.B) { benchmarkDecode(b, "yellow_rose.lossy") } -func BenchmarkDecodeVP8L(b *testing.B) { benchmarkDecode(b, "yellow_rose.lossless") } +func BenchmarkDecodeVP8SimpleFilter(b *testing.B) { benchmarkDecode(b, "blue-purple-pink.lossy") } +func BenchmarkDecodeVP8NormalFilter(b *testing.B) { benchmarkDecode(b, "yellow_rose.lossy") } +func BenchmarkDecodeVP8L(b *testing.B) { benchmarkDecode(b, "yellow_rose.lossless") }