f8a86727c2
The benchmarks are a little slower, but the code is shorter. benchmark old ns/op new ns/op delta BenchmarkDecodeVP8SimpleFilter 621950 644184 +3.57% BenchmarkDecodeVP8NormalFilter 4045745 4269431 +5.53% LGTM=r R=r, minux, ruiu CC=golang-codereviews https://golang.org/cl/108140045
266 lines
7.3 KiB
Go
266 lines
7.3 KiB
Go
// Copyright 2014 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 vp8
|
|
|
|
// 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 abs(p0-q0)<<1+abs(p1-q1)>>1 > level {
|
|
continue
|
|
}
|
|
a := 3*(q0-p0) + clamp127(p1-q1)
|
|
a1 := clamp15((a + 4) >> 3)
|
|
a2 := clamp15((a + 3) >> 3)
|
|
pix[index-1*jStep] = clamp255(p0 + a2)
|
|
pix[index+0*jStep] = clamp255(q0 - a1)
|
|
}
|
|
}
|
|
|
|
// 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 abs(p0-q0)<<1+abs(p1-q1)>>1 > level {
|
|
continue
|
|
}
|
|
if abs(p3-p2) > ilevel ||
|
|
abs(p2-p1) > ilevel ||
|
|
abs(p1-p0) > ilevel ||
|
|
abs(q1-q0) > ilevel ||
|
|
abs(q2-q1) > ilevel ||
|
|
abs(q3-q2) > ilevel {
|
|
continue
|
|
}
|
|
if abs(p1-p0) > hlevel || abs(q1-q0) > hlevel {
|
|
// Filter 2 pixels.
|
|
a := 3*(q0-p0) + clamp127(p1-q1)
|
|
a1 := clamp15((a + 4) >> 3)
|
|
a2 := clamp15((a + 3) >> 3)
|
|
pix[index-1*jStep] = clamp255(p0 + a2)
|
|
pix[index+0*jStep] = clamp255(q0 - a1)
|
|
} else if fourNotSix {
|
|
// Filter 4 pixels.
|
|
a := 3 * (q0 - p0)
|
|
a1 := clamp15((a + 4) >> 3)
|
|
a2 := clamp15((a + 3) >> 3)
|
|
a3 := (a1 + 1) >> 1
|
|
pix[index-2*jStep] = clamp255(p1 + a3)
|
|
pix[index-1*jStep] = clamp255(p0 + a2)
|
|
pix[index+0*jStep] = clamp255(q0 - a1)
|
|
pix[index+1*jStep] = clamp255(q1 - a3)
|
|
} else {
|
|
// Filter 6 pixels.
|
|
a := clamp127(3*(q0-p0) + clamp127(p1-q1))
|
|
a1 := (27*a + 63) >> 7
|
|
a2 := (18*a + 63) >> 7
|
|
a3 := (9*a + 63) >> 7
|
|
pix[index-3*jStep] = clamp255(p2 + a3)
|
|
pix[index-2*jStep] = clamp255(p1 + a2)
|
|
pix[index-1*jStep] = clamp255(p0 + a1)
|
|
pix[index+0*jStep] = clamp255(q0 - a1)
|
|
pix[index+1*jStep] = clamp255(q1 - a2)
|
|
pix[index+2*jStep] = clamp255(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.level == 0 {
|
|
continue
|
|
}
|
|
l := int(f.level)
|
|
yIndex := (mby*d.img.YStride + mbx) * 16
|
|
if mbx > 0 {
|
|
filter2(d.img.Y, l+4, yIndex, d.img.YStride, 1)
|
|
}
|
|
if f.inner {
|
|
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, l+4, yIndex, 1, d.img.YStride)
|
|
}
|
|
if f.inner {
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// filterParam holds the loop filter parameters for a macroblock.
|
|
type filterParam struct {
|
|
// 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
|
|
// section 15.4.
|
|
func (d *Decoder) computeFilterParams() {
|
|
for i := range d.filterParams {
|
|
baseLevel := d.filterHeader.level
|
|
if d.segmentHeader.useSegment {
|
|
baseLevel = d.segmentHeader.filterStrength[i]
|
|
if d.segmentHeader.relativeDelta {
|
|
baseLevel += d.filterHeader.level
|
|
}
|
|
}
|
|
|
|
for j := range d.filterParams[i] {
|
|
p := &d.filterParams[i][j]
|
|
p.inner = j != 0
|
|
level := baseLevel
|
|
if d.filterHeader.useLFDelta {
|
|
// The libwebp C code has a "TODO: only CURRENT is handled for now."
|
|
level += d.filterHeader.refLFDelta[0]
|
|
if j != 0 {
|
|
level += d.filterHeader.modeLFDelta[0]
|
|
}
|
|
}
|
|
if level <= 0 {
|
|
p.level = 0
|
|
continue
|
|
}
|
|
if level > 63 {
|
|
level = 63
|
|
}
|
|
ilevel := level
|
|
if d.filterHeader.sharpness > 0 {
|
|
if d.filterHeader.sharpness > 4 {
|
|
ilevel >>= 2
|
|
} else {
|
|
ilevel >>= 1
|
|
}
|
|
if x := int8(9 - d.filterHeader.sharpness); ilevel > x {
|
|
ilevel = x
|
|
}
|
|
}
|
|
if ilevel < 1 {
|
|
ilevel = 1
|
|
}
|
|
p.ilevel = uint8(ilevel)
|
|
p.level = uint8(2*level + ilevel)
|
|
if d.frameHeader.KeyFrame {
|
|
if level < 15 {
|
|
p.hlevel = 0
|
|
} else if level < 40 {
|
|
p.hlevel = 1
|
|
} else {
|
|
p.hlevel = 2
|
|
}
|
|
} else {
|
|
if level < 15 {
|
|
p.hlevel = 0
|
|
} else if level < 20 {
|
|
p.hlevel = 1
|
|
} else if level < 40 {
|
|
p.hlevel = 2
|
|
} else {
|
|
p.hlevel = 3
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func abs(x int) int {
|
|
if x < 0 {
|
|
return -x
|
|
}
|
|
return x
|
|
}
|
|
|
|
func clamp15(x int) int {
|
|
if x < -16 {
|
|
return -16
|
|
}
|
|
if x > 15 {
|
|
return 15
|
|
}
|
|
return x
|
|
}
|
|
|
|
func clamp127(x int) int {
|
|
if x < -128 {
|
|
return -128
|
|
}
|
|
if x > 127 {
|
|
return 127
|
|
}
|
|
return x
|
|
}
|
|
|
|
func clamp255(x int) uint8 {
|
|
if x < 0 {
|
|
return 0
|
|
}
|
|
if x > 255 {
|
|
return 255
|
|
}
|
|
return uint8(x)
|
|
}
|