blob: acde9b6d58a429a9cdaaaf9a8a1d891a6c450838 [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 */
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00007
scroggo@google.comd614c6a2012-09-14 17:26:37 +00008#include "Test.h"
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00009#include "TestClassDef.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000010#include "SkBitmapDevice.h"
reed@google.com21b519d2012-10-02 17:42:15 +000011#include "SkCanvas.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000012#include "SkColorPriv.h"
13#include "SkData.h"
reed@google.combf790232013-12-13 19:45:58 +000014#include "SkDecodingImageGenerator.h"
scroggo@google.com49ce11b2013-04-25 18:29:32 +000015#include "SkError.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000016#include "SkImageEncoder.h"
17#include "SkImageGenerator.h"
reed@google.com21b519d2012-10-02 17:42:15 +000018#include "SkPaint.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000019#include "SkPicture.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000020#include "SkPictureUtils.h"
reed@google.com21b519d2012-10-02 17:42:15 +000021#include "SkRandom.h"
reed@google.com72aa79c2013-01-24 18:27:42 +000022#include "SkRRect.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000023#include "SkShader.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000024#include "SkStream.h"
25
reed@google.comfe7b1ed2012-11-29 21:00:39 +000026
27static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
28 bm->setConfig(SkBitmap::kARGB_8888_Config, w, h);
29 bm->allocPixels();
30 bm->eraseColor(color);
31 if (immutable) {
32 bm->setImmutable();
33 }
34}
35
36typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkPoint&);
37
38static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
39 const SkPoint& pos) {
40 canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
41}
42
43static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
44 const SkPoint& pos) {
45 SkRect r = {
46 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
47 };
48 r.offset(pos.fX, pos.fY);
49 canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
50}
51
52static void drawshader_proc(SkCanvas* canvas, const SkBitmap& bm,
53 const SkPoint& pos) {
54 SkRect r = {
55 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
56 };
57 r.offset(pos.fX, pos.fY);
58
59 SkShader* s = SkShader::CreateBitmapShader(bm,
60 SkShader::kClamp_TileMode,
61 SkShader::kClamp_TileMode);
62 SkPaint paint;
63 paint.setShader(s)->unref();
64 canvas->drawRect(r, paint);
reed@google.com72aa79c2013-01-24 18:27:42 +000065 canvas->drawOval(r, paint);
66 SkRRect rr;
67 rr.setRectXY(r, 10, 10);
68 canvas->drawRRect(rr, paint);
reed@google.comfe7b1ed2012-11-29 21:00:39 +000069}
70
71// Return a picture with the bitmaps drawn at the specified positions.
72static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[],
73 int count, DrawBitmapProc proc) {
74 SkPicture* pic = new SkPicture;
75 SkCanvas* canvas = pic->beginRecording(1000, 1000);
76 for (int i = 0; i < count; ++i) {
77 proc(canvas, bm[i], pos[i]);
78 }
79 pic->endRecording();
80 return pic;
81}
82
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000083static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) {
reed@google.comfe7b1ed2012-11-29 21:00:39 +000084 rect->fLeft = rand.nextRangeScalar(-W, 2*W);
85 rect->fTop = rand.nextRangeScalar(-H, 2*H);
86 rect->fRight = rect->fLeft + rand.nextRangeScalar(0, W);
87 rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H);
88
89 // we integralize rect to make our tests more predictable, since Gather is
90 // a little sloppy.
91 SkIRect ir;
92 rect->round(&ir);
93 rect->set(ir);
94}
95
96// Allocate result to be large enough to hold subset, and then draw the picture
97// into it, offsetting by subset's top/left corner.
98static void draw(SkPicture* pic, const SkRect& subset, SkBitmap* result) {
99 SkIRect ir;
100 subset.roundOut(&ir);
101 int w = ir.width();
102 int h = ir.height();
103 make_bm(result, w, h, 0, false);
104
105 SkCanvas canvas(*result);
106 canvas.translate(-SkIntToScalar(ir.left()), -SkIntToScalar(ir.top()));
107 canvas.drawPicture(*pic);
108}
109
110template <typename T> int find_index(const T* array, T elem, int count) {
111 for (int i = 0; i < count; ++i) {
112 if (array[i] == elem) {
113 return i;
114 }
115 }
116 return -1;
117}
118
119// Return true if 'ref' is found in array[]
120static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) {
121 return find_index<const SkPixelRef*>(array, ref, count) >= 0;
122}
123
124// Look at each pixel in bm, and if its color appears in colors[], find the
125// corresponding value in refs[] and append that ref into array, skipping
126// duplicates of the same value.
127static void gather_from_colors(const SkBitmap& bm, SkPixelRef* const refs[],
128 int count, SkTDArray<SkPixelRef*>* array) {
129 // Since we only want to return unique values in array, when we scan we just
130 // set a bit for each index'd color found. In practice we only have a few
131 // distinct colors, so we just use an int's bits as our array. Hence the
132 // assert that count <= number-of-bits-in-our-int.
133 SkASSERT((unsigned)count <= 32);
134 uint32_t bitarray = 0;
135
136 SkAutoLockPixels alp(bm);
137
138 for (int y = 0; y < bm.height(); ++y) {
139 for (int x = 0; x < bm.width(); ++x) {
140 SkPMColor pmc = *bm.getAddr32(x, y);
141 // the only good case where the color is not found would be if
142 // the color is transparent, meaning no bitmap was drawn in that
143 // pixel.
144 if (pmc) {
bsalomon@google.comc3d753e2013-01-08 17:24:44 +0000145 uint32_t index = SkGetPackedR32(pmc);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000146 SkASSERT(SkGetPackedG32(pmc) == index);
147 SkASSERT(SkGetPackedB32(pmc) == index);
bsalomon@google.com5f429b02013-01-08 18:42:20 +0000148 SkASSERT(static_cast<int>(index) < count);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000149 bitarray |= 1 << index;
150 }
151 }
152 }
153
154 for (int i = 0; i < count; ++i) {
155 if (bitarray & (1 << i)) {
156 *array->append() = refs[i];
157 }
158 }
159}
160
161static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
162 const int IW = 8;
163 const int IH = IW;
164 const SkScalar W = SkIntToScalar(IW);
165 const SkScalar H = W;
166
167 static const int N = 4;
168 SkBitmap bm[N];
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000169 SkPixelRef* refs[N];
170
171 const SkPoint pos[] = {
172 { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
173 };
174
175 // Our convention is that the color components contain the index of their
176 // corresponding bitmap/pixelref
177 for (int i = 0; i < N; ++i) {
178 make_bm(&bm[i], IW, IH, SkColorSetARGB(0xFF, i, i, i), true);
179 refs[i] = bm[i].pixelRef();
180 }
181
182 static const DrawBitmapProc procs[] = {
183 drawbitmap_proc, drawbitmaprect_proc, drawshader_proc
184 };
185
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000186 SkRandom rand;
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000187 for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) {
188 SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k]));
189
tomhudson@google.com381010e2013-10-24 11:12:47 +0000190 REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000191 // quick check for a small piece of each quadrant, which should just
192 // contain 1 bitmap.
193 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
194 SkRect r;
195 r.set(2, 2, W - 2, H - 2);
196 r.offset(pos[i].fX, pos[i].fY);
197 SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r));
198 REPORTER_ASSERT(reporter, data);
commit-bot@chromium.orgfe433c12013-07-09 16:04:32 +0000199 if (data) {
robertphillips@google.come9cd27d2013-10-16 17:48:11 +0000200 int count = static_cast<int>(data->size() / sizeof(SkPixelRef*));
commit-bot@chromium.orgfe433c12013-07-09 16:04:32 +0000201 REPORTER_ASSERT(reporter, 1 == count);
202 REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]);
203 }
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000204 }
205
206 // Test a bunch of random (mostly) rects, and compare the gather results
207 // with a deduced list of refs by looking at the colors drawn.
208 for (int j = 0; j < 100; ++j) {
209 SkRect r;
210 rand_rect(&r, rand, 2*W, 2*H);
211
212 SkBitmap result;
213 draw(pic, r, &result);
214 SkTDArray<SkPixelRef*> array;
215
216 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
217 size_t dataSize = data ? data->size() : 0;
robertphillips@google.come9cd27d2013-10-16 17:48:11 +0000218 int gatherCount = static_cast<int>(dataSize / sizeof(SkPixelRef*));
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000219 SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
220 SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
221 SkAutoDataUnref adu(data);
222
223 gather_from_colors(result, refs, N, &array);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000224
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000225 /*
226 * GatherPixelRefs is conservative, so it can return more bitmaps
227 * that we actually can see (usually because of conservative bounds
228 * inflation for antialiasing). Thus our check here is only that
229 * Gather didn't miss any that we actually saw. Even that isn't
230 * a strict requirement on Gather, which is meant to be quick and
231 * only mostly-correct, but at the moment this test should work.
232 */
233 for (int i = 0; i < array.count(); ++i) {
234 bool found = find(gatherRefs, array[i], gatherCount);
235 REPORTER_ASSERT(reporter, found);
236#if 0
237 // enable this block of code to debug failures, as it will rerun
238 // the case that failed.
239 if (!found) {
240 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
241 size_t dataSize = data ? data->size() : 0;
242 }
243#endif
244 }
245 }
246 }
247}
248
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000249#ifdef SK_DEBUG
250// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
251// run in debug mode.
252static void test_deleting_empty_playback() {
253 SkPicture picture;
254 // Creates an SkPictureRecord
255 picture.beginRecording(0, 0);
256 // Turns that into an SkPicturePlayback
257 picture.endRecording();
258 // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord
259 picture.beginRecording(0, 0);
260}
261
262// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
263static void test_serializing_empty_picture() {
264 SkPicture picture;
265 picture.beginRecording(0, 0);
266 picture.endRecording();
267 SkDynamicMemoryWStream stream;
268 picture.serialize(&stream);
269}
270#endif
271
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000272static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +0000273 SkPaint paint;
274 SkRect rect = SkRect::MakeWH(50, 50);
275
276 SkScalar unit = rand.nextUScalar1();
277 if (unit <= 0.3) {
278// SkDebugf("save\n");
279 canvas->save();
280 } else if (unit <= 0.6) {
281// SkDebugf("restore\n");
282 canvas->restore();
283 } else if (unit <= 0.9) {
284// SkDebugf("clip\n");
285 canvas->clipRect(rect);
286 } else {
287// SkDebugf("draw\n");
288 canvas->drawPaint(paint);
289 }
290}
291
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000292static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000293 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000294
295 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000296 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000297
298 SkPicture picture;
299 SkCanvas* canvas = picture.beginRecording(100, 100);
300
301 for (int i = 0; i < 1000; ++i) {
302 rand_op(canvas, rand);
303 }
304 picture.endRecording();
jvanverth@google.comc490f802013-03-04 13:56:38 +0000305
306 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000307 }
308
309 {
310 SkPicture picture;
311 SkCanvas* canvas = picture.beginRecording(100, 100);
312 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000313
reed@google.com21b519d2012-10-02 17:42:15 +0000314 for (int i = 0; i < 100; ++i) {
315 canvas->save();
316 }
317 while (canvas->getSaveCount() > 1) {
318 canvas->clipRect(rect);
319 canvas->restore();
320 }
321 picture.endRecording();
322 }
323}
324
scroggo@google.com4b90b112012-12-04 15:08:56 +0000325#ifndef SK_DEBUG
326// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
327// should never do this.
328static void test_bad_bitmap() {
329 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
330 // fail.
331 SkBitmap bm;
332 bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
333 SkPicture picture;
334 SkCanvas* recordingCanvas = picture.beginRecording(100, 100);
335 recordingCanvas->drawBitmap(bm, 0, 0);
336 picture.endRecording();
337
338 SkCanvas canvas;
339 canvas.drawPicture(picture);
340}
341#endif
342
reed@google.com672588b2014-01-08 15:42:01 +0000343static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) {
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000344 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000345}
346
347static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
348 SkPicture picture;
349 SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height());
350 canvas->drawBitmap(bitmap, 0, 0);
351 SkDynamicMemoryWStream wStream;
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000352 picture.serialize(&wStream, &encode_bitmap_to_data);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000353 return wStream.copyToData();
354}
355
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000356struct ErrorContext {
357 int fErrors;
358 skiatest::Reporter* fReporter;
359};
360
361static void assert_one_parse_error_cb(SkError error, void* context) {
362 ErrorContext* errorContext = static_cast<ErrorContext*>(context);
363 errorContext->fErrors++;
364 // This test only expects one error, and that is a kParseError. If there are others,
365 // there is some unknown problem.
366 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
367 "This threw more errors than expected.");
368 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
369 SkGetLastErrorString());
370}
371
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000372static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
373 // Create a bitmap that will be encoded.
374 SkBitmap original;
375 make_bm(&original, 100, 100, SK_ColorBLUE, true);
376 SkDynamicMemoryWStream wStream;
377 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
378 return;
379 }
380 SkAutoDataUnref data(wStream.copyToData());
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000381
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000382 SkBitmap bm;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000383 bool installSuccess = SkInstallDiscardablePixelRef(
384 SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm, NULL);
reed@google.combf790232013-12-13 19:45:58 +0000385 REPORTER_ASSERT(reporter, installSuccess);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000386
387 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
388 // Flattening original will follow the old path of performing an encode, while flattening bm
389 // will use the already encoded data.
390 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
391 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
392 REPORTER_ASSERT(reporter, picture1->equals(picture2));
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000393 // Now test that a parse error was generated when trying to create a new SkPicture without
394 // providing a function to decode the bitmap.
395 ErrorContext context;
396 context.fErrors = 0;
397 context.fReporter = reporter;
398 SkSetErrorCallback(assert_one_parse_error_cb, &context);
399 SkMemoryStream pictureStream(picture1);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000400 SkClearLastError();
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000401 SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
402 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000403 SkClearLastError();
404 SkSetErrorCallback(NULL, NULL);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000405}
406
junov@chromium.org94f20dc2013-01-28 21:04:44 +0000407static void test_clone_empty(skiatest::Reporter* reporter) {
408 // This is a regression test for crbug.com/172062
409 // Before the fix, we used to crash accessing a null pointer when we
410 // had a picture with no paints. This test passes by not crashing.
411 {
412 SkPicture picture;
413 picture.beginRecording(1, 1);
414 picture.endRecording();
415 SkPicture* destPicture = picture.clone();
416 REPORTER_ASSERT(reporter, NULL != destPicture);
417 destPicture->unref();
418 }
419 {
420 // Test without call to endRecording
421 SkPicture picture;
422 picture.beginRecording(1, 1);
423 SkPicture* destPicture = picture.clone();
424 REPORTER_ASSERT(reporter, NULL != destPicture);
425 destPicture->unref();
426 }
427}
428
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000429static void test_clip_bound_opt(skiatest::Reporter* reporter) {
430 // Test for crbug.com/229011
431 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
432 SkIntToScalar(2), SkIntToScalar(2));
433 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
434 SkIntToScalar(1), SkIntToScalar(1));
435 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
436 SkIntToScalar(1), SkIntToScalar(1));
437
438 SkPath invPath;
439 invPath.addOval(rect1);
440 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
441 SkPath path;
442 path.addOval(rect2);
443 SkPath path2;
444 path2.addOval(rect3);
445 SkIRect clipBounds;
446 // Minimalist test set for 100% code coverage of
447 // SkPictureRecord::updateClipConservativelyUsingBounds
448 {
449 SkPicture picture;
450 SkCanvas* canvas = picture.beginRecording(10, 10,
451 SkPicture::kUsePathBoundsForClip_RecordingFlag);
452 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
453 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
454 REPORTER_ASSERT(reporter, true == nonEmpty);
455 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
456 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
457 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
458 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
459 }
460 {
461 SkPicture picture;
462 SkCanvas* canvas = picture.beginRecording(10, 10,
463 SkPicture::kUsePathBoundsForClip_RecordingFlag);
464 canvas->clipPath(path, SkRegion::kIntersect_Op);
465 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
466 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
467 REPORTER_ASSERT(reporter, true == nonEmpty);
468 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
469 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
470 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
471 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
472 }
473 {
474 SkPicture picture;
475 SkCanvas* canvas = picture.beginRecording(10, 10,
476 SkPicture::kUsePathBoundsForClip_RecordingFlag);
477 canvas->clipPath(path, SkRegion::kIntersect_Op);
478 canvas->clipPath(invPath, SkRegion::kUnion_Op);
479 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
480 REPORTER_ASSERT(reporter, true == nonEmpty);
481 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
482 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
483 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
484 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
485 }
486 {
487 SkPicture picture;
488 SkCanvas* canvas = picture.beginRecording(10, 10,
489 SkPicture::kUsePathBoundsForClip_RecordingFlag);
490 canvas->clipPath(path, SkRegion::kDifference_Op);
491 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
492 REPORTER_ASSERT(reporter, true == nonEmpty);
493 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
494 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
495 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
496 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
497 }
498 {
499 SkPicture picture;
500 SkCanvas* canvas = picture.beginRecording(10, 10,
501 SkPicture::kUsePathBoundsForClip_RecordingFlag);
502 canvas->clipPath(path, SkRegion::kReverseDifference_Op);
503 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
504 // True clip is actually empty in this case, but the best
505 // determination we can make using only bounds as input is that the
506 // clip is included in the bounds of 'path'.
507 REPORTER_ASSERT(reporter, true == nonEmpty);
508 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
509 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
510 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
511 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
512 }
513 {
514 SkPicture picture;
515 SkCanvas* canvas = picture.beginRecording(10, 10,
516 SkPicture::kUsePathBoundsForClip_RecordingFlag);
517 canvas->clipPath(path, SkRegion::kIntersect_Op);
518 canvas->clipPath(path2, SkRegion::kXOR_Op);
519 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
520 REPORTER_ASSERT(reporter, true == nonEmpty);
521 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
522 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
523 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
524 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
525 }
526}
527
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000528/**
529 * A canvas that records the number of clip commands.
530 */
531class ClipCountingCanvas : public SkCanvas {
532public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000533 explicit ClipCountingCanvas(SkBaseDevice* device)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000534 : SkCanvas(device)
535 , fClipCount(0){
536 }
537
538 virtual bool clipRect(const SkRect& r, SkRegion::Op op, bool doAA)
539 SK_OVERRIDE {
540 fClipCount += 1;
541 return this->INHERITED::clipRect(r, op, doAA);
542 }
543
544 virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA)
545 SK_OVERRIDE {
546 fClipCount += 1;
547 return this->INHERITED::clipRRect(rrect, op, doAA);
548 }
549
550 virtual bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA)
551 SK_OVERRIDE {
552 fClipCount += 1;
553 return this->INHERITED::clipPath(path, op, doAA);
554 }
555
556 unsigned getClipCount() const { return fClipCount; }
557
558private:
559 unsigned fClipCount;
560
561 typedef SkCanvas INHERITED;
562};
563
564static void test_clip_expansion(skiatest::Reporter* reporter) {
565 SkPicture picture;
566 SkCanvas* canvas = picture.beginRecording(10, 10, 0);
567
568 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
569 // The following expanding clip should not be skipped.
570 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
571 // Draw something so the optimizer doesn't just fold the world.
572 SkPaint p;
573 p.setColor(SK_ColorBLUE);
574 canvas->drawPaint(p);
575
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000576 SkBitmapDevice testDevice(SkBitmap::kNo_Config, 10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000577 ClipCountingCanvas testCanvas(&testDevice);
578 picture.draw(&testCanvas);
579
580 // Both clips should be present on playback.
581 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
582}
583
tomhudson@google.com381010e2013-10-24 11:12:47 +0000584static void test_hierarchical(skiatest::Reporter* reporter) {
585 SkBitmap bm;
586 make_bm(&bm, 10, 10, SK_ColorRED, true);
587
588 SkCanvas* canvas;
589
590 SkPicture childPlain;
591 childPlain.beginRecording(10, 10);
592 childPlain.endRecording();
593 REPORTER_ASSERT(reporter, !childPlain.willPlayBackBitmaps()); // 0
594
595 SkPicture childWithBitmap;
596 childWithBitmap.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
597 childWithBitmap.endRecording();
598 REPORTER_ASSERT(reporter, childWithBitmap.willPlayBackBitmaps()); // 1
599
600 SkPicture parentPP;
601 canvas = parentPP.beginRecording(10, 10);
602 canvas->drawPicture(childPlain);
603 parentPP.endRecording();
604 REPORTER_ASSERT(reporter, !parentPP.willPlayBackBitmaps()); // 0
605
606 SkPicture parentPWB;
607 canvas = parentPWB.beginRecording(10, 10);
608 canvas->drawPicture(childWithBitmap);
609 parentPWB.endRecording();
610 REPORTER_ASSERT(reporter, parentPWB.willPlayBackBitmaps()); // 1
611
612 SkPicture parentWBP;
613 canvas = parentWBP.beginRecording(10, 10);
614 canvas->drawBitmap(bm, 0, 0);
615 canvas->drawPicture(childPlain);
616 parentWBP.endRecording();
617 REPORTER_ASSERT(reporter, parentWBP.willPlayBackBitmaps()); // 1
618
619 SkPicture parentWBWB;
620 canvas = parentWBWB.beginRecording(10, 10);
621 canvas->drawBitmap(bm, 0, 0);
622 canvas->drawPicture(childWithBitmap);
623 parentWBWB.endRecording();
624 REPORTER_ASSERT(reporter, parentWBWB.willPlayBackBitmaps()); // 2
625}
626
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000627DEF_TEST(Picture, reporter) {
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000628#ifdef SK_DEBUG
629 test_deleting_empty_playback();
630 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +0000631#else
632 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000633#endif
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000634 test_peephole();
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000635 test_gatherpixelrefs(reporter);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000636 test_bitmap_with_encoded_data(reporter);
junov@chromium.org94f20dc2013-01-28 21:04:44 +0000637 test_clone_empty(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000638 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000639 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +0000640 test_hierarchical(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000641}