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