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