Home | History | Annotate | Download | only in tools
      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 
     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?
     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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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.
    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
    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
    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
    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
    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 
    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 
    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
    844 int main(int argc, char * const argv[]) {
    845     return tool_main(argc, (char**) argv);
    846 }
    847 #endif
    848