}
}
}
+ drawRGBA(dst0, r, src, sp, mask, mp, op)
+ return
}
x0, x1, dx := r.Min.X, r.Max.X, 1
cb >>= 16
ca >>= 16
for y, my := y0, mp.Y; y != y1; y, my = y+1, my+1 {
+ p := dst.Pixel[y]
for x, mx := x0, mp.X; x != x1; x, mx = x+1, mx+1 {
ma := uint32(mask.Pixel[my][mx].A)
if ma == 0 {
continue
}
ma |= ma << 8
- rgba := dst.Pixel[y][x]
+ rgba := p[x]
dr := uint32(rgba.R)
dg := uint32(rgba.G)
db := uint32(rgba.B)
da := uint32(rgba.A)
- // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255].
- // We work in 16-bit color, and so would normally do:
- // dr |= dr << 8
- // and similarly for dg, db and da, but instead we multiply a
- // (which is a 16-bit color, ranging in [0,65535]) by 0x101.
- // This yields the same result, but is fewer arithmetic operations.
const M = 1<<16 - 1
- a := M - (ca * ma / M)
- a *= 0x101
+ // The 0x101 is here for the same reason as in drawRGBA.
+ a := (M - (ca * ma / M)) * 0x101
dr = (dr*a + cr*ma) / M
dg = (dg*a + cg*ma) / M
db = (db*a + cb*ma) / M
da = (da*a + ca*ma) / M
- dst.Pixel[y][x] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
+ p[x] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
}
}
}
}
}
+func drawRGBA(dst *image.RGBA, r Rectangle, src image.Image, sp Point, mask image.Image, mp Point, op Op) {
+ x0, x1, dx := r.Min.X, r.Max.X, 1
+ y0, y1, dy := r.Min.Y, r.Max.Y, 1
+ if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) {
+ if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X {
+ x0, x1, dx = x1-1, x0-1, -1
+ y0, y1, dy = y1-1, y0-1, -1
+ }
+ }
+
+ sy := sp.Y + y0 - r.Min.Y
+ my := mp.Y + y0 - r.Min.Y
+ for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
+ sx := sp.X + x0 - r.Min.X
+ mx := mp.X + x0 - r.Min.X
+ p := dst.Pixel[y]
+ for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx {
+ const M = 1<<16 - 1
+ ma := uint32(M)
+ if mask != nil {
+ _, _, _, ma = mask.At(mx, my).RGBA()
+ ma >>= 16
+ }
+ sr, sg, sb, sa := src.At(sx, sy).RGBA()
+ sr >>= 16
+ sg >>= 16
+ sb >>= 16
+ sa >>= 16
+ var dr, dg, db, da uint32
+ if op == Over {
+ rgba := p[x]
+ dr = uint32(rgba.R)
+ dg = uint32(rgba.G)
+ db = uint32(rgba.B)
+ da = uint32(rgba.A)
+ // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255].
+ // We work in 16-bit color, and so would normally do:
+ // dr |= dr << 8
+ // and similarly for dg, db and da, but instead we multiply a
+ // (which is a 16-bit color, ranging in [0,65535]) by 0x101.
+ // This yields the same result, but is fewer arithmetic operations.
+ a := (M - (sa * ma / M)) * 0x101
+ dr = (dr*a + sr*ma) / M
+ dg = (dg*a + sg*ma) / M
+ db = (db*a + sb*ma) / M
+ da = (da*a + sa*ma) / M
+ } else {
+ dr = sr * ma / M
+ dg = sg * ma / M
+ db = sb * ma / M
+ da = sa * ma / M
+ }
+ p[x] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
+ }
+ }
+}
+
// Border aligns r.Min in dst with sp in src and then replaces pixels
// in a w-pixel border around r in dst with the result of the Porter-Duff compositing
// operation ``src over dst.'' If w is positive, the border extends w pixels inside r.
drawTest{"genericSrc", fillBlue(255), vgradAlpha(192), Src, image.RGBAColor{0, 0, 102, 102}},
}
+func makeGolden(dst image.Image, t drawTest) image.Image {
+ // Since golden is a newly allocated image, we don't have to check if the
+ // input source and mask images and the output golden image overlap.
+ golden := image.NewRGBA(dst.Width(), dst.Height())
+ for y := 0; y < golden.Height(); y++ {
+ my, sy := y, y
+ for x := 0; x < golden.Width(); x++ {
+ mx, sx := x, x
+ const M = 1<<16 - 1
+ var dr, dg, db, da uint32
+ if t.op == Over {
+ dr, dg, db, da = dst.At(x, y).RGBA()
+ dr >>= 16
+ dg >>= 16
+ db >>= 16
+ da >>= 16
+ }
+ sr, sg, sb, sa := t.src.At(sx, sy).RGBA()
+ sr >>= 16
+ sg >>= 16
+ sb >>= 16
+ sa >>= 16
+ ma := uint32(M)
+ if t.mask != nil {
+ _, _, _, ma = t.mask.At(mx, my).RGBA()
+ ma >>= 16
+ }
+ a := M - (sa * ma / M)
+ golden.Set(x, y, image.RGBA64Color{
+ uint16((dr*a + sr*ma) / M),
+ uint16((dg*a + sg*ma) / M),
+ uint16((db*a + sb*ma) / M),
+ uint16((da*a + sa*ma) / M),
+ })
+ }
+ }
+ return golden
+}
+
func TestDraw(t *testing.T) {
- for _, test := range drawTests {
+loop: for _, test := range drawTests {
dst := hgradRed(255)
- DrawMask(dst, Rect(0, 0, 16, 16), test.src, ZP, test.mask, ZP, test.op)
+ // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
+ golden := makeGolden(dst, test)
+ // Draw the same combination onto the actual dst using the optimized DrawMask implementation.
+ DrawMask(dst, Rect(0, 0, dst.Width(), dst.Height()), test.src, ZP, test.mask, ZP, test.op)
+ // Check that the resultant pixel at (8, 8) matches what we expect
+ // (the expected value can be verified by hand).
if !eq(dst.At(8, 8), test.expected) {
- t.Errorf("draw %s: %v versus %v", test.desc, dst.At(8, 8), test.expected)
+ t.Errorf("draw %s: at (8, 8) %v versus %v", test.desc, dst.At(8, 8), test.expected)
+ continue
+ }
+ // Check that the resultant dst image matches the golden output.
+ for y := 0; y < golden.Height(); y++ {
+ for x := 0; x < golden.Width(); x++ {
+ if !eq(dst.At(x, y), golden.At(x, y)) {
+ t.Errorf("draw %s: at (%d, %d), %v versus golden %v", test.desc, x, y, dst.At(x, y), golden.At(x, y))
+ continue loop
+ }
+ }
}
}
}