blob: a6b61705d8a6ee16ee5e7951bc626e23faa52371 [file] [log] [blame]
robertphillips@google.comad7d4812013-04-12 15:13:35 +00001/*
2 * Copyright 2013 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 "gm.h"
9#include "SkDebugCanvas.h"
10#include "SkPictureFlat.h"
11
12#define WARN(msg) \
13 SkDebugf("%s:%d: %s\n", __FILE__, __LINE__, msg);
14
15namespace {
16
17// Do the commands in 'input' match the supplied pattern? Note: this is a pretty
skia.committer@gmail.com4bb50b22013-04-13 07:01:15 +000018// heavy-weight operation since we are drawing the picture into a debug canvas
robertphillips@google.comad7d4812013-04-12 15:13:35 +000019// to extract the commands.
20bool check_pattern(SkPicture& input, const SkTDArray<DrawType> &pattern) {
21 SkDebugCanvas debugCanvas(input.width(), input.height());
22 debugCanvas.setBounds(input.width(), input.height());
23 input.draw(&debugCanvas);
24
25 if (pattern.count() != debugCanvas.getSize()) {
26 return false;
27 }
28
29 for (int i = 0; i < pattern.count(); ++i) {
30 if (pattern[i] != debugCanvas.getDrawCommandAt(i)->getType()) {
31 return false;
32 }
33 }
34
35 return true;
36}
37
38// construct the pattern removed by the SkPictureRecord::remove_save_layer1
39// optimization, i.e.:
40// SAVE_LAYER
41// DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
42// RESTORE
43//
44// saveLayerHasPaint - control if the saveLayer has a paint (the optimization
45// takes a different path if this is false)
46// dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization
47// takes a different path if this is false)
48// colorsMatch - control if the saveLayer and dbmr2r paint colors
49// match (the optimization will fail if they do not)
50SkPicture* create_save_layer_opt_1(SkTDArray<DrawType> *preOptPattern,
51 SkTDArray<DrawType> *postOptPattern,
52 const SkBitmap& checkerBoard,
53 bool saveLayerHasPaint,
54 bool dbmr2rHasPaint,
55 bool colorsMatch) {
56 // Create the pattern that should trigger the optimization
57 preOptPattern->setCount(5);
58 (*preOptPattern)[0] = SAVE;
59 (*preOptPattern)[1] = SAVE_LAYER;
60 (*preOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
61 (*preOptPattern)[3] = RESTORE;
62 (*preOptPattern)[4] = RESTORE;
63
64 if (colorsMatch) {
65 // Create the pattern that should appear after the optimization
66 postOptPattern->setCount(5);
67 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
68 (*postOptPattern)[1] = SAVE;
69 (*postOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
70 (*postOptPattern)[3] = RESTORE;
71 (*postOptPattern)[4] = RESTORE;
72 } else {
73 // Create the pattern that appears if the optimization doesn't fire
74 postOptPattern->setCount(7);
75 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
76 (*postOptPattern)[1] = SAVE;
77 (*postOptPattern)[2] = SAVE_LAYER;
78 (*postOptPattern)[3] = DRAW_BITMAP_RECT_TO_RECT;
79 (*postOptPattern)[4] = RESTORE;
80 (*postOptPattern)[5] = RESTORE;
81 (*postOptPattern)[6] = RESTORE;
82 }
83
84 SkPicture* result = new SkPicture;
85
86 // have to disable the optimizations while generating the picture
87 SkCanvas* canvas = result->beginRecording(100, 100,
88 SkPicture::kDisableRecordOptimizations_RecordingFlag);
89
90 SkPaint saveLayerPaint;
91 saveLayerPaint.setColor(0xCC000000);
92
93 // saveLayer's 'bounds' parameter must be NULL for this optimization
94 if (saveLayerHasPaint) {
95 canvas->saveLayer(NULL, &saveLayerPaint);
96 } else {
97 canvas->saveLayer(NULL, NULL);
98 }
99
100 SkRect rect = { 10, 10, 90, 90 };
101
102 // The dbmr2r's paint must be opaque
103 SkPaint dbmr2rPaint;
104 if (colorsMatch) {
105 dbmr2rPaint.setColor(0xFF000000);
106 } else {
107 dbmr2rPaint.setColor(0xFFFF0000);
108 }
109
110 if (dbmr2rHasPaint) {
111 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
112 } else {
113 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
114 }
115 canvas->restore();
116
117 result->endRecording();
118
119 return result;
120}
121
122// straight-ahead version that is seen in the skps
123SkPicture* create_save_layer_opt_1_v1(SkTDArray<DrawType> *preOptPattern,
124 SkTDArray<DrawType> *postOptPattern,
125 const SkBitmap& checkerBoard) {
126 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
127 true, // saveLayer has a paint
128 true, // dbmr2r has a paint
129 true); // and the colors match
130}
131
132// alternate version that should still succeed
133SkPicture* create_save_layer_opt_1_v2(SkTDArray<DrawType> *preOptPattern,
134 SkTDArray<DrawType> *postOptPattern,
135 const SkBitmap& checkerBoard) {
136 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
137 false, // saveLayer doesn't have a paint!
138 true, // dbmr2r has a paint
139 true); // color matching not really applicable
140}
141
142// alternate version that should still succeed
143SkPicture* create_save_layer_opt_1_v3(SkTDArray<DrawType> *preOptPattern,
144 SkTDArray<DrawType> *postOptPattern,
145 const SkBitmap& checkerBoard) {
146 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
147 true, // saveLayer has a paint
148 false, // dbmr2r doesn't have a paint!
149 true); // color matching not really applicable
150}
151
152// version in which the optimization fails b.c. the colors don't match
153SkPicture* create_save_layer_opt_1_v4(SkTDArray<DrawType> *preOptPattern,
154 SkTDArray<DrawType> *postOptPattern,
155 const SkBitmap& checkerBoard) {
156 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
157 true, // saveLayer has a paint
158 true, // dbmr2r has a paint
159 false); // and the colors don't match!
160}
161
162// construct the pattern removed by the SkPictureRecord::remove_save_layer2
163// optimization, i.e.:
164// SAVE_LAYER (with NULL == bounds)
165// SAVE
166// CLIP_RECT
167// DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
168// RESTORE
169// RESTORE
170//
171// saveLayerHasPaint - control if the saveLayer has a paint (the optimization
172// takes a different path if this is false)
173// dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization
174// takes a different path if this is false)
175// colorsMatch - control if the saveLayer and dbmr2r paint colors
176// match (the optimization will fail if they do not)
177SkPicture* create_save_layer_opt_2(SkTDArray<DrawType> *preOptPattern,
178 SkTDArray<DrawType> *postOptPattern,
179 const SkBitmap& checkerBoard,
180 bool saveLayerHasPaint,
181 bool dbmr2rHasPaint,
182 bool colorsMatch) {
183 // Create the pattern that should trigger the optimization
184 preOptPattern->setCount(8);
185 (*preOptPattern)[0] = SAVE;
186 (*preOptPattern)[1] = SAVE_LAYER;
187 (*preOptPattern)[2] = SAVE;
188 (*preOptPattern)[3] = CLIP_RECT;
189 (*preOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
190 (*preOptPattern)[5] = RESTORE;
191 (*preOptPattern)[6] = RESTORE;
192 (*preOptPattern)[7] = RESTORE;
193
194 if (colorsMatch) {
195 // Create the pattern that should appear after the optimization
196 postOptPattern->setCount(8);
197 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
198 (*postOptPattern)[1] = SAVE;
199 (*postOptPattern)[2] = SAVE;
200 (*postOptPattern)[3] = CLIP_RECT;
201 (*postOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
202 (*postOptPattern)[5] = RESTORE;
203 (*postOptPattern)[6] = RESTORE;
204 (*postOptPattern)[7] = RESTORE;
205 } else {
206 // Create the pattern that appears if the optimization doesn't fire
207 postOptPattern->setCount(10);
208 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
209 (*postOptPattern)[1] = SAVE;
210 (*postOptPattern)[2] = SAVE_LAYER;
211 (*postOptPattern)[3] = SAVE;
212 (*postOptPattern)[4] = CLIP_RECT;
213 (*postOptPattern)[5] = DRAW_BITMAP_RECT_TO_RECT;
214 (*postOptPattern)[6] = RESTORE;
215 (*postOptPattern)[7] = RESTORE;
216 (*postOptPattern)[8] = RESTORE;
217 (*postOptPattern)[9] = RESTORE;
218 }
219
220 SkPicture* result = new SkPicture;
221
222 // have to disable the optimizations while generating the picture
223 SkCanvas* canvas = result->beginRecording(100, 100,
224 SkPicture::kDisableRecordOptimizations_RecordingFlag);
225
226 SkPaint saveLayerPaint;
227 saveLayerPaint.setColor(0xCC000000);
228
229 // saveLayer's 'bounds' parameter must be NULL for this optimization
230 if (saveLayerHasPaint) {
231 canvas->saveLayer(NULL, &saveLayerPaint);
232 } else {
233 canvas->saveLayer(NULL, NULL);
234 }
235
236 canvas->save();
237
238 SkRect rect = { 10, 10, 90, 90 };
239 canvas->clipRect(rect);
240
241 // The dbmr2r's paint must be opaque
242 SkPaint dbmr2rPaint;
243 if (colorsMatch) {
244 dbmr2rPaint.setColor(0xFF000000);
245 } else {
246 dbmr2rPaint.setColor(0xFFFF0000);
247 }
248
249 if (dbmr2rHasPaint) {
250 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
251 } else {
252 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
253 }
254 canvas->restore();
255 canvas->restore();
256
257 result->endRecording();
258
259 return result;
260}
261
262// straight-ahead version that is seen in the skps
263SkPicture* create_save_layer_opt_2_v1(SkTDArray<DrawType> *preOptPattern,
264 SkTDArray<DrawType> *postOptPattern,
265 const SkBitmap& checkerBoard) {
266 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
267 true, // saveLayer has a paint
268 true, // dbmr2r has a paint
269 true); // and the colors match
270}
271
272// alternate version that should still succeed
273SkPicture* create_save_layer_opt_2_v2(SkTDArray<DrawType> *preOptPattern,
274 SkTDArray<DrawType> *postOptPattern,
275 const SkBitmap& checkerBoard) {
276 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
277 false, // saveLayer doesn't have a paint!
278 true, // dbmr2r has a paint
279 true); // color matching not really applicable
280}
281
282// alternate version that should still succeed
283SkPicture* create_save_layer_opt_2_v3(SkTDArray<DrawType> *preOptPattern,
284 SkTDArray<DrawType> *postOptPattern,
285 const SkBitmap& checkerBoard) {
286 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
287 true, // saveLayer has a paint
288 false, // dbmr2r doesn't have a paint!
289 true); // color matching not really applicable
290}
291
292// version in which the optimization fails b.c. the colors don't match
293SkPicture* create_save_layer_opt_2_v4(SkTDArray<DrawType> *preOptPattern,
294 SkTDArray<DrawType> *postOptPattern,
295 const SkBitmap& checkerBoard) {
296 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
297 true, // saveLayer has a paint
298 true, // dbmr2r has a paint
299 false); // and the colors don't match!
300}
301
302};
303
304
305// As our .skp optimizations get folded into the captured skps our code will
306// no longer be locally exercised. This GM manually constructs the patterns
307// our optimizations will remove to test them. It acts as both a GM and a unit
308// test
309class OptimizationsGM : public skiagm::GM {
310public:
311 OptimizationsGM() {
312 this->makeCheckerboard();
313 }
314
315 static const int kWidth = 800;
316 static const int kHeight = 800;
317
318protected:
319 SkString onShortName() {
320 return SkString("optimizations");
321 }
322
323 SkISize onISize() { return SkISize::Make(kWidth, kHeight); }
324
325 typedef SkPicture* (*PFCreateOpt)(SkTDArray<DrawType> *preOptPattern,
326 SkTDArray<DrawType> *postOptPattern,
327 const SkBitmap& checkerBoard);
328
329 virtual void onDraw(SkCanvas* canvas) {
330
331 PFCreateOpt gOpts[] = {
332 create_save_layer_opt_1_v1,
333 create_save_layer_opt_1_v2,
334 create_save_layer_opt_1_v3,
335 create_save_layer_opt_1_v4,
336 create_save_layer_opt_2_v1,
337 create_save_layer_opt_2_v2,
338 create_save_layer_opt_2_v3,
339 create_save_layer_opt_2_v4,
340 };
341
342 SkTDArray<DrawType> prePattern, postPattern;
343 int xPos = 0, yPos = 0;
344
345 for (size_t i = 0; i < SK_ARRAY_COUNT(gOpts); ++i) {
346 SkAutoTUnref<SkPicture> pre((*gOpts[i])(&prePattern, &postPattern, fCheckerboard));
347
348 if (!(check_pattern(*pre, prePattern))) {
349 WARN("Pre optimization pattern mismatch");
350 SkASSERT(0);
351 }
352
353 canvas->save();
354 canvas->translate(SkIntToScalar(xPos), SkIntToScalar(yPos));
355 pre->draw(canvas);
356 xPos += pre->width();
357 canvas->restore();
358
359 // re-render the 'pre' picture and thus 'apply' the optimization
360 SkAutoTUnref<SkPicture> post(new SkPicture);
361
362 SkCanvas* recordCanvas = post->beginRecording(pre->width(), pre->height());
363
364 pre->draw(recordCanvas);
365
366 post->endRecording();
367
368 if (!(check_pattern(*post, postPattern))) {
369 WARN("Post optimization pattern mismatch");
370 SkASSERT(0);
371 }
372
373 canvas->save();
374 canvas->translate(SkIntToScalar(xPos), SkIntToScalar(yPos));
375 post->draw(canvas);
376 xPos += post->width();
377 canvas->restore();
378
379 if (xPos >= kWidth) {
380 // start a new line
381 xPos = 0;
382 yPos += post->height();
383 }
384
385 // TODO: we could also render the pre and post pictures to bitmaps
386 // and manually compare them in this method
387 }
388 }
389
390private:
391 void makeCheckerboard() {
392 static const unsigned int kCheckerboardWidth = 16;
393 static const unsigned int kCheckerboardHeight = 16;
394
skia.committer@gmail.com4bb50b22013-04-13 07:01:15 +0000395 fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config,
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000396 kCheckerboardWidth, kCheckerboardHeight);
397 fCheckerboard.allocPixels();
398 SkAutoLockPixels lock(fCheckerboard);
399 for (unsigned int y = 0; y < kCheckerboardHeight; y += 2) {
400 SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
401 for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) {
402 *scanline++ = 0xFFFFFFFF;
403 *scanline++ = 0xFF000000;
404 }
405 scanline = fCheckerboard.getAddr32(0, y + 1);
406 for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) {
407 *scanline++ = 0xFF000000;
408 *scanline++ = 0xFFFFFFFF;
409 }
410 }
411 }
412
413 SkBitmap fCheckerboard;
414
415 typedef skiagm::GM INHERITED;
416};
417
418//////////////////////////////////////////////////////////////////////////////
419
420DEF_GM( return new OptimizationsGM; )