~james-page/ubuntu/utopic/gccgo-go/cgo-support

« back to all changes in this revision

Viewing changes to src/pkg/image/draw/draw_test.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-01-27 09:18:55 UTC
  • Revision ID: package-import@ubuntu.com-20140127091855-zxfshmykfsyyw4b2
Tags: upstream-1.2
ImportĀ upstreamĀ versionĀ 1.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.
 
4
 
 
5
package draw
 
6
 
 
7
import (
 
8
        "image"
 
9
        "image/color"
 
10
        "image/png"
 
11
        "os"
 
12
        "testing"
 
13
)
 
14
 
 
15
func eq(c0, c1 color.Color) bool {
 
16
        r0, g0, b0, a0 := c0.RGBA()
 
17
        r1, g1, b1, a1 := c1.RGBA()
 
18
        return r0 == r1 && g0 == g1 && b0 == b1 && a0 == a1
 
19
}
 
20
 
 
21
func fillBlue(alpha int) image.Image {
 
22
        return image.NewUniform(color.RGBA{0, 0, uint8(alpha), uint8(alpha)})
 
23
}
 
24
 
 
25
func fillAlpha(alpha int) image.Image {
 
26
        return image.NewUniform(color.Alpha{uint8(alpha)})
 
27
}
 
28
 
 
29
func vgradGreen(alpha int) image.Image {
 
30
        m := image.NewRGBA(image.Rect(0, 0, 16, 16))
 
31
        for y := 0; y < 16; y++ {
 
32
                for x := 0; x < 16; x++ {
 
33
                        m.Set(x, y, color.RGBA{0, uint8(y * alpha / 15), 0, uint8(alpha)})
 
34
                }
 
35
        }
 
36
        return m
 
37
}
 
38
 
 
39
func vgradAlpha(alpha int) image.Image {
 
40
        m := image.NewAlpha(image.Rect(0, 0, 16, 16))
 
41
        for y := 0; y < 16; y++ {
 
42
                for x := 0; x < 16; x++ {
 
43
                        m.Set(x, y, color.Alpha{uint8(y * alpha / 15)})
 
44
                }
 
45
        }
 
46
        return m
 
47
}
 
48
 
 
49
func vgradGreenNRGBA(alpha int) image.Image {
 
50
        m := image.NewNRGBA(image.Rect(0, 0, 16, 16))
 
51
        for y := 0; y < 16; y++ {
 
52
                for x := 0; x < 16; x++ {
 
53
                        m.Set(x, y, color.RGBA{0, uint8(y * 0x11), 0, uint8(alpha)})
 
54
                }
 
55
        }
 
56
        return m
 
57
}
 
58
 
 
59
func vgradCr() image.Image {
 
60
        m := &image.YCbCr{
 
61
                Y:              make([]byte, 16*16),
 
62
                Cb:             make([]byte, 16*16),
 
63
                Cr:             make([]byte, 16*16),
 
64
                YStride:        16,
 
65
                CStride:        16,
 
66
                SubsampleRatio: image.YCbCrSubsampleRatio444,
 
67
                Rect:           image.Rect(0, 0, 16, 16),
 
68
        }
 
69
        for y := 0; y < 16; y++ {
 
70
                for x := 0; x < 16; x++ {
 
71
                        m.Cr[y*m.CStride+x] = uint8(y * 0x11)
 
72
                }
 
73
        }
 
74
        return m
 
75
}
 
76
 
 
77
func hgradRed(alpha int) Image {
 
78
        m := image.NewRGBA(image.Rect(0, 0, 16, 16))
 
79
        for y := 0; y < 16; y++ {
 
80
                for x := 0; x < 16; x++ {
 
81
                        m.Set(x, y, color.RGBA{uint8(x * alpha / 15), 0, 0, uint8(alpha)})
 
82
                }
 
83
        }
 
84
        return m
 
85
}
 
86
 
 
87
func gradYellow(alpha int) Image {
 
88
        m := image.NewRGBA(image.Rect(0, 0, 16, 16))
 
89
        for y := 0; y < 16; y++ {
 
90
                for x := 0; x < 16; x++ {
 
91
                        m.Set(x, y, color.RGBA{uint8(x * alpha / 15), uint8(y * alpha / 15), 0, uint8(alpha)})
 
92
                }
 
93
        }
 
94
        return m
 
95
}
 
96
 
 
97
type drawTest struct {
 
98
        desc     string
 
99
        src      image.Image
 
100
        mask     image.Image
 
101
        op       Op
 
102
        expected color.Color
 
103
}
 
104
 
 
105
var drawTests = []drawTest{
 
106
        // Uniform mask (0% opaque).
 
107
        {"nop", vgradGreen(255), fillAlpha(0), Over, color.RGBA{136, 0, 0, 255}},
 
108
        {"clear", vgradGreen(255), fillAlpha(0), Src, color.RGBA{0, 0, 0, 0}},
 
109
        // Uniform mask (100%, 75%, nil) and uniform source.
 
110
        // At (x, y) == (8, 8):
 
111
        // The destination pixel is {136, 0, 0, 255}.
 
112
        // The source pixel is {0, 0, 90, 90}.
 
113
        {"fill", fillBlue(90), fillAlpha(255), Over, color.RGBA{88, 0, 90, 255}},
 
114
        {"fillSrc", fillBlue(90), fillAlpha(255), Src, color.RGBA{0, 0, 90, 90}},
 
115
        {"fillAlpha", fillBlue(90), fillAlpha(192), Over, color.RGBA{100, 0, 68, 255}},
 
116
        {"fillAlphaSrc", fillBlue(90), fillAlpha(192), Src, color.RGBA{0, 0, 68, 68}},
 
117
        {"fillNil", fillBlue(90), nil, Over, color.RGBA{88, 0, 90, 255}},
 
118
        {"fillNilSrc", fillBlue(90), nil, Src, color.RGBA{0, 0, 90, 90}},
 
119
        // Uniform mask (100%, 75%, nil) and variable source.
 
120
        // At (x, y) == (8, 8):
 
121
        // The destination pixel is {136, 0, 0, 255}.
 
122
        // The source pixel is {0, 48, 0, 90}.
 
123
        {"copy", vgradGreen(90), fillAlpha(255), Over, color.RGBA{88, 48, 0, 255}},
 
124
        {"copySrc", vgradGreen(90), fillAlpha(255), Src, color.RGBA{0, 48, 0, 90}},
 
125
        {"copyAlpha", vgradGreen(90), fillAlpha(192), Over, color.RGBA{100, 36, 0, 255}},
 
126
        {"copyAlphaSrc", vgradGreen(90), fillAlpha(192), Src, color.RGBA{0, 36, 0, 68}},
 
127
        {"copyNil", vgradGreen(90), nil, Over, color.RGBA{88, 48, 0, 255}},
 
128
        {"copyNilSrc", vgradGreen(90), nil, Src, color.RGBA{0, 48, 0, 90}},
 
129
        // Uniform mask (100%, 75%, nil) and variable NRGBA source.
 
130
        // At (x, y) == (8, 8):
 
131
        // The destination pixel is {136, 0, 0, 255}.
 
132
        // The source pixel is {0, 136, 0, 90} in NRGBA-space, which is {0, 48, 0, 90} in RGBA-space.
 
133
        // The result pixel is different than in the "copy*" test cases because of rounding errors.
 
134
        {"nrgba", vgradGreenNRGBA(90), fillAlpha(255), Over, color.RGBA{88, 46, 0, 255}},
 
135
        {"nrgbaSrc", vgradGreenNRGBA(90), fillAlpha(255), Src, color.RGBA{0, 46, 0, 90}},
 
136
        {"nrgbaAlpha", vgradGreenNRGBA(90), fillAlpha(192), Over, color.RGBA{100, 34, 0, 255}},
 
137
        {"nrgbaAlphaSrc", vgradGreenNRGBA(90), fillAlpha(192), Src, color.RGBA{0, 34, 0, 68}},
 
138
        {"nrgbaNil", vgradGreenNRGBA(90), nil, Over, color.RGBA{88, 46, 0, 255}},
 
139
        {"nrgbaNilSrc", vgradGreenNRGBA(90), nil, Src, color.RGBA{0, 46, 0, 90}},
 
140
        // Uniform mask (100%, 75%, nil) and variable YCbCr source.
 
141
        // At (x, y) == (8, 8):
 
142
        // The destination pixel is {136, 0, 0, 255}.
 
143
        // The source pixel is {0, 0, 136} in YCbCr-space, which is {11, 38, 0, 255} in RGB-space.
 
144
        {"ycbcr", vgradCr(), fillAlpha(255), Over, color.RGBA{11, 38, 0, 255}},
 
145
        {"ycbcrSrc", vgradCr(), fillAlpha(255), Src, color.RGBA{11, 38, 0, 255}},
 
146
        {"ycbcrAlpha", vgradCr(), fillAlpha(192), Over, color.RGBA{42, 28, 0, 255}},
 
147
        {"ycbcrAlphaSrc", vgradCr(), fillAlpha(192), Src, color.RGBA{8, 28, 0, 192}},
 
148
        {"ycbcrNil", vgradCr(), nil, Over, color.RGBA{11, 38, 0, 255}},
 
149
        {"ycbcrNilSrc", vgradCr(), nil, Src, color.RGBA{11, 38, 0, 255}},
 
150
        // Variable mask and variable source.
 
151
        // At (x, y) == (8, 8):
 
152
        // The destination pixel is {136, 0, 0, 255}.
 
153
        // The source pixel is {0, 0, 255, 255}.
 
154
        // The mask pixel's alpha is 102, or 40%.
 
155
        {"generic", fillBlue(255), vgradAlpha(192), Over, color.RGBA{81, 0, 102, 255}},
 
156
        {"genericSrc", fillBlue(255), vgradAlpha(192), Src, color.RGBA{0, 0, 102, 102}},
 
157
}
 
158
 
 
159
func makeGolden(dst image.Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) image.Image {
 
160
        // Since golden is a newly allocated image, we don't have to check if the
 
161
        // input source and mask images and the output golden image overlap.
 
162
        b := dst.Bounds()
 
163
        sb := src.Bounds()
 
164
        mb := image.Rect(-1e9, -1e9, 1e9, 1e9)
 
165
        if mask != nil {
 
166
                mb = mask.Bounds()
 
167
        }
 
168
        golden := image.NewRGBA(image.Rect(0, 0, b.Max.X, b.Max.Y))
 
169
        for y := r.Min.Y; y < r.Max.Y; y++ {
 
170
                sy := y + sp.Y - r.Min.Y
 
171
                my := y + mp.Y - r.Min.Y
 
172
                for x := r.Min.X; x < r.Max.X; x++ {
 
173
                        if !(image.Pt(x, y).In(b)) {
 
174
                                continue
 
175
                        }
 
176
                        sx := x + sp.X - r.Min.X
 
177
                        if !(image.Pt(sx, sy).In(sb)) {
 
178
                                continue
 
179
                        }
 
180
                        mx := x + mp.X - r.Min.X
 
181
                        if !(image.Pt(mx, my).In(mb)) {
 
182
                                continue
 
183
                        }
 
184
 
 
185
                        const M = 1<<16 - 1
 
186
                        var dr, dg, db, da uint32
 
187
                        if op == Over {
 
188
                                dr, dg, db, da = dst.At(x, y).RGBA()
 
189
                        }
 
190
                        sr, sg, sb, sa := src.At(sx, sy).RGBA()
 
191
                        ma := uint32(M)
 
192
                        if mask != nil {
 
193
                                _, _, _, ma = mask.At(mx, my).RGBA()
 
194
                        }
 
195
                        a := M - (sa * ma / M)
 
196
                        golden.Set(x, y, color.RGBA64{
 
197
                                uint16((dr*a + sr*ma) / M),
 
198
                                uint16((dg*a + sg*ma) / M),
 
199
                                uint16((db*a + sb*ma) / M),
 
200
                                uint16((da*a + sa*ma) / M),
 
201
                        })
 
202
                }
 
203
        }
 
204
        return golden.SubImage(b)
 
205
}
 
206
 
 
207
func TestDraw(t *testing.T) {
 
208
        rr := []image.Rectangle{
 
209
                image.Rect(0, 0, 0, 0),
 
210
                image.Rect(0, 0, 16, 16),
 
211
                image.Rect(3, 5, 12, 10),
 
212
                image.Rect(0, 0, 9, 9),
 
213
                image.Rect(8, 8, 16, 16),
 
214
                image.Rect(8, 0, 9, 16),
 
215
                image.Rect(0, 8, 16, 9),
 
216
                image.Rect(8, 8, 9, 9),
 
217
                image.Rect(8, 8, 8, 8),
 
218
        }
 
219
        for _, r := range rr {
 
220
        loop:
 
221
                for _, test := range drawTests {
 
222
                        dst := hgradRed(255).(*image.RGBA).SubImage(r).(Image)
 
223
                        // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
 
224
                        golden := makeGolden(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op)
 
225
                        b := dst.Bounds()
 
226
                        if !b.Eq(golden.Bounds()) {
 
227
                                t.Errorf("draw %v %s: bounds %v versus %v", r, test.desc, dst.Bounds(), golden.Bounds())
 
228
                                continue
 
229
                        }
 
230
                        // Draw the same combination onto the actual dst using the optimized DrawMask implementation.
 
231
                        DrawMask(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op)
 
232
                        if image.Pt(8, 8).In(r) {
 
233
                                // Check that the resultant pixel at (8, 8) matches what we expect
 
234
                                // (the expected value can be verified by hand).
 
235
                                if !eq(dst.At(8, 8), test.expected) {
 
236
                                        t.Errorf("draw %v %s: at (8, 8) %v versus %v", r, test.desc, dst.At(8, 8), test.expected)
 
237
                                        continue
 
238
                                }
 
239
                        }
 
240
                        // Check that the resultant dst image matches the golden output.
 
241
                        for y := b.Min.Y; y < b.Max.Y; y++ {
 
242
                                for x := b.Min.X; x < b.Max.X; x++ {
 
243
                                        if !eq(dst.At(x, y), golden.At(x, y)) {
 
244
                                                t.Errorf("draw %v %s: at (%d, %d), %v versus golden %v", r, test.desc, x, y, dst.At(x, y), golden.At(x, y))
 
245
                                                continue loop
 
246
                                        }
 
247
                                }
 
248
                        }
 
249
                }
 
250
        }
 
251
}
 
252
 
 
253
func TestDrawOverlap(t *testing.T) {
 
254
        for _, op := range []Op{Over, Src} {
 
255
                for yoff := -2; yoff <= 2; yoff++ {
 
256
                loop:
 
257
                        for xoff := -2; xoff <= 2; xoff++ {
 
258
                                m := gradYellow(127).(*image.RGBA)
 
259
                                dst := m.SubImage(image.Rect(5, 5, 10, 10)).(*image.RGBA)
 
260
                                src := m.SubImage(image.Rect(5+xoff, 5+yoff, 10+xoff, 10+yoff)).(*image.RGBA)
 
261
                                b := dst.Bounds()
 
262
                                // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
 
263
                                golden := makeGolden(dst, b, src, src.Bounds().Min, nil, image.ZP, op)
 
264
                                if !b.Eq(golden.Bounds()) {
 
265
                                        t.Errorf("drawOverlap xoff=%d,yoff=%d: bounds %v versus %v", xoff, yoff, dst.Bounds(), golden.Bounds())
 
266
                                        continue
 
267
                                }
 
268
                                // Draw the same combination onto the actual dst using the optimized DrawMask implementation.
 
269
                                DrawMask(dst, b, src, src.Bounds().Min, nil, image.ZP, op)
 
270
                                // Check that the resultant dst image matches the golden output.
 
271
                                for y := b.Min.Y; y < b.Max.Y; y++ {
 
272
                                        for x := b.Min.X; x < b.Max.X; x++ {
 
273
                                                if !eq(dst.At(x, y), golden.At(x, y)) {
 
274
                                                        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))
 
275
                                                        continue loop
 
276
                                                }
 
277
                                        }
 
278
                                }
 
279
                        }
 
280
                }
 
281
        }
 
282
}
 
283
 
 
284
// TestNonZeroSrcPt checks drawing with a non-zero src point parameter.
 
285
func TestNonZeroSrcPt(t *testing.T) {
 
286
        a := image.NewRGBA(image.Rect(0, 0, 1, 1))
 
287
        b := image.NewRGBA(image.Rect(0, 0, 2, 2))
 
288
        b.Set(0, 0, color.RGBA{0, 0, 0, 5})
 
289
        b.Set(1, 0, color.RGBA{0, 0, 5, 5})
 
290
        b.Set(0, 1, color.RGBA{0, 5, 0, 5})
 
291
        b.Set(1, 1, color.RGBA{5, 0, 0, 5})
 
292
        Draw(a, image.Rect(0, 0, 1, 1), b, image.Pt(1, 1), Over)
 
293
        if !eq(color.RGBA{5, 0, 0, 5}, a.At(0, 0)) {
 
294
                t.Errorf("non-zero src pt: want %v got %v", color.RGBA{5, 0, 0, 5}, a.At(0, 0))
 
295
        }
 
296
}
 
297
 
 
298
func TestFill(t *testing.T) {
 
299
        rr := []image.Rectangle{
 
300
                image.Rect(0, 0, 0, 0),
 
301
                image.Rect(0, 0, 40, 30),
 
302
                image.Rect(10, 0, 40, 30),
 
303
                image.Rect(0, 20, 40, 30),
 
304
                image.Rect(10, 20, 40, 30),
 
305
                image.Rect(10, 20, 15, 25),
 
306
                image.Rect(10, 0, 35, 30),
 
307
                image.Rect(0, 15, 40, 16),
 
308
                image.Rect(24, 24, 25, 25),
 
309
                image.Rect(23, 23, 26, 26),
 
310
                image.Rect(22, 22, 27, 27),
 
311
                image.Rect(21, 21, 28, 28),
 
312
                image.Rect(20, 20, 29, 29),
 
313
        }
 
314
        for _, r := range rr {
 
315
                m := image.NewRGBA(image.Rect(0, 0, 40, 30)).SubImage(r).(*image.RGBA)
 
316
                b := m.Bounds()
 
317
                c := color.RGBA{11, 0, 0, 255}
 
318
                src := &image.Uniform{C: c}
 
319
                check := func(desc string) {
 
320
                        for y := b.Min.Y; y < b.Max.Y; y++ {
 
321
                                for x := b.Min.X; x < b.Max.X; x++ {
 
322
                                        if !eq(c, m.At(x, y)) {
 
323
                                                t.Errorf("%s fill: at (%d, %d), sub-image bounds=%v: want %v got %v", desc, x, y, r, c, m.At(x, y))
 
324
                                                return
 
325
                                        }
 
326
                                }
 
327
                        }
 
328
                }
 
329
                // Draw 1 pixel at a time.
 
330
                for y := b.Min.Y; y < b.Max.Y; y++ {
 
331
                        for x := b.Min.X; x < b.Max.X; x++ {
 
332
                                DrawMask(m, image.Rect(x, y, x+1, y+1), src, image.ZP, nil, image.ZP, Src)
 
333
                        }
 
334
                }
 
335
                check("pixel")
 
336
                // Draw 1 row at a time.
 
337
                c = color.RGBA{0, 22, 0, 255}
 
338
                src = &image.Uniform{C: c}
 
339
                for y := b.Min.Y; y < b.Max.Y; y++ {
 
340
                        DrawMask(m, image.Rect(b.Min.X, y, b.Max.X, y+1), src, image.ZP, nil, image.ZP, Src)
 
341
                }
 
342
                check("row")
 
343
                // Draw 1 column at a time.
 
344
                c = color.RGBA{0, 0, 33, 255}
 
345
                src = &image.Uniform{C: c}
 
346
                for x := b.Min.X; x < b.Max.X; x++ {
 
347
                        DrawMask(m, image.Rect(x, b.Min.Y, x+1, b.Max.Y), src, image.ZP, nil, image.ZP, Src)
 
348
                }
 
349
                check("column")
 
350
                // Draw the whole image at once.
 
351
                c = color.RGBA{44, 55, 66, 77}
 
352
                src = &image.Uniform{C: c}
 
353
                DrawMask(m, b, src, image.ZP, nil, image.ZP, Src)
 
354
                check("whole")
 
355
        }
 
356
}
 
357
 
 
358
// TestFloydSteinbergCheckerboard tests that the result of Floyd-Steinberg
 
359
// error diffusion of a uniform 50% gray source image with a black-and-white
 
360
// palette is a checkerboard pattern.
 
361
func TestFloydSteinbergCheckerboard(t *testing.T) {
 
362
        b := image.Rect(0, 0, 640, 480)
 
363
        // We can't represent 50% exactly, but 0x7fff / 0xffff is close enough.
 
364
        src := &image.Uniform{color.Gray16{0x7fff}}
 
365
        dst := image.NewPaletted(b, color.Palette{color.Black, color.White})
 
366
        FloydSteinberg.Draw(dst, b, src, image.Point{})
 
367
        nErr := 0
 
368
        for y := b.Min.Y; y < b.Max.Y; y++ {
 
369
                for x := b.Min.X; x < b.Max.X; x++ {
 
370
                        got := dst.Pix[dst.PixOffset(x, y)]
 
371
                        want := uint8(x+y) % 2
 
372
                        if got != want {
 
373
                                t.Errorf("at (%d, %d): got %d, want %d", x, y, got, want)
 
374
                                if nErr++; nErr == 10 {
 
375
                                        t.Fatal("there may be more errors")
 
376
                                }
 
377
                        }
 
378
                }
 
379
        }
 
380
}
 
381
 
 
382
// embeddedPaletted is an Image that behaves like an *image.Paletted but whose
 
383
// type is not *image.Paletted.
 
384
type embeddedPaletted struct {
 
385
        *image.Paletted
 
386
}
 
387
 
 
388
// TestPaletted tests that the drawPaletted function behaves the same
 
389
// regardless of whether dst is an *image.Paletted.
 
390
func TestPaletted(t *testing.T) {
 
391
        f, err := os.Open("../testdata/video-001.png")
 
392
        if err != nil {
 
393
                t.Fatalf("open: %v", err)
 
394
        }
 
395
        defer f.Close()
 
396
        src, err := png.Decode(f)
 
397
        if err != nil {
 
398
                t.Fatalf("decode: %v", err)
 
399
        }
 
400
        b := src.Bounds()
 
401
 
 
402
        cgaPalette := color.Palette{
 
403
                color.RGBA{0x00, 0x00, 0x00, 0xff},
 
404
                color.RGBA{0x55, 0xff, 0xff, 0xff},
 
405
                color.RGBA{0xff, 0x55, 0xff, 0xff},
 
406
                color.RGBA{0xff, 0xff, 0xff, 0xff},
 
407
        }
 
408
        drawers := map[string]Drawer{
 
409
                "src":             Src,
 
410
                "floyd-steinberg": FloydSteinberg,
 
411
        }
 
412
 
 
413
loop:
 
414
        for dName, d := range drawers {
 
415
                dst0 := image.NewPaletted(b, cgaPalette)
 
416
                dst1 := image.NewPaletted(b, cgaPalette)
 
417
                d.Draw(dst0, b, src, image.Point{})
 
418
                d.Draw(embeddedPaletted{dst1}, b, src, image.Point{})
 
419
                for y := b.Min.Y; y < b.Max.Y; y++ {
 
420
                        for x := b.Min.X; x < b.Max.X; x++ {
 
421
                                if !eq(dst0.At(x, y), dst1.At(x, y)) {
 
422
                                        t.Errorf("%s: at (%d, %d), %v versus %v",
 
423
                                                dName, x, y, dst0.At(x, y), dst1.At(x, y))
 
424
                                        continue loop
 
425
                                }
 
426
                        }
 
427
                }
 
428
        }
 
429
}