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