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
|
||||
// value in the range [0x00, 0xff].
|
||||
//
|
||||
|
@ -136,8 +136,16 @@ func floatingAccumulate(dst []uint8, src []float32) {
|
|||
// instead of the maximal value 0xff.
|
||||
//
|
||||
// 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)
|
||||
for i, v := range src {
|
||||
acc += v
|
||||
|
@ -151,3 +159,22 @@ func floatingAccumulate(dst []uint8, src []float32) {
|
|||
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) {
|
||||
case *image.Alpha:
|
||||
// Fast path for glyph rendering.
|
||||
if srcA == 0xffff && z.DrawOp == draw.Src {
|
||||
z.rasterizeDstAlphaSrcOpaqueOpSrc(dst, r)
|
||||
if srcA == 0xffff {
|
||||
if z.DrawOp == draw.Over {
|
||||
z.rasterizeDstAlphaSrcOpaqueOpOver(dst, r)
|
||||
} else {
|
||||
z.rasterizeDstAlphaSrcOpaqueOpSrc(dst, r)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -221,7 +225,18 @@ func (z *Rasterizer) rasterizeDstAlphaSrcOpaqueOpSrc(dst *image.Alpha, r image.R
|
|||
// TODO: add a fixed point math implementation.
|
||||
// TODO: non-zero vs even-odd winding?
|
||||
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
|
||||
}
|
||||
println("TODO: the general case")
|
||||
|
|
|
@ -31,7 +31,7 @@ func encodePNG(dstFilename string, src image.Image) error {
|
|||
}
|
||||
|
||||
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, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xaa, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
@ -50,27 +50,46 @@ func TestBasicPath(t *testing.T) {
|
|||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
z := NewRasterizer(16, 16)
|
||||
z.MoveTo(f32.Vec2{2, 2})
|
||||
z.LineTo(f32.Vec2{8, 2})
|
||||
z.QuadTo(f32.Vec2{14, 2}, f32.Vec2{14, 14})
|
||||
z.CubeTo(f32.Vec2{8, 2}, f32.Vec2{5, 20}, f32.Vec2{2, 8})
|
||||
z.ClosePath()
|
||||
for _, background := range []uint8{0x00, 0x80} {
|
||||
for _, op := range []draw.Op{draw.Over, draw.Src} {
|
||||
z := NewRasterizer(16, 16)
|
||||
z.MoveTo(f32.Vec2{2, 2})
|
||||
z.LineTo(f32.Vec2{8, 2})
|
||||
z.QuadTo(f32.Vec2{14, 2}, f32.Vec2{14, 14})
|
||||
z.CubeTo(f32.Vec2{8, 2}, f32.Vec2{5, 20}, f32.Vec2{2, 8})
|
||||
z.ClosePath()
|
||||
|
||||
dst := image.NewAlpha(z.Bounds())
|
||||
z.DrawOp = draw.Src
|
||||
z.Draw(dst, dst.Bounds(), image.Opaque, image.Point{})
|
||||
dst := image.NewAlpha(z.Bounds())
|
||||
for i := range dst.Pix {
|
||||
dst.Pix[i] = background
|
||||
}
|
||||
z.DrawOp = op
|
||||
z.Draw(dst, dst.Bounds(), image.Opaque, image.Point{})
|
||||
got := dst.Pix
|
||||
|
||||
got := dst.Pix
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("len(got)=%d and len(want)=%d differ", len(got), len(want))
|
||||
}
|
||||
for i := range got {
|
||||
delta := int(got[i]) - int(want[i])
|
||||
// The +/- 2 allows different implementations to give different
|
||||
// rounding errors.
|
||||
if delta < -2 || +2 < delta {
|
||||
t.Errorf("i=%d: got %#02x, want %#02x", i, got[i], want[i])
|
||||
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) {
|
||||
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 {
|
||||
delta := int(got[i]) - int(want[i])
|
||||
// The +/- 2 allows different implementations to give different
|
||||
// rounding errors.
|
||||
if delta < -2 || +2 < delta {
|
||||
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