blob: 9ef4bb06bdba2551195a1051e6c47bab19220edf [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"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00008#include "SkBitmapDevice.h"
reed@google.com21b519d2012-10-02 17:42:15 +00009#include "SkCanvas.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000010#include "SkColorPriv.h"
11#include "SkData.h"
scroggo@google.com49ce11b2013-04-25 18:29:32 +000012#include "SkError.h"
reed@google.com21b519d2012-10-02 17:42:15 +000013#include "SkPaint.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000014#include "SkPicture.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000015#include "SkPictureUtils.h"
reed@google.com21b519d2012-10-02 17:42:15 +000016#include "SkRandom.h"
reed@google.com72aa79c2013-01-24 18:27:42 +000017#include "SkRRect.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000018#include "SkShader.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000019#include "SkStream.h"
20
reed@google.comfe7b1ed2012-11-29 21:00:39 +000021
22static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
23 bm->setConfig(SkBitmap::kARGB_8888_Config, w, h);
24 bm->allocPixels();
25 bm->eraseColor(color);
26 if (immutable) {
27 bm->setImmutable();
28 }
29}
30
31typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkPoint&);
32
33static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
34 const SkPoint& pos) {
35 canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
36}
37
38static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
39 const SkPoint& pos) {
40 SkRect r = {
41 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
42 };
43 r.offset(pos.fX, pos.fY);
44 canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
45}
46
47static void drawshader_proc(SkCanvas* canvas, const SkBitmap& bm,
48 const SkPoint& pos) {
49 SkRect r = {
50 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
51 };
52 r.offset(pos.fX, pos.fY);
53
54 SkShader* s = SkShader::CreateBitmapShader(bm,
55 SkShader::kClamp_TileMode,
56 SkShader::kClamp_TileMode);
57 SkPaint paint;
58 paint.setShader(s)->unref();
59 canvas->drawRect(r, paint);
reed@google.com72aa79c2013-01-24 18:27:42 +000060 canvas->drawOval(r, paint);
61 SkRRect rr;
62 rr.setRectXY(r, 10, 10);
63 canvas->drawRRect(rr, paint);
reed@google.comfe7b1ed2012-11-29 21:00:39 +000064}
65
66// Return a picture with the bitmaps drawn at the specified positions.
67static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[],
68 int count, DrawBitmapProc proc) {
69 SkPicture* pic = new SkPicture;
70 SkCanvas* canvas = pic->beginRecording(1000, 1000);
71 for (int i = 0; i < count; ++i) {
72 proc(canvas, bm[i], pos[i]);
73 }
74 pic->endRecording();
75 return pic;
76}
77
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000078static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) {
reed@google.comfe7b1ed2012-11-29 21:00:39 +000079 rect->fLeft = rand.nextRangeScalar(-W, 2*W);
80 rect->fTop = rand.nextRangeScalar(-H, 2*H);
81 rect->fRight = rect->fLeft + rand.nextRangeScalar(0, W);
82 rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H);
83
84 // we integralize rect to make our tests more predictable, since Gather is
85 // a little sloppy.
86 SkIRect ir;
87 rect->round(&ir);
88 rect->set(ir);
89}
90
91// Allocate result to be large enough to hold subset, and then draw the picture
92// into it, offsetting by subset's top/left corner.
93static void draw(SkPicture* pic, const SkRect& subset, SkBitmap* result) {
94 SkIRect ir;
95 subset.roundOut(&ir);
96 int w = ir.width();
97 int h = ir.height();
98 make_bm(result, w, h, 0, false);
99
100 SkCanvas canvas(*result);
101 canvas.translate(-SkIntToScalar(ir.left()), -SkIntToScalar(ir.top()));
102 canvas.drawPicture(*pic);
103}
104
105template <typename T> int find_index(const T* array, T elem, int count) {
106 for (int i = 0; i < count; ++i) {
107 if (array[i] == elem) {
108 return i;
109 }
110 }
111 return -1;
112}
113
114// Return true if 'ref' is found in array[]
115static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) {
116 return find_index<const SkPixelRef*>(array, ref, count) >= 0;
117}
118
119// Look at each pixel in bm, and if its color appears in colors[], find the
120// corresponding value in refs[] and append that ref into array, skipping
121// duplicates of the same value.
122static void gather_from_colors(const SkBitmap& bm, SkPixelRef* const refs[],
123 int count, SkTDArray<SkPixelRef*>* array) {
124 // Since we only want to return unique values in array, when we scan we just
125 // set a bit for each index'd color found. In practice we only have a few
126 // distinct colors, so we just use an int's bits as our array. Hence the
127 // assert that count <= number-of-bits-in-our-int.
128 SkASSERT((unsigned)count <= 32);
129 uint32_t bitarray = 0;
130
131 SkAutoLockPixels alp(bm);
132
133 for (int y = 0; y < bm.height(); ++y) {
134 for (int x = 0; x < bm.width(); ++x) {
135 SkPMColor pmc = *bm.getAddr32(x, y);
136 // the only good case where the color is not found would be if
137 // the color is transparent, meaning no bitmap was drawn in that
138 // pixel.
139 if (pmc) {
bsalomon@google.comc3d753e2013-01-08 17:24:44 +0000140 uint32_t index = SkGetPackedR32(pmc);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000141 SkASSERT(SkGetPackedG32(pmc) == index);
142 SkASSERT(SkGetPackedB32(pmc) == index);
bsalomon@google.com5f429b02013-01-08 18:42:20 +0000143 SkASSERT(static_cast<int>(index) < count);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000144 bitarray |= 1 << index;
145 }
146 }
147 }
148
149 for (int i = 0; i < count; ++i) {
150 if (bitarray & (1 << i)) {
151 *array->append() = refs[i];
152 }
153 }
154}
155
156static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
157 const int IW = 8;
158 const int IH = IW;
159 const SkScalar W = SkIntToScalar(IW);
160 const SkScalar H = W;
161
162 static const int N = 4;
163 SkBitmap bm[N];
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000164 SkPixelRef* refs[N];
165
166 const SkPoint pos[] = {
167 { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
168 };
169
170 // Our convention is that the color components contain the index of their
171 // corresponding bitmap/pixelref
172 for (int i = 0; i < N; ++i) {
173 make_bm(&bm[i], IW, IH, SkColorSetARGB(0xFF, i, i, i), true);
174 refs[i] = bm[i].pixelRef();
175 }
176
177 static const DrawBitmapProc procs[] = {
178 drawbitmap_proc, drawbitmaprect_proc, drawshader_proc
179 };
180
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000181 SkRandom rand;
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000182 for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) {
183 SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k]));
184
tomhudson@google.com381010e2013-10-24 11:12:47 +0000185 REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000186 // quick check for a small piece of each quadrant, which should just
187 // contain 1 bitmap.
188 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
189 SkRect r;
190 r.set(2, 2, W - 2, H - 2);
191 r.offset(pos[i].fX, pos[i].fY);
192 SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r));
193 REPORTER_ASSERT(reporter, data);
commit-bot@chromium.orgfe433c12013-07-09 16:04:32 +0000194 if (data) {
robertphillips@google.come9cd27d2013-10-16 17:48:11 +0000195 int count = static_cast<int>(data->size() / sizeof(SkPixelRef*));
commit-bot@chromium.orgfe433c12013-07-09 16:04:32 +0000196 REPORTER_ASSERT(reporter, 1 == count);
197 REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]);
198 }
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000199 }
200
201 // Test a bunch of random (mostly) rects, and compare the gather results
202 // with a deduced list of refs by looking at the colors drawn.
203 for (int j = 0; j < 100; ++j) {
204 SkRect r;
205 rand_rect(&r, rand, 2*W, 2*H);
206
207 SkBitmap result;
208 draw(pic, r, &result);
209 SkTDArray<SkPixelRef*> array;
210
211 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
212 size_t dataSize = data ? data->size() : 0;
robertphillips@google.come9cd27d2013-10-16 17:48:11 +0000213 int gatherCount = static_cast<int>(dataSize / sizeof(SkPixelRef*));
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000214 SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
215 SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
216 SkAutoDataUnref adu(data);
217
218 gather_from_colors(result, refs, N, &array);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000219
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000220 /*
221 * GatherPixelRefs is conservative, so it can return more bitmaps
222 * that we actually can see (usually because of conservative bounds
223 * inflation for antialiasing). Thus our check here is only that
224 * Gather didn't miss any that we actually saw. Even that isn't
225 * a strict requirement on Gather, which is meant to be quick and
226 * only mostly-correct, but at the moment this test should work.
227 */
228 for (int i = 0; i < array.count(); ++i) {
229 bool found = find(gatherRefs, array[i], gatherCount);
230 REPORTER_ASSERT(reporter, found);
231#if 0
232 // enable this block of code to debug failures, as it will rerun
233 // the case that failed.
234 if (!found) {
235 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
236 size_t dataSize = data ? data->size() : 0;
237 }
238#endif
239 }
240 }
241 }
242}
243
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000244#ifdef SK_DEBUG
245// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
246// run in debug mode.
247static void test_deleting_empty_playback() {
248 SkPicture picture;
249 // Creates an SkPictureRecord
250 picture.beginRecording(0, 0);
251 // Turns that into an SkPicturePlayback
252 picture.endRecording();
253 // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord
254 picture.beginRecording(0, 0);
255}
256
257// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
258static void test_serializing_empty_picture() {
259 SkPicture picture;
260 picture.beginRecording(0, 0);
261 picture.endRecording();
262 SkDynamicMemoryWStream stream;
263 picture.serialize(&stream);
264}
265#endif
266
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000267static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +0000268 SkPaint paint;
269 SkRect rect = SkRect::MakeWH(50, 50);
270
271 SkScalar unit = rand.nextUScalar1();
272 if (unit <= 0.3) {
273// SkDebugf("save\n");
274 canvas->save();
275 } else if (unit <= 0.6) {
276// SkDebugf("restore\n");
277 canvas->restore();
278 } else if (unit <= 0.9) {
279// SkDebugf("clip\n");
280 canvas->clipRect(rect);
281 } else {
282// SkDebugf("draw\n");
283 canvas->drawPaint(paint);
284 }
285}
286
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000287static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000288 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000289
290 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000291 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000292
293 SkPicture picture;
294 SkCanvas* canvas = picture.beginRecording(100, 100);
295
296 for (int i = 0; i < 1000; ++i) {
297 rand_op(canvas, rand);
298 }
299 picture.endRecording();
jvanverth@google.comc490f802013-03-04 13:56:38 +0000300
301 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000302 }
303
304 {
305 SkPicture picture;
306 SkCanvas* canvas = picture.beginRecording(100, 100);
307 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000308
reed@google.com21b519d2012-10-02 17:42:15 +0000309 for (int i = 0; i < 100; ++i) {
310 canvas->save();
311 }
312 while (canvas->getSaveCount() > 1) {
313 canvas->clipRect(rect);
314 canvas->restore();
315 }
316 picture.endRecording();
317 }
318}
319
scroggo@google.com4b90b112012-12-04 15:08:56 +0000320#ifndef SK_DEBUG
321// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
322// should never do this.
323static void test_bad_bitmap() {
324 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
325 // fail.
326 SkBitmap bm;
327 bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
328 SkPicture picture;
329 SkCanvas* recordingCanvas = picture.beginRecording(100, 100);
330 recordingCanvas->drawBitmap(bm, 0, 0);
331 picture.endRecording();
332
333 SkCanvas canvas;
334 canvas.drawPicture(picture);
335}
336#endif
337
reed@google.com398337b2013-12-11 21:22:39 +0000338#include "SkData.h"
339#include "SkImageRef_GlobalPool.h"
340// Class to test SkPixelRef::onRefEncodedData, since there are currently no implementations in skia.
341class SkDataImageRef : public SkImageRef_GlobalPool {
342
343public:
344 SkDataImageRef(SkMemoryStream* stream)
345 : SkImageRef_GlobalPool(stream, SkBitmap::kNo_Config) {
346 SkASSERT(stream != NULL);
347 fData = stream->copyToData();
348 this->setImmutable();
349 }
350
351 ~SkDataImageRef() {
352 fData->unref();
353 }
354
355 virtual SkData* onRefEncodedData() SK_OVERRIDE {
356 fData->ref();
357 return fData;
358 }
359
360private:
361 SkData* fData;
362};
363
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000364#include "SkImageEncoder.h"
365
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000366static SkData* encode_bitmap_to_data(size_t* offset, const SkBitmap& bm) {
367 *offset = 0;
368 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000369}
370
371static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
372 SkPicture picture;
373 SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height());
374 canvas->drawBitmap(bitmap, 0, 0);
375 SkDynamicMemoryWStream wStream;
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000376 picture.serialize(&wStream, &encode_bitmap_to_data);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000377 return wStream.copyToData();
378}
379
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000380struct ErrorContext {
381 int fErrors;
382 skiatest::Reporter* fReporter;
383};
384
385static void assert_one_parse_error_cb(SkError error, void* context) {
386 ErrorContext* errorContext = static_cast<ErrorContext*>(context);
387 errorContext->fErrors++;
388 // This test only expects one error, and that is a kParseError. If there are others,
389 // there is some unknown problem.
390 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
391 "This threw more errors than expected.");
392 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
393 SkGetLastErrorString());
394}
395
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000396static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
397 // Create a bitmap that will be encoded.
398 SkBitmap original;
399 make_bm(&original, 100, 100, SK_ColorBLUE, true);
400 SkDynamicMemoryWStream wStream;
401 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
402 return;
403 }
404 SkAutoDataUnref data(wStream.copyToData());
reed@google.com398337b2013-12-11 21:22:39 +0000405 SkMemoryStream memStream;
406 memStream.setData(data);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000407
reed@google.com398337b2013-12-11 21:22:39 +0000408 // Use the encoded bitmap as the data for an image ref.
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000409 SkBitmap bm;
reed@google.com398337b2013-12-11 21:22:39 +0000410 SkAutoTUnref<SkDataImageRef> imageRef(SkNEW_ARGS(SkDataImageRef, (&memStream)));
411 imageRef->getInfo(&bm);
412 bm.setPixelRef(imageRef);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000413
414 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
415 // Flattening original will follow the old path of performing an encode, while flattening bm
416 // will use the already encoded data.
417 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
418 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
419 REPORTER_ASSERT(reporter, picture1->equals(picture2));
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000420 // Now test that a parse error was generated when trying to create a new SkPicture without
421 // providing a function to decode the bitmap.
422 ErrorContext context;
423 context.fErrors = 0;
424 context.fReporter = reporter;
425 SkSetErrorCallback(assert_one_parse_error_cb, &context);
426 SkMemoryStream pictureStream(picture1);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000427 SkClearLastError();
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000428 SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
429 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000430 SkClearLastError();
431 SkSetErrorCallback(NULL, NULL);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000432}
433
junov@chromium.org94f20dc2013-01-28 21:04:44 +0000434static void test_clone_empty(skiatest::Reporter* reporter) {
435 // This is a regression test for crbug.com/172062
436 // Before the fix, we used to crash accessing a null pointer when we
437 // had a picture with no paints. This test passes by not crashing.
438 {
439 SkPicture picture;
440 picture.beginRecording(1, 1);
441 picture.endRecording();
442 SkPicture* destPicture = picture.clone();
443 REPORTER_ASSERT(reporter, NULL != destPicture);
444 destPicture->unref();
445 }
446 {
447 // Test without call to endRecording
448 SkPicture picture;
449 picture.beginRecording(1, 1);
450 SkPicture* destPicture = picture.clone();
451 REPORTER_ASSERT(reporter, NULL != destPicture);
452 destPicture->unref();
453 }
454}
455
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000456static void test_clip_bound_opt(skiatest::Reporter* reporter) {
457 // Test for crbug.com/229011
458 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
459 SkIntToScalar(2), SkIntToScalar(2));
460 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
461 SkIntToScalar(1), SkIntToScalar(1));
462 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
463 SkIntToScalar(1), SkIntToScalar(1));
464
465 SkPath invPath;
466 invPath.addOval(rect1);
467 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
468 SkPath path;
469 path.addOval(rect2);
470 SkPath path2;
471 path2.addOval(rect3);
472 SkIRect clipBounds;
473 // Minimalist test set for 100% code coverage of
474 // SkPictureRecord::updateClipConservativelyUsingBounds
475 {
476 SkPicture picture;
477 SkCanvas* canvas = picture.beginRecording(10, 10,
478 SkPicture::kUsePathBoundsForClip_RecordingFlag);
479 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
480 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
481 REPORTER_ASSERT(reporter, true == nonEmpty);
482 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
483 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
484 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
485 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
486 }
487 {
488 SkPicture picture;
489 SkCanvas* canvas = picture.beginRecording(10, 10,
490 SkPicture::kUsePathBoundsForClip_RecordingFlag);
491 canvas->clipPath(path, SkRegion::kIntersect_Op);
492 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
493 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
494 REPORTER_ASSERT(reporter, true == nonEmpty);
495 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
496 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
497 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
498 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
499 }
500 {
501 SkPicture picture;
502 SkCanvas* canvas = picture.beginRecording(10, 10,
503 SkPicture::kUsePathBoundsForClip_RecordingFlag);
504 canvas->clipPath(path, SkRegion::kIntersect_Op);
505 canvas->clipPath(invPath, SkRegion::kUnion_Op);
506 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
507 REPORTER_ASSERT(reporter, true == nonEmpty);
508 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
509 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
510 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
511 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
512 }
513 {
514 SkPicture picture;
515 SkCanvas* canvas = picture.beginRecording(10, 10,
516 SkPicture::kUsePathBoundsForClip_RecordingFlag);
517 canvas->clipPath(path, SkRegion::kDifference_Op);
518 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
519 REPORTER_ASSERT(reporter, true == nonEmpty);
520 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
521 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
522 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
523 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
524 }
525 {
526 SkPicture picture;
527 SkCanvas* canvas = picture.beginRecording(10, 10,
528 SkPicture::kUsePathBoundsForClip_RecordingFlag);
529 canvas->clipPath(path, SkRegion::kReverseDifference_Op);
530 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
531 // True clip is actually empty in this case, but the best
532 // determination we can make using only bounds as input is that the
533 // clip is included in the bounds of 'path'.
534 REPORTER_ASSERT(reporter, true == nonEmpty);
535 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
536 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
537 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
538 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
539 }
540 {
541 SkPicture picture;
542 SkCanvas* canvas = picture.beginRecording(10, 10,
543 SkPicture::kUsePathBoundsForClip_RecordingFlag);
544 canvas->clipPath(path, SkRegion::kIntersect_Op);
545 canvas->clipPath(path2, SkRegion::kXOR_Op);
546 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
547 REPORTER_ASSERT(reporter, true == nonEmpty);
548 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
549 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
550 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
551 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
552 }
553}
554
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000555/**
556 * A canvas that records the number of clip commands.
557 */
558class ClipCountingCanvas : public SkCanvas {
559public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000560 explicit ClipCountingCanvas(SkBaseDevice* device)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000561 : SkCanvas(device)
562 , fClipCount(0){
563 }
564
565 virtual bool clipRect(const SkRect& r, SkRegion::Op op, bool doAA)
566 SK_OVERRIDE {
567 fClipCount += 1;
568 return this->INHERITED::clipRect(r, op, doAA);
569 }
570
571 virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA)
572 SK_OVERRIDE {
573 fClipCount += 1;
574 return this->INHERITED::clipRRect(rrect, op, doAA);
575 }
576
577 virtual bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA)
578 SK_OVERRIDE {
579 fClipCount += 1;
580 return this->INHERITED::clipPath(path, op, doAA);
581 }
582
583 unsigned getClipCount() const { return fClipCount; }
584
585private:
586 unsigned fClipCount;
587
588 typedef SkCanvas INHERITED;
589};
590
591static void test_clip_expansion(skiatest::Reporter* reporter) {
592 SkPicture picture;
593 SkCanvas* canvas = picture.beginRecording(10, 10, 0);
594
595 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
596 // The following expanding clip should not be skipped.
597 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
598 // Draw something so the optimizer doesn't just fold the world.
599 SkPaint p;
600 p.setColor(SK_ColorBLUE);
601 canvas->drawPaint(p);
602
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000603 SkBitmapDevice testDevice(SkBitmap::kNo_Config, 10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000604 ClipCountingCanvas testCanvas(&testDevice);
605 picture.draw(&testCanvas);
606
607 // Both clips should be present on playback.
608 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
609}
610
tomhudson@google.com381010e2013-10-24 11:12:47 +0000611static void test_hierarchical(skiatest::Reporter* reporter) {
612 SkBitmap bm;
613 make_bm(&bm, 10, 10, SK_ColorRED, true);
614
615 SkCanvas* canvas;
616
617 SkPicture childPlain;
618 childPlain.beginRecording(10, 10);
619 childPlain.endRecording();
620 REPORTER_ASSERT(reporter, !childPlain.willPlayBackBitmaps()); // 0
621
622 SkPicture childWithBitmap;
623 childWithBitmap.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
624 childWithBitmap.endRecording();
625 REPORTER_ASSERT(reporter, childWithBitmap.willPlayBackBitmaps()); // 1
626
627 SkPicture parentPP;
628 canvas = parentPP.beginRecording(10, 10);
629 canvas->drawPicture(childPlain);
630 parentPP.endRecording();
631 REPORTER_ASSERT(reporter, !parentPP.willPlayBackBitmaps()); // 0
632
633 SkPicture parentPWB;
634 canvas = parentPWB.beginRecording(10, 10);
635 canvas->drawPicture(childWithBitmap);
636 parentPWB.endRecording();
637 REPORTER_ASSERT(reporter, parentPWB.willPlayBackBitmaps()); // 1
638
639 SkPicture parentWBP;
640 canvas = parentWBP.beginRecording(10, 10);
641 canvas->drawBitmap(bm, 0, 0);
642 canvas->drawPicture(childPlain);
643 parentWBP.endRecording();
644 REPORTER_ASSERT(reporter, parentWBP.willPlayBackBitmaps()); // 1
645
646 SkPicture parentWBWB;
647 canvas = parentWBWB.beginRecording(10, 10);
648 canvas->drawBitmap(bm, 0, 0);
649 canvas->drawPicture(childWithBitmap);
650 parentWBWB.endRecording();
651 REPORTER_ASSERT(reporter, parentWBWB.willPlayBackBitmaps()); // 2
652}
653
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000654static void TestPicture(skiatest::Reporter* reporter) {
655#ifdef SK_DEBUG
656 test_deleting_empty_playback();
657 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +0000658#else
659 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000660#endif
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000661 test_peephole();
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000662 test_gatherpixelrefs(reporter);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000663 test_bitmap_with_encoded_data(reporter);
junov@chromium.org94f20dc2013-01-28 21:04:44 +0000664 test_clone_empty(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000665 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000666 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +0000667 test_hierarchical(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000668}
669
670#include "TestClassDef.h"
reed@google.com21b519d2012-10-02 17:42:15 +0000671DEFINE_TESTCLASS("Pictures", PictureTestClass, TestPicture)