RecordDrawTest.cpp revision 02d2b9831579173e783569530ab7bae08de907e9
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 "SkDrawPictureCallback.h" 13#include "SkDropShadowImageFilter.h" 14#include "SkImagePriv.h" 15#include "SkRecord.h" 16#include "SkRecordDraw.h" 17#include "SkRecordOpts.h" 18#include "SkRecorder.h" 19#include "SkRecords.h" 20#include "SkSurface.h" 21 22static const int W = 1920, H = 1080; 23 24class JustOneDraw : public SkDrawPictureCallback { 25public: 26 JustOneDraw() : fCalls(0) {} 27 28 virtual bool abortDrawing() SK_OVERRIDE { return fCalls++ > 0; } 29private: 30 int fCalls; 31}; 32 33DEF_TEST(RecordDraw_LazySaves, r) { 34 // Record two commands. 35 SkRecord record; 36 SkRecorder recorder(&record, W, H); 37 38 REPORTER_ASSERT(r, 0 == record.count()); 39 recorder.save(); 40 REPORTER_ASSERT(r, 0 == record.count()); // the save was not recorded (yet) 41 recorder.drawColor(SK_ColorRED); 42 REPORTER_ASSERT(r, 1 == record.count()); 43 recorder.scale(2, 2); 44 REPORTER_ASSERT(r, 3 == record.count()); // now we see the save 45 recorder.restore(); 46 REPORTER_ASSERT(r, 4 == record.count()); 47 48 assert_type<SkRecords::DrawPaint>(r, record, 0); 49 assert_type<SkRecords::Save> (r, record, 1); 50 assert_type<SkRecords::SetMatrix>(r, record, 2); 51 assert_type<SkRecords::Restore> (r, record, 3); 52 53 recorder.save(); 54 recorder.save(); 55 recorder.restore(); 56 recorder.restore(); 57 REPORTER_ASSERT(r, 4 == record.count()); 58} 59 60DEF_TEST(RecordDraw_Abort, r) { 61 // Record two commands. 62 SkRecord record; 63 SkRecorder recorder(&record, W, H); 64 recorder.drawRect(SkRect::MakeWH(200, 300), SkPaint()); 65 recorder.clipRect(SkRect::MakeWH(100, 200)); 66 67 SkRecord rerecord; 68 SkRecorder canvas(&rerecord, W, H); 69 70 JustOneDraw callback; 71 SkRecordDraw(record, &canvas, NULL, NULL, 0, NULL/*bbh*/, &callback); 72 73 REPORTER_ASSERT(r, 1 == count_instances_of_type<SkRecords::DrawRect>(rerecord)); 74 REPORTER_ASSERT(r, 0 == count_instances_of_type<SkRecords::ClipRect>(rerecord)); 75} 76 77DEF_TEST(RecordDraw_Unbalanced, r) { 78 SkRecord record; 79 SkRecorder recorder(&record, W, H); 80 recorder.save(); // We won't balance this, but SkRecordDraw will for us. 81 recorder.scale(2, 2); 82 83 SkRecord rerecord; 84 SkRecorder canvas(&rerecord, W, H); 85 SkRecordDraw(record, &canvas, NULL, NULL, 0, NULL/*bbh*/, NULL/*callback*/); 86 87 int save_count = count_instances_of_type<SkRecords::Save>(rerecord); 88 int restore_count = count_instances_of_type<SkRecords::Save>(rerecord); 89 REPORTER_ASSERT(r, save_count == restore_count); 90} 91 92DEF_TEST(RecordDraw_SetMatrixClobber, r) { 93 // Set up an SkRecord that just scales by 2x,3x. 94 SkRecord scaleRecord; 95 SkRecorder scaleCanvas(&scaleRecord, W, H); 96 SkMatrix scale; 97 scale.setScale(2, 3); 98 scaleCanvas.setMatrix(scale); 99 100 // Set up an SkRecord with an initial +20, +20 translate. 101 SkRecord translateRecord; 102 SkRecorder translateCanvas(&translateRecord, W, H); 103 SkMatrix translate; 104 translate.setTranslate(20, 20); 105 translateCanvas.setMatrix(translate); 106 107 SkRecordDraw(scaleRecord, &translateCanvas, NULL, NULL, 0, NULL/*bbh*/, NULL/*callback*/); 108 REPORTER_ASSERT(r, 4 == translateRecord.count()); 109 assert_type<SkRecords::SetMatrix>(r, translateRecord, 0); 110 assert_type<SkRecords::Save> (r, translateRecord, 1); 111 assert_type<SkRecords::SetMatrix>(r, translateRecord, 2); 112 assert_type<SkRecords::Restore> (r, translateRecord, 3); 113 114 // When we look at translateRecord now, it should have its first +20,+20 translate, 115 // then a 2x,3x scale that's been concatted with that +20,+20 translate. 116 const SkRecords::SetMatrix* setMatrix; 117 setMatrix = assert_type<SkRecords::SetMatrix>(r, translateRecord, 0); 118 REPORTER_ASSERT(r, setMatrix->matrix == translate); 119 120 setMatrix = assert_type<SkRecords::SetMatrix>(r, translateRecord, 2); 121 SkMatrix expected = scale; 122 expected.postConcat(translate); 123 REPORTER_ASSERT(r, setMatrix->matrix == expected); 124} 125 126struct TestBBH : public SkBBoxHierarchy { 127 virtual void insert(SkAutoTMalloc<SkRect>* boundsArray, int N) SK_OVERRIDE { 128 fEntries.setCount(N); 129 for (int i = 0; i < N; i++) { 130 Entry e = { (unsigned)i, (*boundsArray)[i] }; 131 fEntries[i] = e; 132 } 133 } 134 135 virtual void search(const SkRect& query, SkTDArray<unsigned>* results) const SK_OVERRIDE {} 136 virtual size_t bytesUsed() const SK_OVERRIDE { return 0; } 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 // Font metrics are somewhat platform dependent, so these assertions may need to be adjusted. 195 // But these particular numbers are left over from the days when we used to wildly overestimate 196 // font metrics, so these assertions should actually be pretty safe. 197 REPORTER_ASSERT(r, SkRect::MakeLTRB(0, 0,140, 60).contains(bbh.fEntries[0].bounds)); 198 REPORTER_ASSERT(r, SkRect::MakeLTRB(0,20,180,100).contains(bbh.fEntries[1].bounds)); 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 REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[0].bounds, SkRect::MakeLTRB(10, 10, 40, 40))); 279 REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[1].bounds, SkRect::MakeLTRB(20, 20, 30, 30))); 280 REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[2].bounds, SkRect::MakeLTRB(10, 10, 40, 40))); 281} 282 283DEF_TEST(RecordDraw_drawImage, r){ 284 class SkCanvasMock : public SkCanvas { 285 public: 286 SkCanvasMock(int width, int height) : SkCanvas(width, height) { 287 this->resetTestValues(); 288 } 289 virtual ~SkCanvasMock() {} 290 virtual void drawImage(const SkImage* image, SkScalar left, SkScalar top, 291 const SkPaint* paint = NULL) SK_OVERRIDE { 292 293 fDrawImageCalled = true; 294 } 295 296 virtual void drawImageRect(const SkImage* image, const SkRect* src, 297 const SkRect& dst, 298 const SkPaint* paint = NULL) SK_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