blob: 79ca3132f718f147a0bf34c0700fed58c5096405 [file] [log] [blame]
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#include "Test.h"
reed@google.com21b519d2012-10-02 17:42:15 +00008#include "SkCanvas.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +00009#include "SkColorPriv.h"
10#include "SkData.h"
reed@google.com21b519d2012-10-02 17:42:15 +000011#include "SkPaint.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000012#include "SkPicture.h"
reed@google.com21b519d2012-10-02 17:42:15 +000013#include "SkRandom.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000014#include "SkShader.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000015#include "SkStream.h"
16
reed@google.comfe7b1ed2012-11-29 21:00:39 +000017#include "SkPictureUtils.h"
18
19static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
20 bm->setConfig(SkBitmap::kARGB_8888_Config, w, h);
21 bm->allocPixels();
22 bm->eraseColor(color);
23 if (immutable) {
24 bm->setImmutable();
25 }
26}
27
28typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkPoint&);
29
30static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
31 const SkPoint& pos) {
32 canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
33}
34
35static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
36 const SkPoint& pos) {
37 SkRect r = {
38 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
39 };
40 r.offset(pos.fX, pos.fY);
41 canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
42}
43
44static void drawshader_proc(SkCanvas* canvas, const SkBitmap& bm,
45 const SkPoint& pos) {
46 SkRect r = {
47 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
48 };
49 r.offset(pos.fX, pos.fY);
50
51 SkShader* s = SkShader::CreateBitmapShader(bm,
52 SkShader::kClamp_TileMode,
53 SkShader::kClamp_TileMode);
54 SkPaint paint;
55 paint.setShader(s)->unref();
56 canvas->drawRect(r, paint);
57}
58
59// Return a picture with the bitmaps drawn at the specified positions.
60static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[],
61 int count, DrawBitmapProc proc) {
62 SkPicture* pic = new SkPicture;
63 SkCanvas* canvas = pic->beginRecording(1000, 1000);
64 for (int i = 0; i < count; ++i) {
65 proc(canvas, bm[i], pos[i]);
66 }
67 pic->endRecording();
68 return pic;
69}
70
71static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) {
72 rect->fLeft = rand.nextRangeScalar(-W, 2*W);
73 rect->fTop = rand.nextRangeScalar(-H, 2*H);
74 rect->fRight = rect->fLeft + rand.nextRangeScalar(0, W);
75 rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H);
76
77 // we integralize rect to make our tests more predictable, since Gather is
78 // a little sloppy.
79 SkIRect ir;
80 rect->round(&ir);
81 rect->set(ir);
82}
83
84// Allocate result to be large enough to hold subset, and then draw the picture
85// into it, offsetting by subset's top/left corner.
86static void draw(SkPicture* pic, const SkRect& subset, SkBitmap* result) {
87 SkIRect ir;
88 subset.roundOut(&ir);
89 int w = ir.width();
90 int h = ir.height();
91 make_bm(result, w, h, 0, false);
92
93 SkCanvas canvas(*result);
94 canvas.translate(-SkIntToScalar(ir.left()), -SkIntToScalar(ir.top()));
95 canvas.drawPicture(*pic);
96}
97
98template <typename T> int find_index(const T* array, T elem, int count) {
99 for (int i = 0; i < count; ++i) {
100 if (array[i] == elem) {
101 return i;
102 }
103 }
104 return -1;
105}
106
107// Return true if 'ref' is found in array[]
108static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) {
109 return find_index<const SkPixelRef*>(array, ref, count) >= 0;
110}
111
112// Look at each pixel in bm, and if its color appears in colors[], find the
113// corresponding value in refs[] and append that ref into array, skipping
114// duplicates of the same value.
115static void gather_from_colors(const SkBitmap& bm, SkPixelRef* const refs[],
116 int count, SkTDArray<SkPixelRef*>* array) {
117 // Since we only want to return unique values in array, when we scan we just
118 // set a bit for each index'd color found. In practice we only have a few
119 // distinct colors, so we just use an int's bits as our array. Hence the
120 // assert that count <= number-of-bits-in-our-int.
121 SkASSERT((unsigned)count <= 32);
122 uint32_t bitarray = 0;
123
124 SkAutoLockPixels alp(bm);
125
126 for (int y = 0; y < bm.height(); ++y) {
127 for (int x = 0; x < bm.width(); ++x) {
128 SkPMColor pmc = *bm.getAddr32(x, y);
129 // the only good case where the color is not found would be if
130 // the color is transparent, meaning no bitmap was drawn in that
131 // pixel.
132 if (pmc) {
133 int index = SkGetPackedR32(pmc);
134 SkASSERT(SkGetPackedG32(pmc) == index);
135 SkASSERT(SkGetPackedB32(pmc) == index);
136 SkASSERT(index < count);
137 bitarray |= 1 << index;
138 }
139 }
140 }
141
142 for (int i = 0; i < count; ++i) {
143 if (bitarray & (1 << i)) {
144 *array->append() = refs[i];
145 }
146 }
147}
148
149static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
150 const int IW = 8;
151 const int IH = IW;
152 const SkScalar W = SkIntToScalar(IW);
153 const SkScalar H = W;
154
155 static const int N = 4;
156 SkBitmap bm[N];
157 SkPMColor pmcolors[N];
158 SkPixelRef* refs[N];
159
160 const SkPoint pos[] = {
161 { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
162 };
163
164 // Our convention is that the color components contain the index of their
165 // corresponding bitmap/pixelref
166 for (int i = 0; i < N; ++i) {
167 make_bm(&bm[i], IW, IH, SkColorSetARGB(0xFF, i, i, i), true);
168 refs[i] = bm[i].pixelRef();
169 }
170
171 static const DrawBitmapProc procs[] = {
172 drawbitmap_proc, drawbitmaprect_proc, drawshader_proc
173 };
174
175 SkRandom rand;
176 for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) {
177 SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k]));
178
179 // quick check for a small piece of each quadrant, which should just
180 // contain 1 bitmap.
181 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
182 SkRect r;
183 r.set(2, 2, W - 2, H - 2);
184 r.offset(pos[i].fX, pos[i].fY);
185 SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r));
186 REPORTER_ASSERT(reporter, data);
187 int count = data->size() / sizeof(SkPixelRef*);
188 REPORTER_ASSERT(reporter, 1 == count);
189 REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]);
190 }
191
192 // Test a bunch of random (mostly) rects, and compare the gather results
193 // with a deduced list of refs by looking at the colors drawn.
194 for (int j = 0; j < 100; ++j) {
195 SkRect r;
196 rand_rect(&r, rand, 2*W, 2*H);
197
198 SkBitmap result;
199 draw(pic, r, &result);
200 SkTDArray<SkPixelRef*> array;
201
202 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
203 size_t dataSize = data ? data->size() : 0;
204 int gatherCount = dataSize / sizeof(SkPixelRef*);
205 SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
206 SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
207 SkAutoDataUnref adu(data);
208
209 gather_from_colors(result, refs, N, &array);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000210
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000211 /*
212 * GatherPixelRefs is conservative, so it can return more bitmaps
213 * that we actually can see (usually because of conservative bounds
214 * inflation for antialiasing). Thus our check here is only that
215 * Gather didn't miss any that we actually saw. Even that isn't
216 * a strict requirement on Gather, which is meant to be quick and
217 * only mostly-correct, but at the moment this test should work.
218 */
219 for (int i = 0; i < array.count(); ++i) {
220 bool found = find(gatherRefs, array[i], gatherCount);
221 REPORTER_ASSERT(reporter, found);
222#if 0
223 // enable this block of code to debug failures, as it will rerun
224 // the case that failed.
225 if (!found) {
226 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
227 size_t dataSize = data ? data->size() : 0;
228 }
229#endif
230 }
231 }
232 }
233}
234
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000235#ifdef SK_DEBUG
236// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
237// run in debug mode.
238static void test_deleting_empty_playback() {
239 SkPicture picture;
240 // Creates an SkPictureRecord
241 picture.beginRecording(0, 0);
242 // Turns that into an SkPicturePlayback
243 picture.endRecording();
244 // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord
245 picture.beginRecording(0, 0);
246}
247
248// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
249static void test_serializing_empty_picture() {
250 SkPicture picture;
251 picture.beginRecording(0, 0);
252 picture.endRecording();
253 SkDynamicMemoryWStream stream;
254 picture.serialize(&stream);
255}
256#endif
257
reed@google.com21b519d2012-10-02 17:42:15 +0000258static void rand_op(SkCanvas* canvas, SkRandom& rand) {
259 SkPaint paint;
260 SkRect rect = SkRect::MakeWH(50, 50);
261
262 SkScalar unit = rand.nextUScalar1();
263 if (unit <= 0.3) {
264// SkDebugf("save\n");
265 canvas->save();
266 } else if (unit <= 0.6) {
267// SkDebugf("restore\n");
268 canvas->restore();
269 } else if (unit <= 0.9) {
270// SkDebugf("clip\n");
271 canvas->clipRect(rect);
272 } else {
273// SkDebugf("draw\n");
274 canvas->drawPaint(paint);
275 }
276}
277
278static void test_peephole(skiatest::Reporter* reporter) {
279 SkRandom rand;
280
281 for (int j = 0; j < 100; j++) {
282 SkRandom rand2(rand.getSeed()); // remember the seed
283
284 SkPicture picture;
285 SkCanvas* canvas = picture.beginRecording(100, 100);
286
287 for (int i = 0; i < 1000; ++i) {
288 rand_op(canvas, rand);
289 }
290 picture.endRecording();
291 }
292
293 {
294 SkPicture picture;
295 SkCanvas* canvas = picture.beginRecording(100, 100);
296 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000297
reed@google.com21b519d2012-10-02 17:42:15 +0000298 for (int i = 0; i < 100; ++i) {
299 canvas->save();
300 }
301 while (canvas->getSaveCount() > 1) {
302 canvas->clipRect(rect);
303 canvas->restore();
304 }
305 picture.endRecording();
306 }
307}
308
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000309static void TestPicture(skiatest::Reporter* reporter) {
310#ifdef SK_DEBUG
311 test_deleting_empty_playback();
312 test_serializing_empty_picture();
313#endif
reed@google.com21b519d2012-10-02 17:42:15 +0000314 test_peephole(reporter);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000315 test_gatherpixelrefs(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000316}
317
318#include "TestClassDef.h"
reed@google.com21b519d2012-10-02 17:42:15 +0000319DEFINE_TESTCLASS("Pictures", PictureTestClass, TestPicture)