1// Copyright 2011 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
5package draw
6
7import (
8	"image"
9	"image/color"
10	"reflect"
11	"testing"
12)
13
14const (
15	dstw, dsth = 640, 480
16	srcw, srch = 400, 300
17)
18
19var palette = color.Palette{
20	color.Black,
21	color.White,
22}
23
24// bench benchmarks drawing src and mask images onto a dst image with the
25// given op and the color models to create those images from.
26// The created images' pixels are initialized to non-zero values.
27func bench(b *testing.B, dcm, scm, mcm color.Model, op Op) {
28	b.StopTimer()
29
30	var dst Image
31	switch dcm {
32	case color.RGBAModel:
33		dst1 := image.NewRGBA(image.Rect(0, 0, dstw, dsth))
34		for y := 0; y < dsth; y++ {
35			for x := 0; x < dstw; x++ {
36				dst1.SetRGBA(x, y, color.RGBA{
37					uint8(5 * x % 0x100),
38					uint8(7 * y % 0x100),
39					uint8((7*x + 5*y) % 0x100),
40					0xff,
41				})
42			}
43		}
44		dst = dst1
45	case color.RGBA64Model:
46		dst1 := image.NewRGBA64(image.Rect(0, 0, dstw, dsth))
47		for y := 0; y < dsth; y++ {
48			for x := 0; x < dstw; x++ {
49				dst1.SetRGBA64(x, y, color.RGBA64{
50					uint16(53 * x % 0x10000),
51					uint16(59 * y % 0x10000),
52					uint16((59*x + 53*y) % 0x10000),
53					0xffff,
54				})
55			}
56		}
57		dst = dst1
58	default:
59		// The == operator isn't defined on a color.Palette (a slice), so we
60		// use reflection.
61		if reflect.DeepEqual(dcm, palette) {
62			dst1 := image.NewPaletted(image.Rect(0, 0, dstw, dsth), palette)
63			for y := 0; y < dsth; y++ {
64				for x := 0; x < dstw; x++ {
65					dst1.SetColorIndex(x, y, uint8(x^y)&1)
66				}
67			}
68			dst = dst1
69		} else {
70			b.Fatal("unknown destination color model", dcm)
71		}
72	}
73
74	var src image.Image
75	switch scm {
76	case nil:
77		src = &image.Uniform{C: color.RGBA{0x11, 0x22, 0x33, 0x44}}
78	case color.CMYKModel:
79		src1 := image.NewCMYK(image.Rect(0, 0, srcw, srch))
80		for y := 0; y < srch; y++ {
81			for x := 0; x < srcw; x++ {
82				src1.SetCMYK(x, y, color.CMYK{
83					uint8(13 * x % 0x100),
84					uint8(11 * y % 0x100),
85					uint8((11*x + 13*y) % 0x100),
86					uint8((31*x + 37*y) % 0x100),
87				})
88			}
89		}
90		src = src1
91	case color.GrayModel:
92		src1 := image.NewGray(image.Rect(0, 0, srcw, srch))
93		for y := 0; y < srch; y++ {
94			for x := 0; x < srcw; x++ {
95				src1.SetGray(x, y, color.Gray{
96					uint8((11*x + 13*y) % 0x100),
97				})
98			}
99		}
100		src = src1
101	case color.RGBAModel:
102		src1 := image.NewRGBA(image.Rect(0, 0, srcw, srch))
103		for y := 0; y < srch; y++ {
104			for x := 0; x < srcw; x++ {
105				src1.SetRGBA(x, y, color.RGBA{
106					uint8(13 * x % 0x80),
107					uint8(11 * y % 0x80),
108					uint8((11*x + 13*y) % 0x80),
109					0x7f,
110				})
111			}
112		}
113		src = src1
114	case color.RGBA64Model:
115		src1 := image.NewRGBA64(image.Rect(0, 0, srcw, srch))
116		for y := 0; y < srch; y++ {
117			for x := 0; x < srcw; x++ {
118				src1.SetRGBA64(x, y, color.RGBA64{
119					uint16(103 * x % 0x8000),
120					uint16(101 * y % 0x8000),
121					uint16((101*x + 103*y) % 0x8000),
122					0x7fff,
123				})
124			}
125		}
126		src = src1
127	case color.NRGBAModel:
128		src1 := image.NewNRGBA(image.Rect(0, 0, srcw, srch))
129		for y := 0; y < srch; y++ {
130			for x := 0; x < srcw; x++ {
131				src1.SetNRGBA(x, y, color.NRGBA{
132					uint8(13 * x % 0x100),
133					uint8(11 * y % 0x100),
134					uint8((11*x + 13*y) % 0x100),
135					0x7f,
136				})
137			}
138		}
139		src = src1
140	case color.YCbCrModel:
141		yy := make([]uint8, srcw*srch)
142		cb := make([]uint8, srcw*srch)
143		cr := make([]uint8, srcw*srch)
144		for i := range yy {
145			yy[i] = uint8(3 * i % 0x100)
146			cb[i] = uint8(5 * i % 0x100)
147			cr[i] = uint8(7 * i % 0x100)
148		}
149		src = &image.YCbCr{
150			Y:              yy,
151			Cb:             cb,
152			Cr:             cr,
153			YStride:        srcw,
154			CStride:        srcw,
155			SubsampleRatio: image.YCbCrSubsampleRatio444,
156			Rect:           image.Rect(0, 0, srcw, srch),
157		}
158	default:
159		b.Fatal("unknown source color model", scm)
160	}
161
162	var mask image.Image
163	switch mcm {
164	case nil:
165		// No-op.
166	case color.AlphaModel:
167		mask1 := image.NewAlpha(image.Rect(0, 0, srcw, srch))
168		for y := 0; y < srch; y++ {
169			for x := 0; x < srcw; x++ {
170				a := uint8((23*x + 29*y) % 0x100)
171				// Glyph masks are typically mostly zero,
172				// so we only set a quarter of mask1's pixels.
173				if a >= 0xc0 {
174					mask1.SetAlpha(x, y, color.Alpha{a})
175				}
176			}
177		}
178		mask = mask1
179	default:
180		b.Fatal("unknown mask color model", mcm)
181	}
182
183	b.StartTimer()
184	for i := 0; i < b.N; i++ {
185		// Scatter the destination rectangle to draw into.
186		x := 3 * i % (dstw - srcw)
187		y := 7 * i % (dsth - srch)
188
189		DrawMask(dst, dst.Bounds().Add(image.Pt(x, y)), src, image.ZP, mask, image.ZP, op)
190	}
191}
192
193// The BenchmarkFoo functions exercise a drawFoo fast-path function in draw.go.
194
195func BenchmarkFillOver(b *testing.B) {
196	bench(b, color.RGBAModel, nil, nil, Over)
197}
198
199func BenchmarkFillSrc(b *testing.B) {
200	bench(b, color.RGBAModel, nil, nil, Src)
201}
202
203func BenchmarkCopyOver(b *testing.B) {
204	bench(b, color.RGBAModel, color.RGBAModel, nil, Over)
205}
206
207func BenchmarkCopySrc(b *testing.B) {
208	bench(b, color.RGBAModel, color.RGBAModel, nil, Src)
209}
210
211func BenchmarkNRGBAOver(b *testing.B) {
212	bench(b, color.RGBAModel, color.NRGBAModel, nil, Over)
213}
214
215func BenchmarkNRGBASrc(b *testing.B) {
216	bench(b, color.RGBAModel, color.NRGBAModel, nil, Src)
217}
218
219func BenchmarkYCbCr(b *testing.B) {
220	bench(b, color.RGBAModel, color.YCbCrModel, nil, Over)
221}
222
223func BenchmarkGray(b *testing.B) {
224	bench(b, color.RGBAModel, color.GrayModel, nil, Over)
225}
226
227func BenchmarkCMYK(b *testing.B) {
228	bench(b, color.RGBAModel, color.CMYKModel, nil, Over)
229}
230
231func BenchmarkGlyphOver(b *testing.B) {
232	bench(b, color.RGBAModel, nil, color.AlphaModel, Over)
233}
234
235func BenchmarkRGBA(b *testing.B) {
236	bench(b, color.RGBAModel, color.RGBA64Model, nil, Src)
237}
238
239func BenchmarkPaletted(b *testing.B) {
240	bench(b, palette, color.RGBAModel, nil, Src)
241}
242
243// The BenchmarkGenericFoo functions exercise the generic, slow-path code.
244
245func BenchmarkGenericOver(b *testing.B) {
246	bench(b, color.RGBA64Model, color.RGBA64Model, nil, Over)
247}
248
249func BenchmarkGenericMaskOver(b *testing.B) {
250	bench(b, color.RGBA64Model, color.RGBA64Model, color.AlphaModel, Over)
251}
252
253func BenchmarkGenericSrc(b *testing.B) {
254	bench(b, color.RGBA64Model, color.RGBA64Model, nil, Src)
255}
256
257func BenchmarkGenericMaskSrc(b *testing.B) {
258	bench(b, color.RGBA64Model, color.RGBA64Model, color.AlphaModel, Src)
259}
260