blob: d14cf98737285c52274a3421fd004dc036367718 [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];
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000157 SkPixelRef* refs[N];
158
159 const SkPoint pos[] = {
160 { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
161 };
162
163 // Our convention is that the color components contain the index of their
164 // corresponding bitmap/pixelref
165 for (int i = 0; i < N; ++i) {
166 make_bm(&bm[i], IW, IH, SkColorSetARGB(0xFF, i, i, i), true);
167 refs[i] = bm[i].pixelRef();
168 }
169
170 static const DrawBitmapProc procs[] = {
171 drawbitmap_proc, drawbitmaprect_proc, drawshader_proc
172 };
173
174 SkRandom rand;
175 for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) {
176 SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k]));
177
178 // quick check for a small piece of each quadrant, which should just
179 // contain 1 bitmap.
180 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
181 SkRect r;
182 r.set(2, 2, W - 2, H - 2);
183 r.offset(pos[i].fX, pos[i].fY);
184 SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r));
185 REPORTER_ASSERT(reporter, data);
186 int count = data->size() / sizeof(SkPixelRef*);
187 REPORTER_ASSERT(reporter, 1 == count);
188 REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]);
189 }
190
191 // Test a bunch of random (mostly) rects, and compare the gather results
192 // with a deduced list of refs by looking at the colors drawn.
193 for (int j = 0; j < 100; ++j) {
194 SkRect r;
195 rand_rect(&r, rand, 2*W, 2*H);
196
197 SkBitmap result;
198 draw(pic, r, &result);
199 SkTDArray<SkPixelRef*> array;
200
201 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
202 size_t dataSize = data ? data->size() : 0;
203 int gatherCount = dataSize / sizeof(SkPixelRef*);
204 SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
205 SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
206 SkAutoDataUnref adu(data);
207
208 gather_from_colors(result, refs, N, &array);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000209
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000210 /*
211 * GatherPixelRefs is conservative, so it can return more bitmaps
212 * that we actually can see (usually because of conservative bounds
213 * inflation for antialiasing). Thus our check here is only that
214 * Gather didn't miss any that we actually saw. Even that isn't
215 * a strict requirement on Gather, which is meant to be quick and
216 * only mostly-correct, but at the moment this test should work.
217 */
218 for (int i = 0; i < array.count(); ++i) {
219 bool found = find(gatherRefs, array[i], gatherCount);
220 REPORTER_ASSERT(reporter, found);
221#if 0
222 // enable this block of code to debug failures, as it will rerun
223 // the case that failed.
224 if (!found) {
225 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
226 size_t dataSize = data ? data->size() : 0;
227 }
228#endif
229 }
230 }
231 }
232}
233
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000234#ifdef SK_DEBUG
235// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
236// run in debug mode.
237static void test_deleting_empty_playback() {
238 SkPicture picture;
239 // Creates an SkPictureRecord
240 picture.beginRecording(0, 0);
241 // Turns that into an SkPicturePlayback
242 picture.endRecording();
243 // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord
244 picture.beginRecording(0, 0);
245}
246
247// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
248static void test_serializing_empty_picture() {
249 SkPicture picture;
250 picture.beginRecording(0, 0);
251 picture.endRecording();
252 SkDynamicMemoryWStream stream;
253 picture.serialize(&stream);
254}
255#endif
256
reed@google.com21b519d2012-10-02 17:42:15 +0000257static void rand_op(SkCanvas* canvas, SkRandom& rand) {
258 SkPaint paint;
259 SkRect rect = SkRect::MakeWH(50, 50);
260
261 SkScalar unit = rand.nextUScalar1();
262 if (unit <= 0.3) {
263// SkDebugf("save\n");
264 canvas->save();
265 } else if (unit <= 0.6) {
266// SkDebugf("restore\n");
267 canvas->restore();
268 } else if (unit <= 0.9) {
269// SkDebugf("clip\n");
270 canvas->clipRect(rect);
271 } else {
272// SkDebugf("draw\n");
273 canvas->drawPaint(paint);
274 }
275}
276
277static void test_peephole(skiatest::Reporter* reporter) {
278 SkRandom rand;
279
280 for (int j = 0; j < 100; j++) {
281 SkRandom rand2(rand.getSeed()); // remember the seed
282
283 SkPicture picture;
284 SkCanvas* canvas = picture.beginRecording(100, 100);
285
286 for (int i = 0; i < 1000; ++i) {
287 rand_op(canvas, rand);
288 }
289 picture.endRecording();
290 }
291
292 {
293 SkPicture picture;
294 SkCanvas* canvas = picture.beginRecording(100, 100);
295 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000296
reed@google.com21b519d2012-10-02 17:42:15 +0000297 for (int i = 0; i < 100; ++i) {
298 canvas->save();
299 }
300 while (canvas->getSaveCount() > 1) {
301 canvas->clipRect(rect);
302 canvas->restore();
303 }
304 picture.endRecording();
305 }
306}
307
scroggo@google.com4b90b112012-12-04 15:08:56 +0000308#ifndef SK_DEBUG
309// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
310// should never do this.
311static void test_bad_bitmap() {
312 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
313 // fail.
314 SkBitmap bm;
315 bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
316 SkPicture picture;
317 SkCanvas* recordingCanvas = picture.beginRecording(100, 100);
318 recordingCanvas->drawBitmap(bm, 0, 0);
319 picture.endRecording();
320
321 SkCanvas canvas;
322 canvas.drawPicture(picture);
323}
324#endif
325
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000326#include "SkData.h"
327#include "SkImageRef_GlobalPool.h"
328// Class to test SkPixelRef::onRefEncodedData, since there are currently no implementations in skia.
329class SkDataImageRef : public SkImageRef_GlobalPool {
330
331public:
332 SkDataImageRef(SkMemoryStream* stream)
333 : SkImageRef_GlobalPool(stream, SkBitmap::kNo_Config) {
334 SkASSERT(stream != NULL);
335 fData = stream->copyToData();
336 this->setImmutable();
337 }
338
339 ~SkDataImageRef() {
340 fData->unref();
341 }
342
343 virtual SkData* onRefEncodedData() SK_OVERRIDE {
344 fData->ref();
345 return fData;
346 }
347
348private:
349 SkData* fData;
350};
351
352#include "SkImageEncoder.h"
353
354static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) {
355 return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100);
356}
357
358static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
359 SkPicture picture;
360 SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height());
361 canvas->drawBitmap(bitmap, 0, 0);
362 SkDynamicMemoryWStream wStream;
363 picture.serialize(&wStream, &PNGEncodeBitmapToStream);
364 return wStream.copyToData();
365}
366
367static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
368 // Create a bitmap that will be encoded.
369 SkBitmap original;
370 make_bm(&original, 100, 100, SK_ColorBLUE, true);
371 SkDynamicMemoryWStream wStream;
372 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
373 return;
374 }
375 SkAutoDataUnref data(wStream.copyToData());
376 SkMemoryStream memStream;
377 memStream.setData(data);
378
379 // Use the encoded bitmap as the data for an image ref.
380 SkBitmap bm;
381 SkAutoTUnref<SkDataImageRef> imageRef(SkNEW_ARGS(SkDataImageRef, (&memStream)));
382 imageRef->getInfo(&bm);
383 bm.setPixelRef(imageRef);
384
385 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
386 // Flattening original will follow the old path of performing an encode, while flattening bm
387 // will use the already encoded data.
388 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
389 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
390 REPORTER_ASSERT(reporter, picture1->equals(picture2));
391}
392
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000393static void TestPicture(skiatest::Reporter* reporter) {
394#ifdef SK_DEBUG
395 test_deleting_empty_playback();
396 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +0000397#else
398 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000399#endif
reed@google.com21b519d2012-10-02 17:42:15 +0000400 test_peephole(reporter);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000401 test_gatherpixelrefs(reporter);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000402 test_bitmap_with_encoded_data(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000403}
404
405#include "TestClassDef.h"
reed@google.com21b519d2012-10-02 17:42:15 +0000406DEFINE_TESTCLASS("Pictures", PictureTestClass, TestPicture)