2010-02-10 19:38:16 -07:00
|
|
|
// Copyright 2010 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 draw
|
|
|
|
|
|
|
|
import (
|
|
|
|
"image"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
func eq(c0, c1 image.Color) bool {
|
|
|
|
r0, g0, b0, a0 := c0.RGBA()
|
|
|
|
r1, g1, b1, a1 := c1.RGBA()
|
|
|
|
return r0 == r1 && g0 == g1 && b0 == b1 && a0 == a1
|
|
|
|
}
|
|
|
|
|
|
|
|
func fillBlue(alpha int) image.Image {
|
|
|
|
return image.ColorImage{image.RGBAColor{0, 0, uint8(alpha), uint8(alpha)}}
|
|
|
|
}
|
|
|
|
|
|
|
|
func fillAlpha(alpha int) image.Image {
|
|
|
|
return image.ColorImage{image.AlphaColor{uint8(alpha)}}
|
|
|
|
}
|
|
|
|
|
|
|
|
func vgradGreen(alpha int) image.Image {
|
|
|
|
m := image.NewRGBA(16, 16)
|
|
|
|
for y := 0; y < 16; y++ {
|
|
|
|
for x := 0; x < 16; x++ {
|
|
|
|
m.Set(x, y, image.RGBAColor{0, uint8(y * alpha / 15), 0, uint8(alpha)})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
func vgradAlpha(alpha int) image.Image {
|
|
|
|
m := image.NewAlpha(16, 16)
|
|
|
|
for y := 0; y < 16; y++ {
|
|
|
|
for x := 0; x < 16; x++ {
|
|
|
|
m.Set(x, y, image.AlphaColor{uint8(y * alpha / 15)})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
func hgradRed(alpha int) Image {
|
|
|
|
m := image.NewRGBA(16, 16)
|
|
|
|
for y := 0; y < 16; y++ {
|
|
|
|
for x := 0; x < 16; x++ {
|
|
|
|
m.Set(x, y, image.RGBAColor{uint8(x * alpha / 15), 0, 0, uint8(alpha)})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
type drawTest struct {
|
|
|
|
desc string
|
|
|
|
src image.Image
|
|
|
|
mask image.Image
|
2010-02-16 20:34:51 -07:00
|
|
|
op Op
|
2010-02-10 19:38:16 -07:00
|
|
|
expected image.Color
|
|
|
|
}
|
|
|
|
|
|
|
|
var drawTests = []drawTest{
|
2010-02-16 20:34:51 -07:00
|
|
|
// 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}},
|
2010-02-10 19:38:16 -07:00
|
|
|
// Uniform mask (100%, 75%, nil) and uniform source.
|
2010-02-16 20:34:51 -07:00
|
|
|
// 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):
|
2010-02-10 19:38:16 -07:00
|
|
|
// 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%.
|
2010-02-16 20:34:51 -07:00
|
|
|
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}},
|
2010-02-10 19:38:16 -07:00
|
|
|
}
|
|
|
|
|
2010-05-20 14:57:18 -06:00
|
|
|
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.
|
2010-08-09 20:08:52 -06:00
|
|
|
b := dst.Bounds()
|
|
|
|
golden := image.NewRGBA(b.Dx(), b.Dy())
|
|
|
|
for y := b.Min.Y; y < b.Max.Y; y++ {
|
2010-05-20 14:57:18 -06:00
|
|
|
my, sy := y, y
|
2010-08-09 20:08:52 -06:00
|
|
|
for x := b.Min.X; x < b.Max.X; x++ {
|
2010-05-20 14:57:18 -06:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
sr, sg, sb, sa := t.src.At(sx, sy).RGBA()
|
|
|
|
ma := uint32(M)
|
|
|
|
if t.mask != nil {
|
|
|
|
_, _, _, ma = t.mask.At(mx, my).RGBA()
|
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2010-02-10 19:38:16 -07:00
|
|
|
func TestDraw(t *testing.T) {
|
2010-05-21 21:25:08 -06:00
|
|
|
loop:
|
|
|
|
for _, test := range drawTests {
|
2010-02-10 19:38:16 -07:00
|
|
|
dst := hgradRed(255)
|
2010-05-20 14:57:18 -06:00
|
|
|
// Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
|
2010-08-09 20:08:52 -06:00
|
|
|
b := dst.Bounds()
|
2010-05-20 14:57:18 -06:00
|
|
|
golden := makeGolden(dst, test)
|
2010-08-09 20:08:52 -06:00
|
|
|
if !b.Eq(golden.Bounds()) {
|
|
|
|
t.Errorf("draw %s: bounds %v versus %v", test.desc, dst.Bounds(), golden.Bounds())
|
|
|
|
continue
|
|
|
|
}
|
2010-05-20 14:57:18 -06:00
|
|
|
// Draw the same combination onto the actual dst using the optimized DrawMask implementation.
|
2010-08-10 11:11:28 -06:00
|
|
|
DrawMask(dst, image.Rect(b.Min.X, b.Min.Y, b.Max.X, b.Max.Y), test.src, image.ZP, test.mask, image.ZP, test.op)
|
2010-05-20 14:57:18 -06:00
|
|
|
// Check that the resultant pixel at (8, 8) matches what we expect
|
|
|
|
// (the expected value can be verified by hand).
|
2010-02-10 19:38:16 -07:00
|
|
|
if !eq(dst.At(8, 8), test.expected) {
|
2010-05-20 14:57:18 -06:00
|
|
|
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.
|
2010-08-09 20:08:52 -06:00
|
|
|
for y := b.Min.Y; y < b.Max.Y; y++ {
|
|
|
|
for x := b.Min.X; x < b.Max.X; x++ {
|
2010-05-20 14:57:18 -06:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2010-02-10 19:38:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-06-04 18:30:39 -06:00
|
|
|
|
|
|
|
// TestIssue836 verifies http://code.google.com/p/go/issues/detail?id=836.
|
|
|
|
func TestIssue836(t *testing.T) {
|
|
|
|
a := image.NewRGBA(1, 1)
|
|
|
|
b := image.NewRGBA(2, 2)
|
|
|
|
b.Set(0, 0, image.RGBAColor{0, 0, 0, 5})
|
|
|
|
b.Set(1, 0, image.RGBAColor{0, 0, 5, 5})
|
|
|
|
b.Set(0, 1, image.RGBAColor{0, 5, 0, 5})
|
|
|
|
b.Set(1, 1, image.RGBAColor{5, 0, 0, 5})
|
2010-08-10 11:11:28 -06:00
|
|
|
Draw(a, image.Rect(0, 0, 1, 1), b, image.Pt(1, 1))
|
2010-06-04 18:30:39 -06:00
|
|
|
if !eq(image.RGBAColor{5, 0, 0, 5}, a.At(0, 0)) {
|
|
|
|
t.Errorf("Issue 836: want %v got %v", image.RGBAColor{5, 0, 0, 5}, a.At(0, 0))
|
|
|
|
}
|
|
|
|
}
|