vector: implement DrawOp == draw.Over.
Some "TODO: the general case" lines remain. Change-Id: If66e484a00d5ea3fce9db37d4ee493739648daa3 Reviewed-on: https://go-review.googlesource.com/29495 Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
parent
bb355ba442
commit
cd8486aac9
|
@ -122,7 +122,7 @@ func (z *Rasterizer) floatingLineTo(b f32.Vec2) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func floatingAccumulate(dst []uint8, src []float32) {
|
const (
|
||||||
// almost256 scales a floating point value in the range [0, 1] to a uint8
|
// almost256 scales a floating point value in the range [0, 1] to a uint8
|
||||||
// value in the range [0x00, 0xff].
|
// value in the range [0x00, 0xff].
|
||||||
//
|
//
|
||||||
|
@ -136,8 +136,16 @@ func floatingAccumulate(dst []uint8, src []float32) {
|
||||||
// instead of the maximal value 0xff.
|
// instead of the maximal value 0xff.
|
||||||
//
|
//
|
||||||
// math.Float32bits(almost256) is 0x437fffff.
|
// math.Float32bits(almost256) is 0x437fffff.
|
||||||
const almost256 = 255.99998
|
almost256 = 255.99998
|
||||||
|
|
||||||
|
// almost65536 scales a floating point value in the range [0, 1] to a
|
||||||
|
// uint16 value in the range [0x0000, 0xffff].
|
||||||
|
//
|
||||||
|
// math.Float32bits(almost65536) is 0x477fffff.
|
||||||
|
almost65536 = almost256 * 256
|
||||||
|
)
|
||||||
|
|
||||||
|
func floatingAccumulateOpSrc(dst []uint8, src []float32) {
|
||||||
acc := float32(0)
|
acc := float32(0)
|
||||||
for i, v := range src {
|
for i, v := range src {
|
||||||
acc += v
|
acc += v
|
||||||
|
@ -151,3 +159,22 @@ func floatingAccumulate(dst []uint8, src []float32) {
|
||||||
dst[i] = uint8(almost256 * a)
|
dst[i] = uint8(almost256 * a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func floatingAccumulateOpOver(dst []uint8, src []float32) {
|
||||||
|
acc := float32(0)
|
||||||
|
for i, v := range src {
|
||||||
|
acc += v
|
||||||
|
a := acc
|
||||||
|
if a < 0 {
|
||||||
|
a = -a
|
||||||
|
}
|
||||||
|
if a > 1 {
|
||||||
|
a = 1
|
||||||
|
}
|
||||||
|
// This algorithm comes from the standard library's image/draw package.
|
||||||
|
dstA := uint32(dst[i]) * 0x101
|
||||||
|
maskA := uint32(almost65536 * a)
|
||||||
|
outA := dstA*(0xffff-maskA)/0xffff + maskA
|
||||||
|
dst[i] = uint8(outA >> 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -207,8 +207,12 @@ func (z *Rasterizer) Draw(dst draw.Image, r image.Rectangle, src image.Image, sp
|
||||||
switch dst := dst.(type) {
|
switch dst := dst.(type) {
|
||||||
case *image.Alpha:
|
case *image.Alpha:
|
||||||
// Fast path for glyph rendering.
|
// Fast path for glyph rendering.
|
||||||
if srcA == 0xffff && z.DrawOp == draw.Src {
|
if srcA == 0xffff {
|
||||||
|
if z.DrawOp == draw.Over {
|
||||||
|
z.rasterizeDstAlphaSrcOpaqueOpOver(dst, r)
|
||||||
|
} else {
|
||||||
z.rasterizeDstAlphaSrcOpaqueOpSrc(dst, r)
|
z.rasterizeDstAlphaSrcOpaqueOpSrc(dst, r)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,7 +225,18 @@ func (z *Rasterizer) rasterizeDstAlphaSrcOpaqueOpSrc(dst *image.Alpha, r image.R
|
||||||
// TODO: add a fixed point math implementation.
|
// TODO: add a fixed point math implementation.
|
||||||
// TODO: non-zero vs even-odd winding?
|
// TODO: non-zero vs even-odd winding?
|
||||||
if r == dst.Bounds() && r == z.Bounds() {
|
if r == dst.Bounds() && r == z.Bounds() {
|
||||||
floatingAccumulate(dst.Pix, z.area)
|
floatingAccumulateOpSrc(dst.Pix, z.area)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println("TODO: the general case")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Rasterizer) rasterizeDstAlphaSrcOpaqueOpOver(dst *image.Alpha, r image.Rectangle) {
|
||||||
|
// TODO: add SIMD implementations.
|
||||||
|
// TODO: add a fixed point math implementation.
|
||||||
|
// TODO: non-zero vs even-odd winding?
|
||||||
|
if r == dst.Bounds() && r == z.Bounds() {
|
||||||
|
floatingAccumulateOpOver(dst.Pix, z.area)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
println("TODO: the general case")
|
println("TODO: the general case")
|
||||||
|
|
|
@ -31,7 +31,7 @@ func encodePNG(dstFilename string, src image.Image) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicPath(t *testing.T) {
|
func TestBasicPath(t *testing.T) {
|
||||||
want := []byte{
|
mask := []byte{
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xaa, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xaa, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
@ -50,6 +50,8 @@ func TestBasicPath(t *testing.T) {
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, background := range []uint8{0x00, 0x80} {
|
||||||
|
for _, op := range []draw.Op{draw.Over, draw.Src} {
|
||||||
z := NewRasterizer(16, 16)
|
z := NewRasterizer(16, 16)
|
||||||
z.MoveTo(f32.Vec2{2, 2})
|
z.MoveTo(f32.Vec2{2, 2})
|
||||||
z.LineTo(f32.Vec2{8, 2})
|
z.LineTo(f32.Vec2{8, 2})
|
||||||
|
@ -58,19 +60,36 @@ func TestBasicPath(t *testing.T) {
|
||||||
z.ClosePath()
|
z.ClosePath()
|
||||||
|
|
||||||
dst := image.NewAlpha(z.Bounds())
|
dst := image.NewAlpha(z.Bounds())
|
||||||
z.DrawOp = draw.Src
|
for i := range dst.Pix {
|
||||||
|
dst.Pix[i] = background
|
||||||
|
}
|
||||||
|
z.DrawOp = op
|
||||||
z.Draw(dst, dst.Bounds(), image.Opaque, image.Point{})
|
z.Draw(dst, dst.Bounds(), image.Opaque, image.Point{})
|
||||||
|
|
||||||
got := dst.Pix
|
got := dst.Pix
|
||||||
|
|
||||||
|
want := make([]byte, len(mask))
|
||||||
|
if op == draw.Over && background == 0x80 {
|
||||||
|
for i, ma := range mask {
|
||||||
|
want[i] = 0xff - (0xff-ma)/2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copy(want, mask)
|
||||||
|
}
|
||||||
|
|
||||||
if len(got) != len(want) {
|
if len(got) != len(want) {
|
||||||
t.Fatalf("len(got)=%d and len(want)=%d differ", len(got), len(want))
|
t.Errorf("background=%#02x, op=%v: len(got)=%d and len(want)=%d differ",
|
||||||
|
background, op, len(got), len(want))
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
for i := range got {
|
for i := range got {
|
||||||
delta := int(got[i]) - int(want[i])
|
delta := int(got[i]) - int(want[i])
|
||||||
// The +/- 2 allows different implementations to give different
|
// The +/- 2 allows different implementations to give different
|
||||||
// rounding errors.
|
// rounding errors.
|
||||||
if delta < -2 || +2 < delta {
|
if delta < -2 || +2 < delta {
|
||||||
t.Errorf("i=%d: got %#02x, want %#02x", i, got[i], want[i])
|
t.Errorf("background=%#02x, op=%v: i=%d: got %#02x, want %#02x",
|
||||||
|
background, op, i, got[i], want[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user