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