1/* 2 * Copyright 2014 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 8#include "Test.h" 9#include "RecordTestUtils.h" 10 11#include "SkDebugCanvas.h" 12#include "SkDropShadowImageFilter.h" 13#include "SkImagePriv.h" 14#include "SkRecord.h" 15#include "SkRecordDraw.h" 16#include "SkRecordOpts.h" 17#include "SkRecorder.h" 18#include "SkRecords.h" 19#include "SkSurface.h" 20 21static const int W = 1920, H = 1080; 22 23class JustOneDraw : public SkPicture::AbortCallback { 24public: 25 JustOneDraw() : fCalls(0) {} 26 27 bool abort() override { return fCalls++ > 0; } 28private: 29 int fCalls; 30}; 31 32DEF_TEST(RecordDraw_LazySaves, r) { 33 // Record two commands. 34 SkRecord record; 35 SkRecorder recorder(&record, W, H); 36 37 REPORTER_ASSERT(r, 0 == record.count()); 38 recorder.save(); 39 REPORTER_ASSERT(r, 0 == record.count()); // the save was not recorded (yet) 40 recorder.drawColor(SK_ColorRED); 41 REPORTER_ASSERT(r, 1 == record.count()); 42 recorder.scale(2, 2); 43 REPORTER_ASSERT(r, 3 == record.count()); // now we see the save 44 recorder.restore(); 45 REPORTER_ASSERT(r, 4 == record.count()); 46 47 assert_type<SkRecords::DrawPaint>(r, record, 0); 48 assert_type<SkRecords::Save> (r, record, 1); 49 assert_type<SkRecords::SetMatrix>(r, record, 2); 50 assert_type<SkRecords::Restore> (r, record, 3); 51 52 recorder.save(); 53 recorder.save(); 54 recorder.restore(); 55 recorder.restore(); 56 REPORTER_ASSERT(r, 4 == record.count()); 57} 58 59DEF_TEST(RecordDraw_Abort, r) { 60 // Record two commands. 61 SkRecord record; 62 SkRecorder recorder(&record, W, H); 63 recorder.drawRect(SkRect::MakeWH(200, 300), SkPaint()); 64 recorder.clipRect(SkRect::MakeWH(100, 200)); 65 66 SkRecord rerecord; 67 SkRecorder canvas(&rerecord, W, H); 68 69 JustOneDraw callback; 70 SkRecordDraw(record, &canvas, NULL, NULL, 0, NULL/*bbh*/, &callback); 71 72 REPORTER_ASSERT(r, 1 == count_instances_of_type<SkRecords::DrawRect>(rerecord)); 73 REPORTER_ASSERT(r, 0 == count_instances_of_type<SkRecords::ClipRect>(rerecord)); 74} 75 76DEF_TEST(RecordDraw_Unbalanced, r) { 77 SkRecord record; 78 SkRecorder recorder(&record, W, H); 79 recorder.save(); // We won't balance this, but SkRecordDraw will for us. 80 recorder.scale(2, 2); 81 82 SkRecord rerecord; 83 SkRecorder canvas(&rerecord, W, H); 84 SkRecordDraw(record, &canvas, NULL, NULL, 0, NULL/*bbh*/, NULL/*callback*/); 85 86 int save_count = count_instances_of_type<SkRecords::Save>(rerecord); 87 int restore_count = count_instances_of_type<SkRecords::Save>(rerecord); 88 REPORTER_ASSERT(r, save_count == restore_count); 89} 90 91DEF_TEST(RecordDraw_SetMatrixClobber, r) { 92 // Set up an SkRecord that just scales by 2x,3x. 93 SkRecord scaleRecord; 94 SkRecorder scaleCanvas(&scaleRecord, W, H); 95 SkMatrix scale; 96 scale.setScale(2, 3); 97 scaleCanvas.setMatrix(scale); 98 99 // Set up an SkRecord with an initial +20, +20 translate. 100 SkRecord translateRecord; 101 SkRecorder translateCanvas(&translateRecord, W, H); 102 SkMatrix translate; 103 translate.setTranslate(20, 20); 104 translateCanvas.setMatrix(translate); 105 106 SkRecordDraw(scaleRecord, &translateCanvas, NULL, NULL, 0, NULL/*bbh*/, NULL/*callback*/); 107 REPORTER_ASSERT(r, 4 == translateRecord.count()); 108 assert_type<SkRecords::SetMatrix>(r, translateRecord, 0); 109 assert_type<SkRecords::Save> (r, translateRecord, 1); 110 assert_type<SkRecords::SetMatrix>(r, translateRecord, 2); 111 assert_type<SkRecords::Restore> (r, translateRecord, 3); 112 113 // When we look at translateRecord now, it should have its first +20,+20 translate, 114 // then a 2x,3x scale that's been concatted with that +20,+20 translate. 115 const SkRecords::SetMatrix* setMatrix; 116 setMatrix = assert_type<SkRecords::SetMatrix>(r, translateRecord, 0); 117 REPORTER_ASSERT(r, setMatrix->matrix == translate); 118 119 setMatrix = assert_type<SkRecords::SetMatrix>(r, translateRecord, 2); 120 SkMatrix expected = scale; 121 expected.postConcat(translate); 122 REPORTER_ASSERT(r, setMatrix->matrix == expected); 123} 124 125struct TestBBH : public SkBBoxHierarchy { 126 void insert(const SkRect boundsArray[], int N) override { 127 fEntries.setCount(N); 128 for (int i = 0; i < N; i++) { 129 Entry e = { (unsigned)i, boundsArray[i] }; 130 fEntries[i] = e; 131 } 132 } 133 134 void search(const SkRect& query, SkTDArray<unsigned>* results) const override {} 135 size_t bytesUsed() const override { return 0; } 136 SkRect getRootBound() const override { return SkRect::MakeEmpty(); } 137 138 struct Entry { 139 unsigned opIndex; 140 SkRect bounds; 141 }; 142 SkTDArray<Entry> fEntries; 143}; 144 145// Like a==b, with a little slop recognizing that float equality can be weird. 146static bool sloppy_rect_eq(SkRect a, SkRect b) { 147 SkRect inset(a), outset(a); 148 inset.inset(1, 1); 149 outset.outset(1, 1); 150 return outset.contains(b) && !inset.contains(b); 151} 152 153// This test is not meant to make total sense yet. It's testing the status quo 154// of SkRecordFillBounds(), which itself doesn't make total sense yet. 155DEF_TEST(RecordDraw_BBH, r) { 156 SkRecord record; 157 SkRecorder recorder(&record, W, H); 158 recorder.save(); 159 recorder.clipRect(SkRect::MakeWH(400, 500)); 160 recorder.scale(2, 2); 161 recorder.drawRect(SkRect::MakeWH(320, 240), SkPaint()); 162 recorder.restore(); 163 164 TestBBH bbh; 165 SkRecordFillBounds(SkRect::MakeWH(SkIntToScalar(W), SkIntToScalar(H)), record, &bbh); 166 167 REPORTER_ASSERT(r, bbh.fEntries.count() == 5); 168 for (int i = 0; i < bbh.fEntries.count(); i++) { 169 REPORTER_ASSERT(r, bbh.fEntries[i].opIndex == (unsigned)i); 170 171 REPORTER_ASSERT(r, sloppy_rect_eq(SkRect::MakeWH(400, 480), bbh.fEntries[i].bounds)); 172 } 173} 174 175// A regression test for crbug.com/409110. 176DEF_TEST(RecordDraw_TextBounds, r) { 177 SkRecord record; 178 SkRecorder recorder(&record, W, H); 179 180 // Two Chinese characters in UTF-8. 181 const char text[] = { '\xe6', '\xbc', '\xa2', '\xe5', '\xad', '\x97' }; 182 const size_t bytes = SK_ARRAY_COUNT(text); 183 184 const SkScalar xpos[] = { 10, 20 }; 185 recorder.drawPosTextH(text, bytes, xpos, 30, SkPaint()); 186 187 const SkPoint pos[] = { {40, 50}, {60, 70} }; 188 recorder.drawPosText(text, bytes, pos, SkPaint()); 189 190 TestBBH bbh; 191 SkRecordFillBounds(SkRect::MakeWH(SkIntToScalar(W), SkIntToScalar(H)), record, &bbh); 192 REPORTER_ASSERT(r, bbh.fEntries.count() == 2); 193 194 // We can make these next assertions confidently because SkRecordFillBounds 195 // builds its bounds by overestimating font metrics in a platform-independent way. 196 // If that changes, these tests will need to be more flexible. 197 REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[0].bounds, SkRect::MakeLTRB(0, 0, 140, 60))); 198 REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[1].bounds, SkRect::MakeLTRB(0, 20, 180, 100))); 199} 200 201// Base test to ensure start/stop range is respected 202DEF_TEST(RecordDraw_PartialStartStop, r) { 203 static const int kWidth = 10, kHeight = 10; 204 205 SkRect r1 = { 0, 0, kWidth, kHeight }; 206 SkRect r2 = { 0, 0, kWidth, kHeight/2 }; 207 SkRect r3 = { 0, 0, kWidth/2, kHeight }; 208 SkPaint p; 209 210 SkRecord record; 211 SkRecorder recorder(&record, kWidth, kHeight); 212 recorder.drawRect(r1, p); 213 recorder.drawRect(r2, p); 214 recorder.drawRect(r3, p); 215 216 SkRecord rerecord; 217 SkRecorder canvas(&rerecord, kWidth, kHeight); 218 SkRecordPartialDraw(record, &canvas, NULL, 0, 1, 2, SkMatrix::I()); // replay just drawRect of r2 219 220 REPORTER_ASSERT(r, 1 == count_instances_of_type<SkRecords::DrawRect>(rerecord)); 221 int index = find_first_instances_of_type<SkRecords::DrawRect>(rerecord); 222 const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, rerecord, index); 223 REPORTER_ASSERT(r, drawRect->rect == r2); 224} 225 226// A regression test for crbug.com/415468 and skbug.com/2957. 227// 228// This also now serves as a regression test for crbug.com/418417. We used to adjust the 229// bounds for the saveLayer, clip, and restore to be greater than the bounds of the picture. 230// (We were applying the saveLayer paint to the bounds after restore, which makes no sense.) 231DEF_TEST(RecordDraw_SaveLayerAffectsClipBounds, r) { 232 SkRecord record; 233 SkRecorder recorder(&record, 50, 50); 234 235 // We draw a rectangle with a long drop shadow. We used to not update the clip 236 // bounds based on SaveLayer paints, so the drop shadow could be cut off. 237 SkPaint paint; 238 paint.setImageFilter(SkDropShadowImageFilter::Create(20, 0, 0, 0, SK_ColorBLACK, 239 SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode))->unref(); 240 241 recorder.saveLayer(NULL, &paint); 242 recorder.clipRect(SkRect::MakeWH(20, 40)); 243 recorder.drawRect(SkRect::MakeWH(20, 40), SkPaint()); 244 recorder.restore(); 245 246 // Under the original bug, the right edge value of the drawRect would be 20 less than asserted 247 // here because we intersected it with a clip that had not been adjusted for the drop shadow. 248 // 249 // The second bug showed up as adjusting the picture bounds (0,0,50,50) by the drop shadow too. 250 // The saveLayer, clipRect, and restore bounds were incorrectly (0,0,70,50). 251 TestBBH bbh; 252 SkRecordFillBounds(SkRect::MakeWH(50, 50), record, &bbh); 253 REPORTER_ASSERT(r, bbh.fEntries.count() == 4); 254 REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[0].bounds, SkRect::MakeLTRB(0, 0, 50, 50))); 255 REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[1].bounds, SkRect::MakeLTRB(0, 0, 50, 50))); 256 REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[2].bounds, SkRect::MakeLTRB(0, 0, 40, 40))); 257 REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[3].bounds, SkRect::MakeLTRB(0, 0, 50, 50))); 258} 259 260// When a saveLayer provides an explicit bound and has a complex paint (e.g., one that 261// affects transparent black), that bound should serve to shrink the area of the required 262// backing store. 263DEF_TEST(RecordDraw_SaveLayerBoundsAffectsClipBounds, r) { 264 SkRecord record; 265 SkRecorder recorder(&record, 50, 50); 266 267 SkPaint p; 268 p.setXfermodeMode(SkXfermode::kSrc_Mode); 269 270 SkRect bounds = SkRect::MakeLTRB(10, 10, 40, 40); 271 recorder.saveLayer(&bounds, &p); 272 recorder.drawRect(SkRect::MakeLTRB(20, 20, 30, 30), SkPaint()); 273 recorder.restore(); 274 275 TestBBH bbh; 276 SkRecordFillBounds(SkRect::MakeWH(50, 50), record, &bbh); 277 REPORTER_ASSERT(r, bbh.fEntries.count() == 3); 278 if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) { 279 REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[0].bounds, SkRect::MakeLTRB(10, 10, 40, 40))); 280 REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[1].bounds, SkRect::MakeLTRB(20, 20, 30, 30))); 281 REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[2].bounds, SkRect::MakeLTRB(10, 10, 40, 40))); 282 } 283} 284 285DEF_TEST(RecordDraw_drawImage, r){ 286 class SkCanvasMock : public SkCanvas { 287 public: 288 SkCanvasMock(int width, int height) : SkCanvas(width, height) { 289 this->resetTestValues(); 290 } 291 292 void onDrawImage(const SkImage* image, SkScalar left, SkScalar top, 293 const SkPaint* paint) override { 294 fDrawImageCalled = true; 295 } 296 297 void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, 298 const SkPaint* paint) override { 299 fDrawImageRectCalled = true; 300 } 301 302 void resetTestValues() { 303 fDrawImageCalled = fDrawImageRectCalled = false; 304 } 305 306 bool fDrawImageCalled; 307 bool fDrawImageRectCalled; 308 }; 309 310 SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(10, 10)); 311 surface->getCanvas()->clear(SK_ColorGREEN); 312 SkAutoTUnref<SkImage> image(surface->newImageSnapshot()); 313 314 SkCanvasMock canvas(10, 10); 315 316 { 317 SkRecord record; 318 SkRecorder recorder(&record, 10, 10); 319 recorder.drawImage(image, 0, 0); 320 SkRecordDraw(record, &canvas, NULL, NULL, 0, NULL, 0); 321 } 322 REPORTER_ASSERT(r, canvas.fDrawImageCalled); 323 canvas.resetTestValues(); 324 325 { 326 SkRecord record; 327 SkRecorder recorder(&record, 10, 10); 328 recorder.drawImageRect(image, 0, SkRect::MakeWH(10, 10)); 329 SkRecordDraw(record, &canvas, NULL, NULL, 0, NULL, 0); 330 } 331 REPORTER_ASSERT(r, canvas.fDrawImageRectCalled); 332 333} 334