1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
12 func eq(c0
, c1 image
.Color
) bool {
13 r0
, g0
, b0
, a0
:= c0
.RGBA()
14 r1
, g1
, b1
, a1
:= c1
.RGBA()
15 return r0
== r1
&& g0
== g1
&& b0
== b1
&& a0
== a1
18 func fillBlue(alpha
int) image
.Image
{
19 return image
.NewColorImage(image
.RGBAColor
{0, 0, uint8(alpha
), uint8(alpha
)})
22 func fillAlpha(alpha
int) image
.Image
{
23 return image
.NewColorImage(image
.AlphaColor
{uint8(alpha
)})
26 func vgradGreen(alpha
int) image
.Image
{
27 m
:= image
.NewRGBA(16, 16)
28 for y
:= 0; y
< 16; y
++ {
29 for x
:= 0; x
< 16; x
++ {
30 m
.Set(x
, y
, image
.RGBAColor
{0, uint8(y
* alpha
/ 15), 0, uint8(alpha
)})
36 func vgradAlpha(alpha
int) image
.Image
{
37 m
:= image
.NewAlpha(16, 16)
38 for y
:= 0; y
< 16; y
++ {
39 for x
:= 0; x
< 16; x
++ {
40 m
.Set(x
, y
, image
.AlphaColor
{uint8(y
* alpha
/ 15)})
46 func hgradRed(alpha
int) Image
{
47 m
:= image
.NewRGBA(16, 16)
48 for y
:= 0; y
< 16; y
++ {
49 for x
:= 0; x
< 16; x
++ {
50 m
.Set(x
, y
, image
.RGBAColor
{uint8(x
* alpha
/ 15), 0, 0, uint8(alpha
)})
56 func gradYellow(alpha
int) Image
{
57 m
:= image
.NewRGBA(16, 16)
58 for y
:= 0; y
< 16; y
++ {
59 for x
:= 0; x
< 16; x
++ {
60 m
.Set(x
, y
, image
.RGBAColor
{uint8(x
* alpha
/ 15), uint8(y
* alpha
/ 15), 0, uint8(alpha
)})
66 type drawTest
struct {
74 var drawTests
= []drawTest
{
75 // Uniform mask (0% opaque).
76 {"nop", vgradGreen(255), fillAlpha(0), Over
, image
.RGBAColor
{136, 0, 0, 255}},
77 {"clear", vgradGreen(255), fillAlpha(0), Src
, image
.RGBAColor
{0, 0, 0, 0}},
78 // Uniform mask (100%, 75%, nil) and uniform source.
79 // At (x, y) == (8, 8):
80 // The destination pixel is {136, 0, 0, 255}.
81 // The source pixel is {0, 0, 90, 90}.
82 {"fill", fillBlue(90), fillAlpha(255), Over
, image
.RGBAColor
{88, 0, 90, 255}},
83 {"fillSrc", fillBlue(90), fillAlpha(255), Src
, image
.RGBAColor
{0, 0, 90, 90}},
84 {"fillAlpha", fillBlue(90), fillAlpha(192), Over
, image
.RGBAColor
{100, 0, 68, 255}},
85 {"fillAlphaSrc", fillBlue(90), fillAlpha(192), Src
, image
.RGBAColor
{0, 0, 68, 68}},
86 {"fillNil", fillBlue(90), nil, Over
, image
.RGBAColor
{88, 0, 90, 255}},
87 {"fillNilSrc", fillBlue(90), nil, Src
, image
.RGBAColor
{0, 0, 90, 90}},
88 // Uniform mask (100%, 75%, nil) and variable source.
89 // At (x, y) == (8, 8):
90 // The destination pixel is {136, 0, 0, 255}.
91 // The source pixel is {0, 48, 0, 90}.
92 {"copy", vgradGreen(90), fillAlpha(255), Over
, image
.RGBAColor
{88, 48, 0, 255}},
93 {"copySrc", vgradGreen(90), fillAlpha(255), Src
, image
.RGBAColor
{0, 48, 0, 90}},
94 {"copyAlpha", vgradGreen(90), fillAlpha(192), Over
, image
.RGBAColor
{100, 36, 0, 255}},
95 {"copyAlphaSrc", vgradGreen(90), fillAlpha(192), Src
, image
.RGBAColor
{0, 36, 0, 68}},
96 {"copyNil", vgradGreen(90), nil, Over
, image
.RGBAColor
{88, 48, 0, 255}},
97 {"copyNilSrc", vgradGreen(90), nil, Src
, image
.RGBAColor
{0, 48, 0, 90}},
98 // Variable mask and variable source.
99 // At (x, y) == (8, 8):
100 // The destination pixel is {136, 0, 0, 255}.
101 // The source pixel is {0, 0, 255, 255}.
102 // The mask pixel's alpha is 102, or 40%.
103 {"generic", fillBlue(255), vgradAlpha(192), Over
, image
.RGBAColor
{81, 0, 102, 255}},
104 {"genericSrc", fillBlue(255), vgradAlpha(192), Src
, image
.RGBAColor
{0, 0, 102, 102}},
107 func makeGolden(dst
, src
, mask image
.Image
, op Op
) image
.Image
{
108 // Since golden is a newly allocated image, we don't have to check if the
109 // input source and mask images and the output golden image overlap.
111 sx0
:= src
.Bounds().Min
.X
- b
.Min
.X
112 sy0
:= src
.Bounds().Min
.Y
- b
.Min
.Y
115 mx0
= mask
.Bounds().Min
.X
- b
.Min
.X
116 my0
= mask
.Bounds().Min
.Y
- b
.Min
.Y
118 golden
:= image
.NewRGBA(b
.Max
.X
, b
.Max
.Y
)
119 for y
:= b
.Min
.Y
; y
< b
.Max
.Y
; y
++ {
120 my
, sy
:= my0
+y
, sy0
+y
121 for x
:= b
.Min
.X
; x
< b
.Max
.X
; x
++ {
122 mx
, sx
:= mx0
+x
, sx0
+x
124 var dr
, dg
, db
, da
uint32
126 dr
, dg
, db
, da
= dst
.At(x
, y
).RGBA()
128 sr
, sg
, sb
, sa
:= src
.At(sx
, sy
).RGBA()
131 _
, _
, _
, ma
= mask
.At(mx
, my
).RGBA()
133 a
:= M
- (sa
* ma
/ M
)
134 golden
.Set(x
, y
, image
.RGBA64Color
{
135 uint16((dr
*a
+ sr
*ma
) / M
),
136 uint16((dg
*a
+ sg
*ma
) / M
),
137 uint16((db
*a
+ sb
*ma
) / M
),
138 uint16((da
*a
+ sa
*ma
) / M
),
146 func TestDraw(t
*testing
.T
) {
148 for _
, test
:= range drawTests
{
150 // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
151 golden
:= makeGolden(dst
, test
.src
, test
.mask
, test
.op
)
153 if !b
.Eq(golden
.Bounds()) {
154 t
.Errorf("draw %s: bounds %v versus %v", test
.desc
, dst
.Bounds(), golden
.Bounds())
157 // Draw the same combination onto the actual dst using the optimized DrawMask implementation.
158 DrawMask(dst
, b
, test
.src
, image
.ZP
, test
.mask
, image
.ZP
, test
.op
)
159 // Check that the resultant pixel at (8, 8) matches what we expect
160 // (the expected value can be verified by hand).
161 if !eq(dst
.At(8, 8), test
.expected
) {
162 t
.Errorf("draw %s: at (8, 8) %v versus %v", test
.desc
, dst
.At(8, 8), test
.expected
)
165 // Check that the resultant dst image matches the golden output.
166 for y
:= b
.Min
.Y
; y
< b
.Max
.Y
; y
++ {
167 for x
:= b
.Min
.X
; x
< b
.Max
.X
; x
++ {
168 if !eq(dst
.At(x
, y
), golden
.At(x
, y
)) {
169 t
.Errorf("draw %s: at (%d, %d), %v versus golden %v", test
.desc
, x
, y
, dst
.At(x
, y
), golden
.At(x
, y
))
177 func TestDrawOverlap(t
*testing
.T
) {
178 for _
, op
:= range []Op
{Over
, Src
} {
179 for yoff
:= -2; yoff
<= 2; yoff
++ {
181 for xoff
:= -2; xoff
<= 2; xoff
++ {
182 m
:= gradYellow(127).(*image
.RGBA
)
186 Rect
: image
.Rect(5, 5, 10, 10),
191 Rect
: image
.Rect(5+xoff
, 5+yoff
, 10+xoff
, 10+yoff
),
193 // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
194 golden
:= makeGolden(dst
, src
, nil, op
)
196 if !b
.Eq(golden
.Bounds()) {
197 t
.Errorf("drawOverlap xoff=%d,yoff=%d: bounds %v versus %v", xoff
, yoff
, dst
.Bounds(), golden
.Bounds())
200 // Draw the same combination onto the actual dst using the optimized DrawMask implementation.
201 DrawMask(dst
, b
, src
, src
.Bounds().Min
, nil, image
.ZP
, op
)
202 // Check that the resultant dst image matches the golden output.
203 for y
:= b
.Min
.Y
; y
< b
.Max
.Y
; y
++ {
204 for x
:= b
.Min
.X
; x
< b
.Max
.X
; x
++ {
205 if !eq(dst
.At(x
, y
), golden
.At(x
, y
)) {
206 t
.Errorf("drawOverlap xoff=%d,yoff=%d: at (%d, %d), %v versus golden %v", xoff
, yoff
, x
, y
, dst
.At(x
, y
), golden
.At(x
, y
))
216 // TestIssue836 verifies http://code.google.com/p/go/issues/detail?id=836.
217 func TestIssue836(t
*testing
.T
) {
218 a
:= image
.NewRGBA(1, 1)
219 b
:= image
.NewRGBA(2, 2)
220 b
.Set(0, 0, image
.RGBAColor
{0, 0, 0, 5})
221 b
.Set(1, 0, image
.RGBAColor
{0, 0, 5, 5})
222 b
.Set(0, 1, image
.RGBAColor
{0, 5, 0, 5})
223 b
.Set(1, 1, image
.RGBAColor
{5, 0, 0, 5})
224 Draw(a
, image
.Rect(0, 0, 1, 1), b
, image
.Pt(1, 1))
225 if !eq(image
.RGBAColor
{5, 0, 0, 5}, a
.At(0, 0)) {
226 t
.Errorf("Issue 836: want %v got %v", image
.RGBAColor
{5, 0, 0, 5}, a
.At(0, 0))