• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 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 "SkDebugCanvas.h"
9 #include "SkDevice.h"
10 #include "SkForceLinking.h"
11 #include "SkGraphics.h"
12 #include "SkImageDecoder.h"
13 #include "SkImageEncoder.h"
14 #include "SkOSFile.h"
15 #include "SkPicture.h"
16 #include "SkPicturePlayback.h"
17 #include "SkPictureRecord.h"
18 #include "SkPictureRecorder.h"
19 #include "SkStream.h"
20 #include "picture_utils.h"
21 
22 __SK_FORCE_IMAGE_DECODER_LINKING;
23 
usage()24 static void usage() {
25     SkDebugf("Usage: filter -i inFile [-o outFile] [--input-dir path] [--output-dir path]\n");
26     SkDebugf("                        [-h|--help]\n\n");
27     SkDebugf("    -i inFile  : file to filter.\n");
28     SkDebugf("    -o outFile : result of filtering.\n");
29     SkDebugf("    --input-dir : process all files in dir with .skp extension.\n");
30     SkDebugf("    --output-dir : results of filtering the input dir.\n");
31     SkDebugf("    -h|--help  : Show this help message.\n");
32 }
33 
34 // Is the supplied paint simply a color?
is_simple(const SkPaint & p)35 static bool is_simple(const SkPaint& p) {
36     return NULL == p.getPathEffect() &&
37            NULL == p.getShader() &&
38            NULL == p.getXfermode() &&
39            NULL == p.getMaskFilter() &&
40            NULL == p.getColorFilter() &&
41            NULL == p.getRasterizer() &&
42            NULL == p.getLooper() &&
43            NULL == p.getImageFilter();
44 }
45 
46 
47 // Check for:
48 //    SAVE_LAYER
49 //        DRAW_BITMAP_RECT_TO_RECT
50 //    RESTORE
51 // where the saveLayer's color can be moved into the drawBitmapRect
check_0(SkDebugCanvas * canvas,int curCommand)52 static bool check_0(SkDebugCanvas* canvas, int curCommand) {
53     if (SAVE_LAYER != canvas->getDrawCommandAt(curCommand)->getType() ||
54         canvas->getSize() <= curCommand+2 ||
55         DRAW_BITMAP_RECT_TO_RECT != canvas->getDrawCommandAt(curCommand+1)->getType() ||
56         RESTORE != canvas->getDrawCommandAt(curCommand+2)->getType()) {
57         return false;
58     }
59 
60     SkSaveLayerCommand* saveLayer =
61         (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand);
62     SkDrawBitmapRectCommand* dbmr =
63         (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+1);
64 
65     const SkPaint* saveLayerPaint = saveLayer->paint();
66     SkPaint* dbmrPaint = dbmr->paint();
67 
68     // For this optimization we only fold the saveLayer and drawBitmapRect
69     // together if the saveLayer's draw is simple (i.e., no fancy effects)
70     // and the only difference in the colors is their alpha value
71     SkColor layerColor = saveLayerPaint->getColor() | 0xFF000000; // force opaque
72     SkColor dbmrColor = dbmrPaint->getColor() | 0xFF000000;       // force opaque
73 
74     // If either operation lacks a paint then the collapse is trivial
75     return NULL == saveLayerPaint ||
76            NULL == dbmrPaint ||
77            (is_simple(*saveLayerPaint) && dbmrColor == layerColor);
78 }
79 
80 // Fold the saveLayer's alpha into the drawBitmapRect and remove the saveLayer
81 // and restore
apply_0(SkDebugCanvas * canvas,int curCommand)82 static void apply_0(SkDebugCanvas* canvas, int curCommand) {
83     SkSaveLayerCommand* saveLayer =
84         (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand);
85     const SkPaint* saveLayerPaint = saveLayer->paint();
86 
87     // if (NULL == saveLayerPaint) the dbmr's paint doesn't need to be changed
88     if (NULL != saveLayerPaint) {
89         SkDrawBitmapRectCommand* dbmr =
90             (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+1);
91         SkPaint* dbmrPaint = dbmr->paint();
92 
93         if (NULL == dbmrPaint) {
94             // if the DBMR doesn't have a paint just use the saveLayer's
95             dbmr->setPaint(*saveLayerPaint);
96         } else if (NULL != saveLayerPaint) {
97             // Both paints are present so their alphas need to be combined
98             SkColor color = saveLayerPaint->getColor();
99             int a0 = SkColorGetA(color);
100 
101             color = dbmrPaint->getColor();
102             int a1 = SkColorGetA(color);
103 
104             int newA = SkMulDiv255Round(a0, a1);
105             SkASSERT(newA <= 0xFF);
106 
107             SkColor newColor = SkColorSetA(color, newA);
108             dbmrPaint->setColor(newColor);
109         }
110     }
111 
112     canvas->deleteDrawCommandAt(curCommand+2);  // restore
113     canvas->deleteDrawCommandAt(curCommand);    // saveLayer
114 }
115 
116 // Check for:
117 //    SAVE_LAYER
118 //        SAVE
119 //            CLIP_RECT
120 //            DRAW_BITMAP_RECT_TO_RECT
121 //        RESTORE
122 //    RESTORE
123 // where the saveLayer's color can be moved into the drawBitmapRect
check_1(SkDebugCanvas * canvas,int curCommand)124 static bool check_1(SkDebugCanvas* canvas, int curCommand) {
125     if (SAVE_LAYER != canvas->getDrawCommandAt(curCommand)->getType() ||
126         canvas->getSize() <= curCommand+5 ||
127         SAVE != canvas->getDrawCommandAt(curCommand+1)->getType() ||
128         CLIP_RECT != canvas->getDrawCommandAt(curCommand+2)->getType() ||
129         DRAW_BITMAP_RECT_TO_RECT != canvas->getDrawCommandAt(curCommand+3)->getType() ||
130         RESTORE != canvas->getDrawCommandAt(curCommand+4)->getType() ||
131         RESTORE != canvas->getDrawCommandAt(curCommand+5)->getType()) {
132         return false;
133     }
134 
135     SkSaveLayerCommand* saveLayer =
136         (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand);
137     SkDrawBitmapRectCommand* dbmr =
138         (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+3);
139 
140     const SkPaint* saveLayerPaint = saveLayer->paint();
141     SkPaint* dbmrPaint = dbmr->paint();
142 
143     // For this optimization we only fold the saveLayer and drawBitmapRect
144     // together if the saveLayer's draw is simple (i.e., no fancy effects) and
145     // and the only difference in the colors is that the saveLayer's can have
146     // an alpha while the drawBitmapRect's is opaque.
147     // TODO: it should be possible to fold them together even if they both
148     // have different non-255 alphas but this is low priority since we have
149     // never seen that case
150     // If either operation lacks a paint then the collapse is trivial
151     SkColor layerColor = saveLayerPaint->getColor() | 0xFF000000; // force opaque
152 
153     return NULL == saveLayerPaint ||
154            NULL == dbmrPaint ||
155            (is_simple(*saveLayerPaint) && dbmrPaint->getColor() == layerColor);
156 }
157 
158 // Fold the saveLayer's alpha into the drawBitmapRect and remove the saveLayer
159 // and restore
apply_1(SkDebugCanvas * canvas,int curCommand)160 static void apply_1(SkDebugCanvas* canvas, int curCommand) {
161     SkSaveLayerCommand* saveLayer =
162         (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand);
163     const SkPaint* saveLayerPaint = saveLayer->paint();
164 
165     // if (NULL == saveLayerPaint) the dbmr's paint doesn't need to be changed
166     if (NULL != saveLayerPaint) {
167         SkDrawBitmapRectCommand* dbmr =
168             (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+3);
169         SkPaint* dbmrPaint = dbmr->paint();
170 
171         if (NULL == dbmrPaint) {
172             dbmr->setPaint(*saveLayerPaint);
173         } else {
174             SkColor newColor = SkColorSetA(dbmrPaint->getColor(),
175                                            SkColorGetA(saveLayerPaint->getColor()));
176             dbmrPaint->setColor(newColor);
177         }
178     }
179 
180     canvas->deleteDrawCommandAt(curCommand+5);    // restore
181     canvas->deleteDrawCommandAt(curCommand);      // saveLayer
182 }
183 
184 // Check for:
185 //    SAVE
186 //        CLIP_RECT
187 //        DRAW_RECT
188 //    RESTORE
189 // where the rect is entirely within the clip and the clip is an intersect
check_2(SkDebugCanvas * canvas,int curCommand)190 static bool check_2(SkDebugCanvas* canvas, int curCommand) {
191     if (SAVE != canvas->getDrawCommandAt(curCommand)->getType() ||
192         canvas->getSize() <= curCommand+4 ||
193         CLIP_RECT != canvas->getDrawCommandAt(curCommand+1)->getType() ||
194         DRAW_RECT != canvas->getDrawCommandAt(curCommand+2)->getType() ||
195         RESTORE != canvas->getDrawCommandAt(curCommand+3)->getType()) {
196         return false;
197     }
198 
199     SkClipRectCommand* cr =
200         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1);
201     SkDrawRectCommand* dr =
202         (SkDrawRectCommand*) canvas->getDrawCommandAt(curCommand+2);
203 
204     if (SkRegion::kIntersect_Op != cr->op()) {
205         return false;
206     }
207 
208     return cr->rect().contains(dr->rect());
209 }
210 
211 // Remove everything but the drawRect
apply_2(SkDebugCanvas * canvas,int curCommand)212 static void apply_2(SkDebugCanvas* canvas, int curCommand) {
213     canvas->deleteDrawCommandAt(curCommand+3);   // restore
214     // drawRect
215     canvas->deleteDrawCommandAt(curCommand+1);   // clipRect
216     canvas->deleteDrawCommandAt(curCommand);     // save
217 }
218 
219 // Check for:
220 //    SAVE
221 //        CLIP_RRECT
222 //        DRAW_RECT
223 //    RESTORE
224 // where the rect entirely encloses the clip
check_3(SkDebugCanvas * canvas,int curCommand)225 static bool check_3(SkDebugCanvas* canvas, int curCommand) {
226     if (SAVE != canvas->getDrawCommandAt(curCommand)->getType() ||
227         canvas->getSize() <= curCommand+4 ||
228         CLIP_RRECT != canvas->getDrawCommandAt(curCommand+1)->getType() ||
229         DRAW_RECT != canvas->getDrawCommandAt(curCommand+2)->getType() ||
230         RESTORE != canvas->getDrawCommandAt(curCommand+3)->getType()) {
231         return false;
232     }
233 
234     SkClipRRectCommand* crr =
235         (SkClipRRectCommand*) canvas->getDrawCommandAt(curCommand+1);
236     SkDrawRectCommand* dr  =
237         (SkDrawRectCommand*) canvas->getDrawCommandAt(curCommand+2);
238 
239     if (SkRegion::kIntersect_Op != crr->op()) {
240         return false;
241     }
242 
243     return dr->rect().contains(crr->rrect().rect());
244 }
245 
246 // Replace everything with a drawRRect with the paint from the drawRect
247 // and the AA settings from the clipRRect
apply_3(SkDebugCanvas * canvas,int curCommand)248 static void apply_3(SkDebugCanvas* canvas, int curCommand) {
249 
250     canvas->deleteDrawCommandAt(curCommand+3);    // restore
251 
252     SkClipRRectCommand* crr =
253         (SkClipRRectCommand*) canvas->getDrawCommandAt(curCommand+1);
254     SkDrawRectCommand* dr  =
255         (SkDrawRectCommand*) canvas->getDrawCommandAt(curCommand+2);
256 
257     // TODO: could skip paint re-creation if the AA settings already match
258     SkPaint newPaint = dr->paint();
259     newPaint.setAntiAlias(crr->doAA());
260     SkDrawRRectCommand* drr = new SkDrawRRectCommand(crr->rrect(), newPaint);
261     canvas->setDrawCommandAt(curCommand+2, drr);
262 
263     canvas->deleteDrawCommandAt(curCommand+1);    // clipRRect
264     canvas->deleteDrawCommandAt(curCommand);      // save
265 }
266 
267 // Check for:
268 //    SAVE
269 //        CLIP_RECT
270 //        DRAW_BITMAP_RECT_TO_RECT
271 //    RESTORE
272 // where the rect and drawBitmapRect dst exactly match
check_4(SkDebugCanvas * canvas,int curCommand)273 static bool check_4(SkDebugCanvas* canvas, int curCommand) {
274     if (SAVE != canvas->getDrawCommandAt(curCommand)->getType() ||
275         canvas->getSize() <= curCommand+4 ||
276         CLIP_RECT != canvas->getDrawCommandAt(curCommand+1)->getType() ||
277         DRAW_BITMAP_RECT_TO_RECT != canvas->getDrawCommandAt(curCommand+2)->getType() ||
278         RESTORE != canvas->getDrawCommandAt(curCommand+3)->getType()) {
279         return false;
280     }
281 
282     SkClipRectCommand* cr =
283         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1);
284     SkDrawBitmapRectCommand* dbmr  =
285         (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+2);
286 
287     if (SkRegion::kIntersect_Op != cr->op()) {
288         return false;
289     }
290 
291     return dbmr->dstRect() == cr->rect();
292 }
293 
294 // Remove everything but the drawBitmapRect
apply_4(SkDebugCanvas * canvas,int curCommand)295 static void apply_4(SkDebugCanvas* canvas, int curCommand) {
296     canvas->deleteDrawCommandAt(curCommand+3);    // restore
297     // drawBitmapRectToRect
298     canvas->deleteDrawCommandAt(curCommand+1);    // clipRect
299     canvas->deleteDrawCommandAt(curCommand);      // save
300 }
301 
302 // Check for:
303 //    TRANSLATE
304 // where the translate is zero
check_5(SkDebugCanvas * canvas,int curCommand)305 static bool check_5(SkDebugCanvas* canvas, int curCommand) {
306     if (TRANSLATE != canvas->getDrawCommandAt(curCommand)->getType()) {
307         return false;
308     }
309 
310     SkTranslateCommand* t =
311         (SkTranslateCommand*) canvas->getDrawCommandAt(curCommand);
312 
313     return 0 == t->x() && 0 == t->y();
314 }
315 
316 // Just remove the translate
apply_5(SkDebugCanvas * canvas,int curCommand)317 static void apply_5(SkDebugCanvas* canvas, int curCommand) {
318     canvas->deleteDrawCommandAt(curCommand);    // translate
319 }
320 
321 // Check for:
322 //    SCALE
323 // where the scale is 1,1
check_6(SkDebugCanvas * canvas,int curCommand)324 static bool check_6(SkDebugCanvas* canvas, int curCommand) {
325     if (SCALE != canvas->getDrawCommandAt(curCommand)->getType()) {
326         return false;
327     }
328 
329     SkScaleCommand* s = (SkScaleCommand*) canvas->getDrawCommandAt(curCommand);
330 
331     return SK_Scalar1 == s->x() && SK_Scalar1 == s->y();
332 }
333 
334 // Just remove the scale
apply_6(SkDebugCanvas * canvas,int curCommand)335 static void apply_6(SkDebugCanvas* canvas, int curCommand) {
336     canvas->deleteDrawCommandAt(curCommand);   // scale
337 }
338 
339 // Check for:
340 //  SAVE
341 //      CLIP_RECT
342 //      SAVE_LAYER
343 //          SAVE
344 //              CLIP_RECT
345 //              SAVE_LAYER
346 //                  SAVE
347 //                      CLIP_RECT
348 //                      DRAWBITMAPRECTTORECT
349 //                  RESTORE
350 //              RESTORE
351 //          RESTORE
352 //      RESTORE
353 //  RESTORE
354 // where:
355 //      all the clipRect's are BW, nested, intersections
356 //      the drawBitmapRectToRect is a 1-1 copy from src to dest
357 //      the last (smallest) clip rect is a subset of the drawBitmapRectToRect's dest rect
358 //      all the saveLayer's paints can be rolled into the drawBitmapRectToRect's paint
359 // This pattern is used by Google spreadsheet when drawing the toolbar buttons
check_7(SkDebugCanvas * canvas,int curCommand)360 static bool check_7(SkDebugCanvas* canvas, int curCommand) {
361     if (SAVE != canvas->getDrawCommandAt(curCommand)->getType() ||
362         canvas->getSize() <= curCommand+13 ||
363         CLIP_RECT != canvas->getDrawCommandAt(curCommand+1)->getType() ||
364         SAVE_LAYER != canvas->getDrawCommandAt(curCommand+2)->getType() ||
365         SAVE != canvas->getDrawCommandAt(curCommand+3)->getType() ||
366         CLIP_RECT != canvas->getDrawCommandAt(curCommand+4)->getType() ||
367         SAVE_LAYER != canvas->getDrawCommandAt(curCommand+5)->getType() ||
368         SAVE != canvas->getDrawCommandAt(curCommand+6)->getType() ||
369         CLIP_RECT != canvas->getDrawCommandAt(curCommand+7)->getType() ||
370         DRAW_BITMAP_RECT_TO_RECT != canvas->getDrawCommandAt(curCommand+8)->getType() ||
371         RESTORE != canvas->getDrawCommandAt(curCommand+9)->getType() ||
372         RESTORE != canvas->getDrawCommandAt(curCommand+10)->getType() ||
373         RESTORE != canvas->getDrawCommandAt(curCommand+11)->getType() ||
374         RESTORE != canvas->getDrawCommandAt(curCommand+12)->getType() ||
375         RESTORE != canvas->getDrawCommandAt(curCommand+13)->getType()) {
376         return false;
377     }
378 
379     SkClipRectCommand* clip0 =
380         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1);
381     SkSaveLayerCommand* saveLayer0 =
382         (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand+2);
383     SkClipRectCommand* clip1 =
384         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+4);
385     SkSaveLayerCommand* saveLayer1 =
386         (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand+5);
387     SkClipRectCommand* clip2 =
388         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+7);
389     SkDrawBitmapRectCommand* dbmr =
390         (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+8);
391 
392     if (clip0->doAA() || clip1->doAA() || clip2->doAA()) {
393         return false;
394     }
395 
396     if (SkRegion::kIntersect_Op != clip0->op() ||
397         SkRegion::kIntersect_Op != clip1->op() ||
398         SkRegion::kIntersect_Op != clip2->op()) {
399         return false;
400     }
401 
402     if (!clip0->rect().contains(clip1->rect()) ||
403         !clip1->rect().contains(clip2->rect())) {
404         return false;
405     }
406 
407     // The src->dest mapping needs to be 1-to-1
408     if (NULL == dbmr->srcRect()) {
409         if (dbmr->bitmap().width() != dbmr->dstRect().width() ||
410             dbmr->bitmap().height() != dbmr->dstRect().height()) {
411             return false;
412         }
413     } else {
414         if (dbmr->srcRect()->width() != dbmr->dstRect().width() ||
415             dbmr->srcRect()->height() != dbmr->dstRect().height()) {
416             return false;
417         }
418     }
419 
420     if (!dbmr->dstRect().contains(clip2->rect())) {
421         return false;
422     }
423 
424     const SkPaint* saveLayerPaint0 = saveLayer0->paint();
425     const SkPaint* saveLayerPaint1 = saveLayer1->paint();
426 
427     if ((NULL != saveLayerPaint0 && !is_simple(*saveLayerPaint0)) ||
428         (NULL != saveLayerPaint1 && !is_simple(*saveLayerPaint1))) {
429         return false;
430     }
431 
432     SkPaint* dbmrPaint = dbmr->paint();
433 
434     if (NULL == dbmrPaint) {
435         return true;
436     }
437 
438     if (NULL != saveLayerPaint0) {
439         SkColor layerColor0 = saveLayerPaint0->getColor() | 0xFF000000; // force opaque
440         if (dbmrPaint->getColor() != layerColor0) {
441             return false;
442         }
443     }
444 
445     if (NULL != saveLayerPaint1) {
446         SkColor layerColor1 = saveLayerPaint1->getColor() | 0xFF000000; // force opaque
447         if (dbmrPaint->getColor() != layerColor1) {
448             return false;
449         }
450     }
451 
452     return true;
453 }
454 
455 // Reduce to a single drawBitmapRectToRect call by folding the clipRect's into
456 // the src and dst Rects and the saveLayer paints into the drawBitmapRectToRect's
457 // paint.
apply_7(SkDebugCanvas * canvas,int curCommand)458 static void apply_7(SkDebugCanvas* canvas, int curCommand) {
459     SkSaveLayerCommand* saveLayer0 =
460         (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand+2);
461     SkSaveLayerCommand* saveLayer1 =
462         (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand+5);
463     SkClipRectCommand* clip2 =
464         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+7);
465     SkDrawBitmapRectCommand* dbmr =
466         (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+8);
467 
468     SkScalar newSrcLeft = dbmr->srcRect()->fLeft + clip2->rect().fLeft - dbmr->dstRect().fLeft;
469     SkScalar newSrcTop = dbmr->srcRect()->fTop + clip2->rect().fTop - dbmr->dstRect().fTop;
470 
471     SkRect newSrc = SkRect::MakeXYWH(newSrcLeft, newSrcTop,
472                                      clip2->rect().width(), clip2->rect().height());
473 
474     dbmr->setSrcRect(newSrc);
475     dbmr->setDstRect(clip2->rect());
476 
477     SkColor color = 0xFF000000;
478     int a0, a1;
479 
480     const SkPaint* saveLayerPaint0 = saveLayer0->paint();
481     if (NULL != saveLayerPaint0) {
482         color = saveLayerPaint0->getColor();
483         a0 = SkColorGetA(color);
484     } else {
485         a0 = 0xFF;
486     }
487 
488     const SkPaint* saveLayerPaint1 = saveLayer1->paint();
489     if (NULL != saveLayerPaint1) {
490         color = saveLayerPaint1->getColor();
491         a1 = SkColorGetA(color);
492     } else {
493         a1 = 0xFF;
494     }
495 
496     int newA = SkMulDiv255Round(a0, a1);
497     SkASSERT(newA <= 0xFF);
498 
499     SkPaint* dbmrPaint = dbmr->paint();
500 
501     if (NULL != dbmrPaint) {
502         SkColor newColor = SkColorSetA(dbmrPaint->getColor(), newA);
503         dbmrPaint->setColor(newColor);
504     } else {
505         SkColor newColor = SkColorSetA(color, newA);
506 
507         SkPaint newPaint;
508         newPaint.setColor(newColor);
509         dbmr->setPaint(newPaint);
510     }
511 
512     // remove everything except the drawbitmaprect
513     canvas->deleteDrawCommandAt(curCommand+13);   // restore
514     canvas->deleteDrawCommandAt(curCommand+12);   // restore
515     canvas->deleteDrawCommandAt(curCommand+11);   // restore
516     canvas->deleteDrawCommandAt(curCommand+10);   // restore
517     canvas->deleteDrawCommandAt(curCommand+9);    // restore
518     canvas->deleteDrawCommandAt(curCommand+7);    // clipRect
519     canvas->deleteDrawCommandAt(curCommand+6);    // save
520     canvas->deleteDrawCommandAt(curCommand+5);    // saveLayer
521     canvas->deleteDrawCommandAt(curCommand+4);    // clipRect
522     canvas->deleteDrawCommandAt(curCommand+3);    // save
523     canvas->deleteDrawCommandAt(curCommand+2);    // saveLayer
524     canvas->deleteDrawCommandAt(curCommand+1);    // clipRect
525     canvas->deleteDrawCommandAt(curCommand);      // save
526 }
527 
528 // Check for:
529 //    SAVE
530 //       CLIP_RECT
531 //       DRAWBITMAPRECTTORECT
532 //    RESTORE
533 // where:
534 //      the drawBitmapRectToRect is a 1-1 copy from src to dest
535 //      the clip rect is BW and a subset of the drawBitmapRectToRect's dest rect
check_8(SkDebugCanvas * canvas,int curCommand)536 static bool check_8(SkDebugCanvas* canvas, int curCommand) {
537     if (SAVE != canvas->getDrawCommandAt(curCommand)->getType() ||
538         canvas->getSize() <= curCommand+4 ||
539         CLIP_RECT != canvas->getDrawCommandAt(curCommand+1)->getType() ||
540         DRAW_BITMAP_RECT_TO_RECT != canvas->getDrawCommandAt(curCommand+2)->getType() ||
541         RESTORE != canvas->getDrawCommandAt(curCommand+3)->getType()) {
542         return false;
543     }
544 
545     SkClipRectCommand* clip =
546         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1);
547     SkDrawBitmapRectCommand* dbmr =
548         (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+2);
549 
550     if (clip->doAA() || SkRegion::kIntersect_Op != clip->op()) {
551         return false;
552     }
553 
554     // The src->dest mapping needs to be 1-to-1
555     if (NULL == dbmr->srcRect()) {
556         if (dbmr->bitmap().width() != dbmr->dstRect().width() ||
557             dbmr->bitmap().height() != dbmr->dstRect().height()) {
558             return false;
559         }
560     } else {
561         if (dbmr->srcRect()->width() != dbmr->dstRect().width() ||
562             dbmr->srcRect()->height() != dbmr->dstRect().height()) {
563             return false;
564         }
565     }
566 
567     if (!dbmr->dstRect().contains(clip->rect())) {
568         return false;
569     }
570 
571     return true;
572 }
573 
574 // Fold the clipRect into the drawBitmapRectToRect's src and dest rects
apply_8(SkDebugCanvas * canvas,int curCommand)575 static void apply_8(SkDebugCanvas* canvas, int curCommand) {
576     SkClipRectCommand* clip =
577         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1);
578     SkDrawBitmapRectCommand* dbmr =
579         (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+2);
580 
581     SkScalar newSrcLeft, newSrcTop;
582 
583     if (NULL != dbmr->srcRect()) {
584         newSrcLeft = dbmr->srcRect()->fLeft + clip->rect().fLeft - dbmr->dstRect().fLeft;
585         newSrcTop  = dbmr->srcRect()->fTop + clip->rect().fTop - dbmr->dstRect().fTop;
586     } else {
587         newSrcLeft = clip->rect().fLeft - dbmr->dstRect().fLeft;
588         newSrcTop  = clip->rect().fTop - dbmr->dstRect().fTop;
589     }
590 
591     SkRect newSrc = SkRect::MakeXYWH(newSrcLeft, newSrcTop,
592                                      clip->rect().width(), clip->rect().height());
593 
594     dbmr->setSrcRect(newSrc);
595     dbmr->setDstRect(clip->rect());
596 
597     // remove everything except the drawbitmaprect
598     canvas->deleteDrawCommandAt(curCommand+3);
599     canvas->deleteDrawCommandAt(curCommand+1);
600     canvas->deleteDrawCommandAt(curCommand);
601 }
602 
603 // Check for:
604 //  SAVE
605 //    CLIP_RECT
606 //    DRAWBITMAPRECTTORECT
607 //  RESTORE
608 // where:
609 //      clipRect is BW and encloses the DBMR2R's dest rect
check_9(SkDebugCanvas * canvas,int curCommand)610 static bool check_9(SkDebugCanvas* canvas, int curCommand) {
611     if (SAVE != canvas->getDrawCommandAt(curCommand)->getType() ||
612         canvas->getSize() <= curCommand+4 ||
613         CLIP_RECT != canvas->getDrawCommandAt(curCommand+1)->getType() ||
614         DRAW_BITMAP_RECT_TO_RECT != canvas->getDrawCommandAt(curCommand+2)->getType() ||
615         RESTORE != canvas->getDrawCommandAt(curCommand+3)->getType()) {
616         return false;
617     }
618 
619     SkClipRectCommand* clip =
620         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1);
621     SkDrawBitmapRectCommand* dbmr =
622         (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+2);
623 
624     if (clip->doAA() || SkRegion::kIntersect_Op != clip->op()) {
625         return false;
626     }
627 
628     if (!clip->rect().contains(dbmr->dstRect())) {
629         return false;
630     }
631 
632     return true;
633 }
634 
635 // remove everything except the drawbitmaprect
apply_9(SkDebugCanvas * canvas,int curCommand)636 static void apply_9(SkDebugCanvas* canvas, int curCommand) {
637     canvas->deleteDrawCommandAt(curCommand+3);   // restore
638     // drawBitmapRectToRect
639     canvas->deleteDrawCommandAt(curCommand+1);   // clipRect
640     canvas->deleteDrawCommandAt(curCommand);     // save
641 }
642 
643 typedef bool (*PFCheck)(SkDebugCanvas* canvas, int curCommand);
644 typedef void (*PFApply)(SkDebugCanvas* canvas, int curCommand);
645 
646 struct OptTableEntry {
647     PFCheck fCheck;
648     PFApply fApply;
649     int fNumTimesApplied;
650 } gOptTable[] = {
651     { check_0, apply_0, 0 },
652     { check_1, apply_1, 0 },
653     { check_2, apply_2, 0 },
654     { check_3, apply_3, 0 },
655     { check_4, apply_4, 0 },
656     { check_5, apply_5, 0 },
657     { check_6, apply_6, 0 },
658     { check_7, apply_7, 0 },
659     { check_8, apply_8, 0 },
660     { check_9, apply_9, 0 },
661 };
662 
663 
filter_picture(const SkString & inFile,const SkString & outFile)664 static int filter_picture(const SkString& inFile, const SkString& outFile) {
665     SkAutoTDelete<SkPicture> inPicture;
666 
667     SkFILEStream inStream(inFile.c_str());
668     if (inStream.isValid()) {
669         inPicture.reset(SkPicture::CreateFromStream(&inStream));
670     }
671 
672     if (NULL == inPicture.get()) {
673         SkDebugf("Could not read file %s\n", inFile.c_str());
674         return -1;
675     }
676 
677     int localCount[SK_ARRAY_COUNT(gOptTable)];
678 
679     memset(localCount, 0, sizeof(localCount));
680 
681     SkDebugCanvas debugCanvas(inPicture->width(), inPicture->height());
682     debugCanvas.setBounds(inPicture->width(), inPicture->height());
683     inPicture->draw(&debugCanvas);
684 
685     // delete the initial save and restore since replaying the commands will
686     // re-add them
687     if (debugCanvas.getSize() > 1) {
688         debugCanvas.deleteDrawCommandAt(0);
689         debugCanvas.deleteDrawCommandAt(debugCanvas.getSize()-1);
690     }
691 
692     bool changed = true;
693     int numBefore = debugCanvas.getSize();
694 
695     while (changed) {
696         changed = false;
697         for (int i = 0; i < debugCanvas.getSize(); ++i) {
698             for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) {
699                 if ((*gOptTable[opt].fCheck)(&debugCanvas, i)) {
700                     (*gOptTable[opt].fApply)(&debugCanvas, i);
701 
702                     ++gOptTable[opt].fNumTimesApplied;
703                     ++localCount[opt];
704 
705                     if (debugCanvas.getSize() == i) {
706                         // the optimization removed all the remaining operations
707                         break;
708                     }
709 
710                     opt = 0;          // try all the opts all over again
711                     changed = true;
712                 }
713             }
714         }
715     }
716 
717     int numAfter = debugCanvas.getSize();
718 
719     if (!outFile.isEmpty()) {
720         SkPictureRecorder recorder;
721         SkCanvas* canvas = recorder.beginRecording(inPicture->width(), inPicture->height(), NULL, 0);
722         debugCanvas.draw(canvas);
723         SkAutoTUnref<SkPicture> outPicture(recorder.endRecording());
724 
725         SkFILEWStream outStream(outFile.c_str());
726 
727         outPicture->serialize(&outStream);
728     }
729 
730     bool someOptFired = false;
731     for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) {
732         if (0 != localCount[opt]) {
733             SkDebugf("%d: %d ", opt, localCount[opt]);
734             someOptFired = true;
735         }
736     }
737 
738     if (!someOptFired) {
739         SkDebugf("No opts fired\n");
740     } else {
741         SkDebugf("\t before: %d after: %d delta: %d\n",
742                  numBefore, numAfter, numBefore-numAfter);
743     }
744 
745     return 0;
746 }
747 
748 // This function is not marked as 'static' so it can be referenced externally
749 // in the iOS build.
750 int tool_main(int argc, char** argv); // suppress a warning on mac
751 
tool_main(int argc,char ** argv)752 int tool_main(int argc, char** argv) {
753 #if SK_ENABLE_INST_COUNT
754     gPrintInstCount = true;
755 #endif
756 
757     SkGraphics::Init();
758 
759     if (argc < 3) {
760         usage();
761         return -1;
762     }
763 
764     SkString inFile, outFile, inDir, outDir;
765 
766     char* const* stop = argv + argc;
767     for (++argv; argv < stop; ++argv) {
768         if (strcmp(*argv, "-i") == 0) {
769             argv++;
770             if (argv < stop && **argv) {
771                 inFile.set(*argv);
772             } else {
773                 SkDebugf("missing arg for -i\n");
774                 usage();
775                 return -1;
776             }
777         } else if (strcmp(*argv, "--input-dir") == 0) {
778             argv++;
779             if (argv < stop && **argv) {
780                 inDir.set(*argv);
781             } else {
782                 SkDebugf("missing arg for --input-dir\n");
783                 usage();
784                 return -1;
785             }
786         } else if (strcmp(*argv, "--output-dir") == 0) {
787             argv++;
788             if (argv < stop && **argv) {
789                 outDir.set(*argv);
790             } else {
791                 SkDebugf("missing arg for --output-dir\n");
792                 usage();
793                 return -1;
794             }
795         } else if (strcmp(*argv, "-o") == 0) {
796             argv++;
797             if (argv < stop && **argv) {
798                 outFile.set(*argv);
799             } else {
800                 SkDebugf("missing arg for -o\n");
801                 usage();
802                 return -1;
803             }
804         } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) {
805             usage();
806             return 0;
807         } else {
808             SkDebugf("unknown arg %s\n", *argv);
809             usage();
810             return -1;
811         }
812     }
813 
814     SkOSFile::Iter iter(inDir.c_str(), "skp");
815 
816     SkString inputFilename, outputFilename;
817     if (iter.next(&inputFilename)) {
818 
819         do {
820             inFile = SkOSPath::SkPathJoin(inDir.c_str(), inputFilename.c_str());
821             if (!outDir.isEmpty()) {
822                 outFile = SkOSPath::SkPathJoin(outDir.c_str(), inputFilename.c_str());
823             }
824             SkDebugf("Executing %s\n", inputFilename.c_str());
825             filter_picture(inFile, outFile);
826         } while(iter.next(&inputFilename));
827 
828     } else if (!inFile.isEmpty()) {
829         filter_picture(inFile, outFile);
830     } else {
831         usage();
832         return -1;
833     }
834 
835     for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) {
836         SkDebugf("opt %d: %d\n", opt, gOptTable[opt].fNumTimesApplied);
837     }
838 
839     SkGraphics::Term();
840     return 0;
841 }
842 
843 #if !defined SK_BUILD_FOR_IOS
main(int argc,char * const argv[])844 int main(int argc, char * const argv[]) {
845     return tool_main(argc, (char**) argv);
846 }
847 #endif
848