2 * Copyright 2012 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkBBoxHierarchy.h"
9 #include "SkBlurImageFilter.h"
11 #include "SkColorMatrixFilter.h"
12 #include "SkColorPriv.h"
13 #include "SkDashPathEffect.h"
15 #include "SkImageGenerator.h"
17 #include "SkImageEncoder.h"
18 #include "SkImageGenerator.h"
19 #include "SkLayerInfo.h"
21 #include "SkPicture.h"
22 #include "SkPictureRecorder.h"
23 #include "SkPictureUtils.h"
24 #include "SkPixelRef.h"
25 #include "SkPixelSerializer.h"
31 #include "sk_tool_utils.h"
34 #include "SkSurface.h"
35 #include "GrContextFactory.h"
39 #include "SkLumaColorFilter.h"
40 #include "SkColorFilterImageFilter.h"
42 static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
43 bm->allocN32Pixels(w, h);
44 bm->eraseColor(color);
50 /* Hit a few SkPicture::Analysis cases not handled elsewhere. */
51 static void test_analysis(skiatest::Reporter* reporter) {
52 SkPictureRecorder recorder;
54 SkCanvas* canvas = recorder.beginRecording(100, 100);
56 canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ());
58 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
59 REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps());
61 canvas = recorder.beginRecording(100, 100);
64 // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
65 // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
67 bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
68 bitmap.eraseColor(SK_ColorBLUE);
69 *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN;
70 SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode,
71 SkShader::kClamp_TileMode);
72 paint.setShader(shader)->unref();
73 REPORTER_ASSERT(reporter,
74 shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType);
76 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
78 picture.reset(recorder.endRecording());
79 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
84 // Ensure that deleting an empty SkPicture does not assert. Asserts only fire
85 // in debug mode, so only run in debug mode.
86 static void test_deleting_empty_picture() {
87 SkPictureRecorder recorder;
88 // Creates an SkPictureRecord
89 recorder.beginRecording(0, 0);
90 // Turns that into an SkPicture
91 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
92 // Ceates a new SkPictureRecord
93 recorder.beginRecording(0, 0);
96 // Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
97 static void test_serializing_empty_picture() {
98 SkPictureRecorder recorder;
99 recorder.beginRecording(0, 0);
100 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
101 SkDynamicMemoryWStream stream;
102 picture->serialize(&stream);
106 static void rand_op(SkCanvas* canvas, SkRandom& rand) {
108 SkRect rect = SkRect::MakeWH(50, 50);
110 SkScalar unit = rand.nextUScalar1();
112 // SkDebugf("save\n");
114 } else if (unit <= 0.6) {
115 // SkDebugf("restore\n");
117 } else if (unit <= 0.9) {
118 // SkDebugf("clip\n");
119 canvas->clipRect(rect);
121 // SkDebugf("draw\n");
122 canvas->drawPaint(paint);
128 static void test_gpu_veto(skiatest::Reporter* reporter) {
129 SkPictureRecorder recorder;
131 SkCanvas* canvas = recorder.beginRecording(100, 100);
137 SkScalar intervals[] = { 1.0f, 1.0f };
138 SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0));
141 paint.setStyle(SkPaint::kStroke_Style);
142 paint.setPathEffect(dash);
144 for (int i = 0; i < 50; ++i) {
145 canvas->drawPath(path, paint);
148 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
149 // path effects currently render an SkPicture undesireable for GPU rendering
151 const char *reason = NULL;
152 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL, &reason));
153 REPORTER_ASSERT(reporter, reason);
155 canvas = recorder.beginRecording(100, 100);
165 REPORTER_ASSERT(reporter, !path.isConvex());
168 paint.setAntiAlias(true);
169 for (int i = 0; i < 50; ++i) {
170 canvas->drawPath(path, paint);
173 picture.reset(recorder.endRecording());
174 // A lot of small AA concave paths should be fine for GPU rendering
175 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
177 canvas = recorder.beginRecording(100, 100);
184 path.lineTo(100, 100);
187 REPORTER_ASSERT(reporter, !path.isConvex());
190 paint.setAntiAlias(true);
191 for (int i = 0; i < 50; ++i) {
192 canvas->drawPath(path, paint);
195 picture.reset(recorder.endRecording());
196 // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering
197 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
199 canvas = recorder.beginRecording(100, 100);
209 REPORTER_ASSERT(reporter, !path.isConvex());
212 paint.setAntiAlias(true);
213 paint.setStyle(SkPaint::kStroke_Style);
214 paint.setStrokeWidth(0);
215 for (int i = 0; i < 50; ++i) {
216 canvas->drawPath(path, paint);
219 picture.reset(recorder.endRecording());
220 // hairline stroked AA concave paths are fine for GPU rendering
221 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
223 canvas = recorder.beginRecording(100, 100);
226 SkScalar intervals [] = { 10, 20 };
227 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
228 paint.setPathEffect(pe)->unref();
230 SkPoint points [2] = { { 0, 0 }, { 100, 0 } };
232 for (int i = 0; i < 50; ++i) {
233 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
236 picture.reset(recorder.endRecording());
237 // fast-path dashed effects are fine for GPU rendering ...
238 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
240 canvas = recorder.beginRecording(100, 100);
243 SkScalar intervals [] = { 10, 20 };
244 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
245 paint.setPathEffect(pe)->unref();
247 for (int i = 0; i < 50; ++i) {
248 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
251 picture.reset(recorder.endRecording());
252 // ... but only when applied to drawPoint() calls
253 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
255 // Nest the previous picture inside a new one.
256 canvas = recorder.beginRecording(100, 100);
258 canvas->drawPicture(picture.get());
260 picture.reset(recorder.endRecording());
261 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
266 static void test_savelayer_extraction(skiatest::Reporter* reporter) {
267 static const int kWidth = 100;
268 static const int kHeight = 100;
270 // Create complex paint that the bounding box computation code can't
272 SkScalar blueToRedMatrix[20] = { 0 };
273 blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
274 SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix));
275 SkAutoTUnref<SkImageFilter> filter(SkColorFilterImageFilter::Create(blueToRed.get()));
277 SkPaint complexPaint;
278 complexPaint.setImageFilter(filter);
280 SkAutoTUnref<SkPicture> pict, child;
281 SkRTreeFactory bbhFactory;
284 SkPictureRecorder recorder;
286 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight),
288 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
290 c->saveLayer(NULL, &complexPaint);
293 child.reset(recorder.endRecording());
296 // create a picture with the structure:
303 // SaveLayer w/ bound
307 // SaveLayer w/ copyable paint
311 // DrawPicture (which has a SaveLayer/Restore pair)
315 // DrawPicture with Matrix & Paint (with SaveLayer/Restore pair)
318 SkPictureRecorder recorder;
320 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth),
321 SkIntToScalar(kHeight),
323 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
325 c->saveLayer(NULL, &complexPaint); // layer #0
329 c->saveLayer(NULL, NULL); // layer #1
330 c->translate(kWidth / 2.0f, kHeight / 2.0f);
331 SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2);
332 c->saveLayer(&r, &complexPaint); // layer #2
338 c->saveLayer(NULL, &complexPaint); // layer #3
343 layerPaint.setColor(SK_ColorRED); // Non-alpha only to avoid SaveLayerDrawRestoreNooper
346 c->saveLayer(NULL, &layerPaint); // layer #4
347 c->drawPicture(child); // layer #5 inside picture
352 SkPaint picturePaint;
354 trans.setTranslate(10, 10);
356 c->saveLayer(NULL, &layerPaint); // layer #6
357 c->drawPicture(child, &trans, &picturePaint); // layer #7 inside picture
361 pict.reset(recorder.endRecording());
364 // Now test out the SaveLayer extraction
365 if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) {
366 SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
368 const SkPicture::AccelData* data = pict->EXPERIMENTAL_getAccelData(key);
369 REPORTER_ASSERT(reporter, data);
371 const SkLayerInfo *gpuData = static_cast<const SkLayerInfo*>(data);
372 REPORTER_ASSERT(reporter, 8 == gpuData->numBlocks());
374 const SkLayerInfo::BlockInfo& info0 = gpuData->block(0);
375 // The parent/child layers appear in reverse order
376 const SkLayerInfo::BlockInfo& info1 = gpuData->block(2);
377 const SkLayerInfo::BlockInfo& info2 = gpuData->block(1);
379 const SkLayerInfo::BlockInfo& info3 = gpuData->block(3);
381 // The parent/child layers appear in reverse order
382 const SkLayerInfo::BlockInfo& info4 = gpuData->block(5);
383 const SkLayerInfo::BlockInfo& info5 = gpuData->block(4);
385 // The parent/child layers appear in reverse order
386 const SkLayerInfo::BlockInfo& info6 = gpuData->block(7);
387 const SkLayerInfo::BlockInfo& info7 = gpuData->block(6);
389 REPORTER_ASSERT(reporter, NULL == info0.fPicture);
390 REPORTER_ASSERT(reporter, kWidth == info0.fBounds.width() &&
391 kHeight == info0.fBounds.height());
392 REPORTER_ASSERT(reporter, info0.fLocalMat.isIdentity());
393 REPORTER_ASSERT(reporter, info0.fPreMat.isIdentity());
394 REPORTER_ASSERT(reporter, 0 == info0.fBounds.fLeft && 0 == info0.fBounds.fTop);
395 REPORTER_ASSERT(reporter, NULL != info0.fPaint);
396 REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers);
398 REPORTER_ASSERT(reporter, NULL == info1.fPicture);
399 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.width() &&
400 kHeight/2.0 == info1.fBounds.height());
401 REPORTER_ASSERT(reporter, info1.fLocalMat.isIdentity());
402 REPORTER_ASSERT(reporter, info1.fPreMat.isIdentity());
403 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.fLeft &&
404 kHeight/2.0 == info1.fBounds.fTop);
405 REPORTER_ASSERT(reporter, NULL == info1.fPaint);
406 REPORTER_ASSERT(reporter, !info1.fIsNested &&
407 info1.fHasNestedLayers); // has a nested SL
409 REPORTER_ASSERT(reporter, NULL == info2.fPicture);
410 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.width() &&
411 kHeight / 2 == info2.fBounds.height()); // bound reduces size
412 REPORTER_ASSERT(reporter, !info2.fLocalMat.isIdentity());
413 REPORTER_ASSERT(reporter, info2.fPreMat.isIdentity());
414 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.fLeft && // translated
415 kHeight / 2 == info2.fBounds.fTop);
416 REPORTER_ASSERT(reporter, NULL != info2.fPaint);
417 REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested
419 REPORTER_ASSERT(reporter, NULL == info3.fPicture);
420 REPORTER_ASSERT(reporter, kWidth == info3.fBounds.width() &&
421 kHeight == info3.fBounds.height());
422 REPORTER_ASSERT(reporter, info3.fLocalMat.isIdentity());
423 REPORTER_ASSERT(reporter, info3.fPreMat.isIdentity());
424 REPORTER_ASSERT(reporter, 0 == info3.fBounds.fLeft && 0 == info3.fBounds.fTop);
425 REPORTER_ASSERT(reporter, info3.fPaint);
426 REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers);
428 REPORTER_ASSERT(reporter, NULL == info4.fPicture);
429 REPORTER_ASSERT(reporter, kWidth == info4.fBounds.width() &&
430 kHeight == info4.fBounds.height());
431 REPORTER_ASSERT(reporter, 0 == info4.fBounds.fLeft && 0 == info4.fBounds.fTop);
432 REPORTER_ASSERT(reporter, info4.fLocalMat.isIdentity());
433 REPORTER_ASSERT(reporter, info4.fPreMat.isIdentity());
434 REPORTER_ASSERT(reporter, info4.fPaint);
435 REPORTER_ASSERT(reporter, !info4.fIsNested &&
436 info4.fHasNestedLayers); // has a nested SL
438 REPORTER_ASSERT(reporter, child == info5.fPicture); // in a child picture
439 REPORTER_ASSERT(reporter, kWidth == info5.fBounds.width() &&
440 kHeight == info5.fBounds.height());
441 REPORTER_ASSERT(reporter, 0 == info5.fBounds.fLeft && 0 == info5.fBounds.fTop);
442 REPORTER_ASSERT(reporter, info5.fLocalMat.isIdentity());
443 REPORTER_ASSERT(reporter, info5.fPreMat.isIdentity());
444 REPORTER_ASSERT(reporter, NULL != info5.fPaint);
445 REPORTER_ASSERT(reporter, info5.fIsNested && !info5.fHasNestedLayers); // is nested
447 REPORTER_ASSERT(reporter, NULL == info6.fPicture);
448 REPORTER_ASSERT(reporter, kWidth-10 == info6.fBounds.width() &&
449 kHeight-10 == info6.fBounds.height());
450 REPORTER_ASSERT(reporter, 10 == info6.fBounds.fLeft && 10 == info6.fBounds.fTop);
451 REPORTER_ASSERT(reporter, info6.fLocalMat.isIdentity());
452 REPORTER_ASSERT(reporter, info6.fPreMat.isIdentity());
453 REPORTER_ASSERT(reporter, info6.fPaint);
454 REPORTER_ASSERT(reporter, !info6.fIsNested &&
455 info6.fHasNestedLayers); // has a nested SL
457 REPORTER_ASSERT(reporter, child == info7.fPicture); // in a child picture
458 REPORTER_ASSERT(reporter, kWidth == info7.fBounds.width() &&
459 kHeight == info7.fBounds.height());
460 REPORTER_ASSERT(reporter, 0 == info7.fBounds.fLeft && 0 == info7.fBounds.fTop);
461 REPORTER_ASSERT(reporter, info7.fLocalMat.isIdentity());
462 REPORTER_ASSERT(reporter, info7.fPreMat.isIdentity());
463 REPORTER_ASSERT(reporter, NULL != info7.fPaint);
464 REPORTER_ASSERT(reporter, info7.fIsNested && !info7.fHasNestedLayers); // is nested
468 static void test_has_text(skiatest::Reporter* reporter) {
469 SkPictureRecorder recorder;
471 SkCanvas* canvas = recorder.beginRecording(100,100);
473 canvas->drawRect(SkRect::MakeWH(20, 20), SkPaint());
475 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
476 REPORTER_ASSERT(reporter, !picture->hasText());
478 SkPoint point = SkPoint::Make(10, 10);
479 canvas = recorder.beginRecording(100,100);
481 canvas->drawText("Q", 1, point.fX, point.fY, SkPaint());
483 picture.reset(recorder.endRecording());
484 REPORTER_ASSERT(reporter, picture->hasText());
486 canvas = recorder.beginRecording(100,100);
488 canvas->drawPosText("Q", 1, &point, SkPaint());
490 picture.reset(recorder.endRecording());
491 REPORTER_ASSERT(reporter, picture->hasText());
493 canvas = recorder.beginRecording(100,100);
495 canvas->drawPosTextH("Q", 1, &point.fX, point.fY, SkPaint());
497 picture.reset(recorder.endRecording());
498 REPORTER_ASSERT(reporter, picture->hasText());
500 canvas = recorder.beginRecording(100,100);
506 canvas->drawTextOnPathHV("Q", 1, path, point.fX, point.fY, SkPaint());
508 picture.reset(recorder.endRecording());
509 REPORTER_ASSERT(reporter, picture->hasText());
511 canvas = recorder.beginRecording(100,100);
517 canvas->drawTextOnPath("Q", 1, path, NULL, SkPaint());
519 picture.reset(recorder.endRecording());
520 REPORTER_ASSERT(reporter, picture->hasText());
522 // Nest the previous picture inside a new one.
523 canvas = recorder.beginRecording(100,100);
525 canvas->drawPicture(picture.get());
527 picture.reset(recorder.endRecording());
528 REPORTER_ASSERT(reporter, picture->hasText());
531 static void set_canvas_to_save_count_4(SkCanvas* canvas) {
532 canvas->restoreToCount(1);
539 * A canvas that records the number of saves, saveLayers and restores.
541 class SaveCountingCanvas : public SkCanvas {
543 SaveCountingCanvas(int width, int height)
544 : INHERITED(width, height)
550 virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint,
551 SaveFlags flags) SK_OVERRIDE {
553 return this->INHERITED::willSaveLayer(bounds, paint, flags);
556 void willSave() SK_OVERRIDE {
558 this->INHERITED::willSave();
561 void willRestore() SK_OVERRIDE {
563 this->INHERITED::willRestore();
566 unsigned int getSaveCount() const { return fSaveCount; }
567 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
568 unsigned int getRestoreCount() const { return fRestoreCount; }
571 unsigned int fSaveCount;
572 unsigned int fSaveLayerCount;
573 unsigned int fRestoreCount;
575 typedef SkCanvas INHERITED;
578 void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
579 unsigned int numSaves, unsigned int numSaveLayers,
580 unsigned int numRestores) {
581 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
582 SkScalarCeilToInt(picture->cullRect().height()));
584 picture->playback(&canvas);
586 // Optimizations may have removed these,
587 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
588 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
589 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
590 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
593 // This class exists so SkPicture can friend it and give it access to
594 // the 'partialReplay' method.
595 class SkPictureRecorderReplayTester {
597 static SkPicture* Copy(SkPictureRecorder* recorder) {
598 SkPictureRecorder recorder2;
600 SkCanvas* canvas = recorder2.beginRecording(10, 10);
602 recorder->partialReplay(canvas);
604 return recorder2.endRecording();
608 static void create_imbalance(SkCanvas* canvas) {
609 SkRect clipRect = SkRect::MakeWH(2, 2);
610 SkRect drawRect = SkRect::MakeWH(10, 10);
612 canvas->clipRect(clipRect, SkRegion::kReplace_Op);
613 canvas->translate(1.0f, 1.0f);
615 p.setColor(SK_ColorGREEN);
616 canvas->drawRect(drawRect, p);
620 // This tests that replaying a potentially unbalanced picture into a canvas
621 // doesn't affect the canvas' save count or matrix/clip state.
622 static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
624 bm.allocN32Pixels(4, 3);
627 int beforeSaveCount = canvas.getSaveCount();
629 SkMatrix beforeMatrix = canvas.getTotalMatrix();
633 canvas.getClipBounds(&beforeClip);
635 canvas.drawPicture(picture);
637 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
638 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
642 canvas.getClipBounds(&afterClip);
644 REPORTER_ASSERT(reporter, afterClip == beforeClip);
647 // Test out SkPictureRecorder::partialReplay
648 DEF_TEST(PictureRecorder_replay, reporter) {
649 // check save/saveLayer state
651 SkPictureRecorder recorder;
653 SkCanvas* canvas = recorder.beginRecording(10, 10);
655 canvas->saveLayer(NULL, NULL);
657 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
659 // The extra save and restore comes from the Copy process.
660 check_save_state(reporter, copy, 2, 1, 3);
662 canvas->saveLayer(NULL, NULL);
664 SkAutoTUnref<SkPicture> final(recorder.endRecording());
666 check_save_state(reporter, final, 1, 2, 3);
668 // The copy shouldn't pick up any operations added after it was made
669 check_save_state(reporter, copy, 2, 1, 3);
672 // (partially) check leakage of draw ops
674 SkPictureRecorder recorder;
676 SkCanvas* canvas = recorder.beginRecording(10, 10);
678 SkRect r = SkRect::MakeWH(5, 5);
681 canvas->drawRect(r, p);
683 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
685 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
688 make_bm(&bm, 10, 10, SK_ColorRED, true);
690 r.offset(5.0f, 5.0f);
691 canvas->drawBitmapRectToRect(bm, NULL, r);
693 SkAutoTUnref<SkPicture> final(recorder.endRecording());
694 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
696 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
698 // The snapshot shouldn't pick up any operations added after it was made
699 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
702 // Recreate the Android partialReplay test case
704 SkPictureRecorder recorder;
706 SkCanvas* canvas = recorder.beginRecording(4, 3, NULL, 0);
707 create_imbalance(canvas);
709 int expectedSaveCount = canvas->getSaveCount();
711 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
712 check_balance(reporter, copy);
714 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
716 // End the recording of source to test the picture finalization
717 // process isn't complicated by the partialReplay step
718 SkAutoTUnref<SkPicture> final(recorder.endRecording());
722 static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
723 SkCanvas testCanvas(100, 100);
724 set_canvas_to_save_count_4(&testCanvas);
726 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
729 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
731 SkPictureRecorder recorder;
734 // Create picture with 2 unbalanced saves
735 SkCanvas* canvas = recorder.beginRecording(100, 100);
737 canvas->translate(10, 10);
738 canvas->drawRect(rect, paint);
740 canvas->translate(10, 10);
741 canvas->drawRect(rect, paint);
742 SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording());
744 testCanvas.drawPicture(extraSavePicture);
745 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
748 set_canvas_to_save_count_4(&testCanvas);
751 // Create picture with 2 unbalanced restores
752 SkCanvas* canvas = recorder.beginRecording(100, 100);
754 canvas->translate(10, 10);
755 canvas->drawRect(rect, paint);
757 canvas->translate(10, 10);
758 canvas->drawRect(rect, paint);
763 SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording());
765 testCanvas.drawPicture(extraRestorePicture);
766 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
769 set_canvas_to_save_count_4(&testCanvas);
772 SkCanvas* canvas = recorder.beginRecording(100, 100);
773 canvas->translate(10, 10);
774 canvas->drawRect(rect, paint);
775 SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording());
777 testCanvas.drawPicture(noSavePicture);
778 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
779 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
783 static void test_peephole() {
786 SkPictureRecorder recorder;
788 for (int j = 0; j < 100; j++) {
789 SkRandom rand2(rand); // remember the seed
791 SkCanvas* canvas = recorder.beginRecording(100, 100);
793 for (int i = 0; i < 1000; ++i) {
794 rand_op(canvas, rand);
796 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
802 SkCanvas* canvas = recorder.beginRecording(100, 100);
803 SkRect rect = SkRect::MakeWH(50, 50);
805 for (int i = 0; i < 100; ++i) {
808 while (canvas->getSaveCount() > 1) {
809 canvas->clipRect(rect);
812 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
817 // Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
818 // should never do this.
819 static void test_bad_bitmap() {
820 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
823 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
824 SkPictureRecorder recorder;
825 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
826 recordingCanvas->drawBitmap(bm, 0, 0);
827 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
830 canvas.drawPicture(picture);
834 static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
835 SkPictureRecorder recorder;
836 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bitmap.width()),
837 SkIntToScalar(bitmap.height()));
838 canvas->drawBitmap(bitmap, 0, 0);
839 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
841 SkDynamicMemoryWStream wStream;
842 sk_tool_utils::PngPixelSerializer serializer;
843 picture->serialize(&wStream, &serializer);
844 return wStream.copyToData();
847 struct ErrorContext {
849 skiatest::Reporter* fReporter;
852 static void assert_one_parse_error_cb(SkError error, void* context) {
853 ErrorContext* errorContext = static_cast<ErrorContext*>(context);
854 errorContext->fErrors++;
855 // This test only expects one error, and that is a kParseError. If there are others,
856 // there is some unknown problem.
857 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
858 "This threw more errors than expected.");
859 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
860 SkGetLastErrorString());
863 static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
864 // Create a bitmap that will be encoded.
866 make_bm(&original, 100, 100, SK_ColorBLUE, true);
867 SkDynamicMemoryWStream wStream;
868 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
871 SkAutoDataUnref data(wStream.copyToData());
874 bool installSuccess = SkInstallDiscardablePixelRef(data, &bm);
875 REPORTER_ASSERT(reporter, installSuccess);
877 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
878 // Flattening original will follow the old path of performing an encode, while flattening bm
879 // will use the already encoded data.
880 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
881 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
882 REPORTER_ASSERT(reporter, picture1->equals(picture2));
883 // Now test that a parse error was generated when trying to create a new SkPicture without
884 // providing a function to decode the bitmap.
885 ErrorContext context;
887 context.fReporter = reporter;
888 SkSetErrorCallback(assert_one_parse_error_cb, &context);
889 SkMemoryStream pictureStream(picture1);
891 SkAutoTUnref<SkPicture> pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
892 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
894 SkSetErrorCallback(NULL, NULL);
897 static void test_clip_bound_opt(skiatest::Reporter* reporter) {
898 // Test for crbug.com/229011
899 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
900 SkIntToScalar(2), SkIntToScalar(2));
901 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
902 SkIntToScalar(1), SkIntToScalar(1));
903 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
904 SkIntToScalar(1), SkIntToScalar(1));
907 invPath.addOval(rect1);
908 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
912 path2.addOval(rect3);
914 SkPictureRecorder recorder;
916 // Testing conservative-raster-clip that is enabled by PictureRecord
918 SkCanvas* canvas = recorder.beginRecording(10, 10);
919 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
920 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
921 REPORTER_ASSERT(reporter, true == nonEmpty);
922 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
923 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
924 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
925 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
928 SkCanvas* canvas = recorder.beginRecording(10, 10);
929 canvas->clipPath(path, SkRegion::kIntersect_Op);
930 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
931 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
932 REPORTER_ASSERT(reporter, true == nonEmpty);
933 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
934 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
935 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
936 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
939 SkCanvas* canvas = recorder.beginRecording(10, 10);
940 canvas->clipPath(path, SkRegion::kIntersect_Op);
941 canvas->clipPath(invPath, SkRegion::kUnion_Op);
942 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
943 REPORTER_ASSERT(reporter, true == nonEmpty);
944 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
945 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
946 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
947 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
950 SkCanvas* canvas = recorder.beginRecording(10, 10);
951 canvas->clipPath(path, SkRegion::kDifference_Op);
952 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
953 REPORTER_ASSERT(reporter, true == nonEmpty);
954 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
955 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
956 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
957 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
960 SkCanvas* canvas = recorder.beginRecording(10, 10);
961 canvas->clipPath(path, SkRegion::kReverseDifference_Op);
962 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
963 // True clip is actually empty in this case, but the best
964 // determination we can make using only bounds as input is that the
965 // clip is included in the bounds of 'path'.
966 REPORTER_ASSERT(reporter, true == nonEmpty);
967 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
968 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
969 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
970 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
973 SkCanvas* canvas = recorder.beginRecording(10, 10);
974 canvas->clipPath(path, SkRegion::kIntersect_Op);
975 canvas->clipPath(path2, SkRegion::kXOR_Op);
976 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
977 REPORTER_ASSERT(reporter, true == nonEmpty);
978 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
979 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
980 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
981 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
986 * A canvas that records the number of clip commands.
988 class ClipCountingCanvas : public SkCanvas {
990 ClipCountingCanvas(int width, int height)
991 : INHERITED(width, height)
995 virtual void onClipRect(const SkRect& r,
997 ClipEdgeStyle edgeStyle) SK_OVERRIDE {
999 this->INHERITED::onClipRect(r, op, edgeStyle);
1002 virtual void onClipRRect(const SkRRect& rrect,
1004 ClipEdgeStyle edgeStyle)SK_OVERRIDE {
1006 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
1009 virtual void onClipPath(const SkPath& path,
1011 ClipEdgeStyle edgeStyle) SK_OVERRIDE {
1013 this->INHERITED::onClipPath(path, op, edgeStyle);
1016 void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE {
1018 this->INHERITED::onClipRegion(deviceRgn, op);
1021 unsigned getClipCount() const { return fClipCount; }
1024 unsigned fClipCount;
1026 typedef SkCanvas INHERITED;
1029 static void test_clip_expansion(skiatest::Reporter* reporter) {
1030 SkPictureRecorder recorder;
1031 SkCanvas* canvas = recorder.beginRecording(10, 10);
1033 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
1034 // The following expanding clip should not be skipped.
1035 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
1036 // Draw something so the optimizer doesn't just fold the world.
1038 p.setColor(SK_ColorBLUE);
1039 canvas->drawPaint(p);
1040 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1042 ClipCountingCanvas testCanvas(10, 10);
1043 picture->playback(&testCanvas);
1045 // Both clips should be present on playback.
1046 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
1049 static void test_hierarchical(skiatest::Reporter* reporter) {
1051 make_bm(&bm, 10, 10, SK_ColorRED, true);
1053 SkPictureRecorder recorder;
1055 recorder.beginRecording(10, 10);
1056 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording());
1057 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
1059 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
1060 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording());
1061 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
1064 SkCanvas* canvas = recorder.beginRecording(10, 10);
1065 canvas->drawPicture(childPlain);
1066 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording());
1067 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
1070 SkCanvas* canvas = recorder.beginRecording(10, 10);
1071 canvas->drawPicture(childWithBitmap);
1072 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording());
1073 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
1076 SkCanvas* canvas = recorder.beginRecording(10, 10);
1077 canvas->drawBitmap(bm, 0, 0);
1078 canvas->drawPicture(childPlain);
1079 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording());
1080 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
1083 SkCanvas* canvas = recorder.beginRecording(10, 10);
1084 canvas->drawBitmap(bm, 0, 0);
1085 canvas->drawPicture(childWithBitmap);
1086 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording());
1087 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
1091 static void test_gen_id(skiatest::Reporter* reporter) {
1093 SkPictureRecorder recorder;
1094 recorder.beginRecording(0, 0);
1095 SkAutoTUnref<SkPicture> empty(recorder.endRecording());
1097 // Empty pictures should still have a valid ID
1098 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
1100 SkCanvas* canvas = recorder.beginRecording(1, 1);
1101 canvas->drawARGB(255, 255, 255, 255);
1102 SkAutoTUnref<SkPicture> hasData(recorder.endRecording());
1103 // picture should have a non-zero id after recording
1104 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
1106 // both pictures should have different ids
1107 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
1110 static void test_bytes_used(skiatest::Reporter* reporter) {
1111 SkPictureRecorder recorder;
1113 recorder.beginRecording(0, 0);
1114 SkAutoTUnref<SkPicture> empty(recorder.endRecording());
1116 // Sanity check to make sure we aren't under-measuring.
1117 REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(empty.get()) >=
1118 sizeof(SkPicture) + sizeof(SkRecord));
1120 // Protect against any unintentional bloat.
1121 size_t approxUsed = SkPictureUtils::ApproximateBytesUsed(empty.get());
1122 REPORTER_ASSERT(reporter, approxUsed <= 136);
1124 // Sanity check of nested SkPictures.
1125 SkPictureRecorder r2;
1126 r2.beginRecording(0, 0);
1127 r2.getRecordingCanvas()->drawPicture(empty.get());
1128 SkAutoTUnref<SkPicture> nested(r2.endRecording());
1130 REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(nested.get()) >
1131 SkPictureUtils::ApproximateBytesUsed(empty.get()));
1134 DEF_TEST(Picture, reporter) {
1136 test_deleting_empty_picture();
1137 test_serializing_empty_picture();
1141 test_unbalanced_save_restores(reporter);
1144 test_gpu_veto(reporter);
1146 test_has_text(reporter);
1147 test_analysis(reporter);
1148 test_bitmap_with_encoded_data(reporter);
1149 test_clip_bound_opt(reporter);
1150 test_clip_expansion(reporter);
1151 test_hierarchical(reporter);
1152 test_gen_id(reporter);
1153 test_savelayer_extraction(reporter);
1154 test_bytes_used(reporter);
1157 static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
1158 const SkPaint paint;
1159 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
1160 const SkIRect irect = { 2, 2, 3, 3 };
1162 // Don't care what these record, as long as they're legal.
1163 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
1164 canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag);
1165 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
1166 canvas->drawSprite(bitmap, 1, 1);
1169 static void test_draw_bitmaps(SkCanvas* canvas) {
1171 draw_bitmaps(empty, canvas);
1172 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
1173 draw_bitmaps(empty, canvas);
1176 DEF_TEST(Picture_EmptyBitmap, r) {
1177 SkPictureRecorder recorder;
1178 test_draw_bitmaps(recorder.beginRecording(10, 10));
1179 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1182 DEF_TEST(Canvas_EmptyBitmap, r) {
1184 dst.allocN32Pixels(10, 10);
1185 SkCanvas canvas(dst);
1187 test_draw_bitmaps(&canvas);
1190 DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
1191 // This test is from crbug.com/344987.
1192 // The commands are:
1193 // saveLayer with paint that modifies alpha
1194 // drawBitmapRectToRect
1195 // drawBitmapRectToRect
1197 // The bug was that this structure was modified so that:
1198 // - The saveLayer and restore were eliminated
1199 // - The alpha was only applied to the first drawBitmapRectToRect
1201 // This test draws blue and red squares inside a 50% transparent
1202 // layer. Both colours should show up muted.
1203 // When the bug is present, the red square (the second bitmap)
1204 // shows upwith full opacity.
1207 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
1209 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
1210 SkPaint semiTransparent;
1211 semiTransparent.setAlpha(0x80);
1213 SkPictureRecorder recorder;
1214 SkCanvas* canvas = recorder.beginRecording(100, 100);
1215 canvas->drawARGB(0, 0, 0, 0);
1217 canvas->saveLayer(0, &semiTransparent);
1218 canvas->drawBitmap(blueBM, 25, 25);
1219 canvas->drawBitmap(redBM, 50, 50);
1222 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1224 // Now replay the picture back on another canvas
1225 // and check a couple of its pixels.
1227 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
1228 SkCanvas replayCanvas(replayBM);
1229 picture->playback(&replayCanvas);
1230 replayCanvas.flush();
1232 // With the bug present, at (55, 55) we would get a fully opaque red
1233 // intead of a dark red.
1234 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
1235 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
1238 struct CountingBBH : public SkBBoxHierarchy {
1239 mutable int searchCalls;
1242 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
1244 void search(const SkRect& query, SkTDArray<unsigned>* results) const SK_OVERRIDE {
1245 this->searchCalls++;
1248 void insert(const SkRect[], int) SK_OVERRIDE {}
1249 virtual size_t bytesUsed() const SK_OVERRIDE { return 0; }
1250 SkRect getRootBound() const SK_OVERRIDE { return rootBound; }
1253 class SpoonFedBBHFactory : public SkBBHFactory {
1255 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
1256 SkBBoxHierarchy* operator()(const SkRect&) const SK_OVERRIDE {
1260 SkBBoxHierarchy* fBBH;
1263 // When the canvas clip covers the full picture, we don't need to call the BBH.
1264 DEF_TEST(Picture_SkipBBH, r) {
1265 SkRect bound = SkRect::MakeWH(320, 240);
1266 CountingBBH bbh(bound);
1267 SpoonFedBBHFactory factory(&bbh);
1269 SkPictureRecorder recorder;
1270 recorder.beginRecording(bound, &factory);
1271 SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
1273 SkCanvas big(640, 480), small(300, 200);
1275 picture->playback(&big);
1276 REPORTER_ASSERT(r, bbh.searchCalls == 0);
1278 picture->playback(&small);
1279 REPORTER_ASSERT(r, bbh.searchCalls == 1);
1282 DEF_TEST(Picture_BitmapLeak, r) {
1283 SkBitmap mut, immut;
1284 mut.allocN32Pixels(300, 200);
1285 immut.allocN32Pixels(300, 200);
1286 immut.setImmutable();
1287 SkASSERT(!mut.isImmutable());
1288 SkASSERT(immut.isImmutable());
1290 // No one can hold a ref on our pixels yet.
1291 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1292 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1294 SkAutoTUnref<const SkPicture> pic;
1296 // we want the recorder to go out of scope before our subsequent checks, so we
1297 // place it inside local braces.
1298 SkPictureRecorder rec;
1299 SkCanvas* canvas = rec.beginRecording(1920, 1200);
1300 canvas->drawBitmap(mut, 0, 0);
1301 canvas->drawBitmap(immut, 800, 600);
1302 pic.reset(rec.endRecording());
1305 // The picture shares the immutable pixels but copies the mutable ones.
1306 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1307 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1309 // When the picture goes away, it's just our bitmaps holding the refs.
1311 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1312 REPORTER_ASSERT(r, immut.pixelRef()->unique());