From 3dc04f4a22743623ef35ef69062e4d99ac12275d Mon Sep 17 00:00:00 2001 From: Nigel Tao Date: Wed, 17 Feb 2010 14:34:51 +1100 Subject: [PATCH] Add Src and Over draw operators. R=r, rsc CC=golang-dev https://golang.org/cl/207096 --- src/pkg/exp/4s/xs.go | 18 +++--- src/pkg/exp/draw/draw.go | 100 ++++++++++++++++++++-------------- src/pkg/exp/draw/draw_test.go | 40 ++++++++++---- 3 files changed, 95 insertions(+), 63 deletions(-) diff --git a/src/pkg/exp/4s/xs.go b/src/pkg/exp/4s/xs.go index e1d28c8850..c5493e719e 100644 --- a/src/pkg/exp/4s/xs.go +++ b/src/pkg/exp/4s/xs.go @@ -266,14 +266,14 @@ func setpiece(p *Piece) { draw.Draw(bb2, r2, draw.White, draw.ZP) draw.Draw(bb2, r.Add(delta), bb, bbr.Min) draw.Draw(bb2mask, r2, draw.Transparent, draw.ZP) - draw.DrawMask(bb2mask, r, draw.Opaque, bbr.Min, bbmask, draw.ZP, draw.SoverD) - draw.DrawMask(bb2mask, r.Add(delta), draw.Opaque, bbr.Min, bbmask, draw.ZP, draw.SoverD) + draw.DrawMask(bb2mask, r, draw.Opaque, bbr.Min, bbmask, draw.ZP, draw.Over) + draw.DrawMask(bb2mask, r.Add(delta), draw.Opaque, bbr.Min, bbmask, draw.ZP, draw.Over) } func drawpiece() { - draw.DrawMask(screen, br.Add(pos), bb, bbr.Min, bbmask, draw.ZP, draw.SoverD) + draw.DrawMask(screen, br.Add(pos), bb, bbr.Min, bbmask, draw.ZP, draw.Over) if suspended { - draw.DrawMask(screen, br.Add(pos), draw.White, draw.ZP, whitemask, draw.ZP, draw.SoverD) + draw.DrawMask(screen, br.Add(pos), draw.White, draw.ZP, whitemask, draw.ZP, draw.Over) } } @@ -282,7 +282,7 @@ func undrawpiece() { if collider(pos, br.Max) { mask = bbmask } - draw.DrawMask(screen, br.Add(pos), draw.White, bbr.Min, mask, bbr.Min, draw.SoverD) + draw.DrawMask(screen, br.Add(pos), draw.White, bbr.Min, mask, bbr.Min, draw.Over) } func rest() { @@ -349,7 +349,7 @@ func drawboard() { } score(0) if suspended { - draw.DrawMask(screen, screenr, draw.White, draw.ZP, whitemask, draw.ZP, draw.SoverD) + draw.DrawMask(screen, screenr, draw.White, draw.ZP, whitemask, draw.ZP, draw.Over) } } @@ -375,7 +375,7 @@ func movepiece() bool { if collider(pos, br2.Max) { mask = bb2mask } - draw.DrawMask(screen, br2.Add(pos), bb2, bb2r.Min, mask, bb2r.Min, draw.SoverD) + draw.DrawMask(screen, br2.Add(pos), bb2, bb2r.Min, mask, bb2r.Min, draw.Over) pos.Y += DY display.FlushImage() return true @@ -444,7 +444,7 @@ func horiz() bool { for j := 0; j < h; j++ { r.Min.Y = rboard.Min.Y + lev[j]*pcsz r.Max.Y = r.Min.Y + pcsz - draw.DrawMask(screen, r, draw.White, draw.ZP, whitemask, draw.ZP, draw.SoverD) + draw.DrawMask(screen, r, draw.White, draw.ZP, whitemask, draw.ZP, draw.Over) display.FlushImage() } PlaySound(whoosh) @@ -457,7 +457,7 @@ func horiz() bool { for j := 0; j < h; j++ { r.Min.Y = rboard.Min.Y + lev[j]*pcsz r.Max.Y = r.Min.Y + pcsz - draw.DrawMask(screen, r, draw.White, draw.ZP, whitemask, draw.ZP, draw.SoverD) + draw.DrawMask(screen, r, draw.White, draw.ZP, whitemask, draw.ZP, draw.Over) } display.FlushImage() } diff --git a/src/pkg/exp/draw/draw.go b/src/pkg/exp/draw/draw.go index 1888e69a58..bf5a08479e 100644 --- a/src/pkg/exp/draw/draw.go +++ b/src/pkg/exp/draw/draw.go @@ -15,7 +15,14 @@ import "image" // A Porter-Duff compositing operator. type Op int -const SoverD Op = 0 +const ( + // Over specifies ``(src in mask) over dst''. + Over Op = iota + // Src specifies ``src in mask''. + Src +) + +var zeroColor image.Color = image.AlphaColor{0} // A draw.Image is an image.Image with a Set method to change a single pixel. type Image interface { @@ -23,14 +30,13 @@ type Image interface { Set(x, y int, c image.Color) } -// Draw calls DrawMask with a nil mask and an SoverD op. +// Draw calls DrawMask with a nil mask and an Over op. func Draw(dst Image, r Rectangle, src image.Image, sp Point) { - DrawMask(dst, r, src, sp, nil, ZP, SoverD) + DrawMask(dst, r, src, sp, nil, ZP, Over) } // DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r -// in dst with the result of a Porter-Duff composition. For the SoverD operator, the result -// is ``(src in mask) over dst''. If mask is nil, this simplifies to ``src over dst''. +// in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. // The implementation is simple and slow. // TODO(nigeltao): Optimize this. func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Image, mp Point, op Op) { @@ -54,22 +60,25 @@ func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Imag // TODO(nigeltao): Ensure that r is well formed, i.e. r.Max.X >= r.Min.X and likewise for Y. // Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation. - if dst0, ok := dst.(*image.RGBA); ok && op == SoverD { - if mask == nil { - if src0, ok := src.(image.ColorImage); ok { - drawFill(dst0, r, src0) - return - } - if src0, ok := src.(*image.RGBA); ok { - if dst0 == src0 && r.Overlaps(r.Add(sp.Sub(r.Min))) { - // TODO(nigeltao): Implement a fast path for the overlapping case. - } else { - drawCopy(dst0, r, src0, sp) + if dst0, ok := dst.(*image.RGBA); ok { + if op == Over { + // TODO(nigeltao): Implement a fast path for font glyphs (i.e. when mask is an image.Alpha). + } else { + if mask == nil { + if src0, ok := src.(image.ColorImage); ok { + drawFill(dst0, r, src0) return } + if src0, ok := src.(*image.RGBA); ok { + if dst0 == src0 && r.Overlaps(r.Add(sp.Sub(r.Min))) { + // TODO(nigeltao): Implement a fast path for the overlapping case. + } else { + drawCopy(dst0, r, src0, sp) + return + } + } } } - // TODO(nigeltao): Implement a fast path for font glyphs (i.e. when mask is an image.Alpha). } x0, x1, dx := r.Min.X, r.Max.X, 1 @@ -89,42 +98,49 @@ func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Imag sx := sp.X + x0 - r.Min.X mx := mp.X + x0 - r.Min.X for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx { - // TODO(nigeltao): Check that op == SoverD. - if mask == nil { - dst.Set(x, y, src.At(sx, sy)) - continue + // A nil mask is equivalent to a fully opaque, infinitely large mask. + // We work in 16-bit color, so that multiplying two values does not overflow a uint32. + const M = 1<<16 - 1 + ma := uint32(M) + if mask != nil { + _, _, _, ma = mask.At(mx, my).RGBA() + ma >>= 16 } - _, _, _, ma := mask.At(mx, my).RGBA() - switch ma { - case 0: - continue - case 0xFFFFFFFF: + switch { + case ma == 0: + if op == Over { + // No-op. + } else { + dst.Set(x, y, zeroColor) + } + case ma == M && op == Src: dst.Set(x, y, src.At(sx, sy)) default: - dr, dg, db, da := dst.At(x, y).RGBA() - dr >>= 16 - dg >>= 16 - db >>= 16 - da >>= 16 sr, sg, sb, sa := src.At(sx, sy).RGBA() sr >>= 16 sg >>= 16 sb >>= 16 sa >>= 16 - ma >>= 16 - const M = 1<<16 - 1 - a := sa * ma / M - dr = (dr*(M-a) + sr*ma) / M - dg = (dg*(M-a) + sg*ma) / M - db = (db*(M-a) + sb*ma) / M - da = (da*(M-a) + sa*ma) / M if out == nil { out = new(image.RGBA64Color) } - out.R = uint16(dr) - out.G = uint16(dg) - out.B = uint16(db) - out.A = uint16(da) + if op == Over { + dr, dg, db, da := dst.At(x, y).RGBA() + dr >>= 16 + dg >>= 16 + db >>= 16 + da >>= 16 + a := M - (sa * ma / M) + out.R = uint16((dr*a + sr*ma) / M) + out.G = uint16((dg*a + sg*ma) / M) + out.B = uint16((db*a + sb*ma) / M) + out.A = uint16((da*a + sa*ma) / M) + } else { + out.R = uint16(sr * ma / M) + out.G = uint16(sg * ma / M) + out.B = uint16(sb * ma / M) + out.A = uint16(sa * ma / M) + } dst.Set(x, y, out) } } diff --git a/src/pkg/exp/draw/draw_test.go b/src/pkg/exp/draw/draw_test.go index 61794dab88..64cf71cf61 100644 --- a/src/pkg/exp/draw/draw_test.go +++ b/src/pkg/exp/draw/draw_test.go @@ -57,31 +57,47 @@ type drawTest struct { desc string src image.Image mask image.Image + op Op expected image.Color } var drawTests = []drawTest{ - // Uniform mask (0% opaque) mask. - drawTest{"nop", vgradGreen(255), fillAlpha(0), image.RGBAColor{136, 0, 0, 255}}, - // Uniform mask (100%, 75%, nil) and vertical-gradient source. - drawTest{"copy", vgradGreen(90), fillAlpha(255), image.RGBAColor{0, 48, 0, 90}}, - drawTest{"copyAlpha", vgradGreen(90), fillAlpha(192), image.RGBAColor{100, 36, 0, 255}}, - drawTest{"copyNil", vgradGreen(90), nil, image.RGBAColor{0, 48, 0, 90}}, + // Uniform mask (0% opaque). + drawTest{"nop", vgradGreen(255), fillAlpha(0), Over, image.RGBAColor{136, 0, 0, 255}}, + drawTest{"clear", vgradGreen(255), fillAlpha(0), Src, image.RGBAColor{0, 0, 0, 0}}, // Uniform mask (100%, 75%, nil) and uniform source. - drawTest{"fill", fillBlue(90), fillAlpha(255), image.RGBAColor{0, 0, 90, 90}}, - drawTest{"fillAlpha", fillBlue(90), fillAlpha(192), image.RGBAColor{100, 0, 68, 255}}, - drawTest{"fillNil", fillBlue(90), nil, image.RGBAColor{0, 0, 90, 90}}, - // Variable mask. In detail, at (x, y) == (8, 8): + // At (x, y) == (8, 8): + // The destination pixel is {136, 0, 0, 255}. + // The source pixel is {0, 0, 90, 90}. + drawTest{"fill", fillBlue(90), fillAlpha(255), Over, image.RGBAColor{88, 0, 90, 255}}, + drawTest{"fillSrc", fillBlue(90), fillAlpha(255), Src, image.RGBAColor{0, 0, 90, 90}}, + drawTest{"fillAlpha", fillBlue(90), fillAlpha(192), Over, image.RGBAColor{100, 0, 68, 255}}, + drawTest{"fillAlphaSrc", fillBlue(90), fillAlpha(192), Src, image.RGBAColor{0, 0, 68, 68}}, + drawTest{"fillNil", fillBlue(90), nil, Over, image.RGBAColor{88, 0, 90, 255}}, + drawTest{"fillNilSrc", fillBlue(90), nil, Src, image.RGBAColor{0, 0, 90, 90}}, + // Uniform mask (100%, 75%, nil) and variable source. + // At (x, y) == (8, 8): + // The destination pixel is {136, 0, 0, 255}. + // The source pixel is {0, 48, 0, 90}. + drawTest{"copy", vgradGreen(90), fillAlpha(255), Over, image.RGBAColor{88, 48, 0, 255}}, + drawTest{"copySrc", vgradGreen(90), fillAlpha(255), Src, image.RGBAColor{0, 48, 0, 90}}, + drawTest{"copyAlpha", vgradGreen(90), fillAlpha(192), Over, image.RGBAColor{100, 36, 0, 255}}, + drawTest{"copyAlphaSrc", vgradGreen(90), fillAlpha(192), Src, image.RGBAColor{0, 36, 0, 68}}, + drawTest{"copyNil", vgradGreen(90), nil, Over, image.RGBAColor{88, 48, 0, 255}}, + drawTest{"copyNilSrc", vgradGreen(90), nil, Src, image.RGBAColor{0, 48, 0, 90}}, + // Variable mask and variable source. + // At (x, y) == (8, 8): // The destination pixel is {136, 0, 0, 255}. // The source pixel is {0, 0, 255, 255}. // The mask pixel's alpha is 102, or 40%. - drawTest{"generic", fillBlue(255), vgradAlpha(192), image.RGBAColor{81, 0, 102, 255}}, + drawTest{"generic", fillBlue(255), vgradAlpha(192), Over, image.RGBAColor{81, 0, 102, 255}}, + drawTest{"genericSrc", fillBlue(255), vgradAlpha(192), Src, image.RGBAColor{0, 0, 102, 102}}, } func TestDraw(t *testing.T) { for _, test := range drawTests { dst := hgradRed(255) - DrawMask(dst, Rect(0, 0, 16, 16), test.src, ZP, test.mask, ZP, SoverD) + DrawMask(dst, Rect(0, 0, 16, 16), test.src, ZP, test.mask, ZP, test.op) if !eq(dst.At(8, 8), test.expected) { t.Errorf("draw %s: %v versus %v", test.desc, dst.At(8, 8), test.expected) } -- 2.48.1