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