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 (SkDrawCommand::kSaveLayer_OpType != canvas->getDrawCommandAt(curCommand)->getType() ||
     53         canvas->getSize() <= curCommand+2 ||
     54         SkDrawCommand::kDrawBitmapRect_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() ||
     55         SkDrawCommand::kRestore_OpType != 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 (SkDrawCommand::kSaveLayer_OpType != canvas->getDrawCommandAt(curCommand)->getType() ||
    125         canvas->getSize() <= curCommand+5 ||
    126         SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() ||
    127         SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+2)->getType() ||
    128         SkDrawCommand::kDrawBitmapRect_OpType != canvas->getDrawCommandAt(curCommand+3)->getType() ||
    129         SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+4)->getType() ||
    130         SkDrawCommand::kRestore_OpType != 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 (SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand)->getType() ||
    191         canvas->getSize() <= curCommand+4 ||
    192         SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() ||
    193         SkDrawCommand::kDrawRect_OpType != canvas->getDrawCommandAt(curCommand+2)->getType() ||
    194         SkDrawCommand::kRestore_OpType != 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 (SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand)->getType() ||
    226         canvas->getSize() <= curCommand+4 ||
    227         SkDrawCommand::kClipRRect_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() ||
    228         SkDrawCommand::kDrawRect_OpType != canvas->getDrawCommandAt(curCommand+2)->getType() ||
    229         SkDrawCommand::kRestore_OpType != 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 (SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand)->getType() ||
    274         canvas->getSize() <= curCommand+4 ||
    275         SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() ||
    276         SkDrawCommand::kDrawBitmapRect_OpType != canvas->getDrawCommandAt(curCommand+2)->getType() ||
    277         SkDrawCommand::kRestore_OpType != 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 //  SAVE
    303 //      CLIP_RECT
    304 //      SAVE_LAYER
    305 //          SAVE
    306 //              CLIP_RECT
    307 //              SAVE_LAYER
    308 //                  SAVE
    309 //                      CLIP_RECT
    310 //                      DRAWBITMAPRECTTORECT
    311 //                  RESTORE
    312 //              RESTORE
    313 //          RESTORE
    314 //      RESTORE
    315 //  RESTORE
    316 // where:
    317 //      all the clipRect's are BW, nested, intersections
    318 //      the drawBitmapRectToRect is a 1-1 copy from src to dest
    319 //      the last (smallest) clip rect is a subset of the drawBitmapRectToRect's dest rect
    320 //      all the saveLayer's paints can be rolled into the drawBitmapRectToRect's paint
    321 // This pattern is used by Google spreadsheet when drawing the toolbar buttons
    322 static bool check_7(SkDebugCanvas* canvas, int curCommand) {
    323     if (SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand)->getType() ||
    324         canvas->getSize() <= curCommand+13 ||
    325         SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() ||
    326         SkDrawCommand::kSaveLayer_OpType != canvas->getDrawCommandAt(curCommand+2)->getType() ||
    327         SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand+3)->getType() ||
    328         SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+4)->getType() ||
    329         SkDrawCommand::kSaveLayer_OpType != canvas->getDrawCommandAt(curCommand+5)->getType() ||
    330         SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand+6)->getType() ||
    331         SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+7)->getType() ||
    332         SkDrawCommand::kDrawBitmapRect_OpType != canvas->getDrawCommandAt(curCommand+8)->getType() ||
    333         SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+9)->getType() ||
    334         SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+10)->getType() ||
    335         SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+11)->getType() ||
    336         SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+12)->getType() ||
    337         SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+13)->getType()) {
    338         return false;
    339     }
    340 
    341     SkClipRectCommand* clip0 =
    342         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1);
    343     SkSaveLayerCommand* saveLayer0 =
    344         (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand+2);
    345     SkClipRectCommand* clip1 =
    346         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+4);
    347     SkSaveLayerCommand* saveLayer1 =
    348         (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand+5);
    349     SkClipRectCommand* clip2 =
    350         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+7);
    351     SkDrawBitmapRectCommand* dbmr =
    352         (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+8);
    353 
    354     if (clip0->doAA() || clip1->doAA() || clip2->doAA()) {
    355         return false;
    356     }
    357 
    358     if (SkRegion::kIntersect_Op != clip0->op() ||
    359         SkRegion::kIntersect_Op != clip1->op() ||
    360         SkRegion::kIntersect_Op != clip2->op()) {
    361         return false;
    362     }
    363 
    364     if (!clip0->rect().contains(clip1->rect()) ||
    365         !clip1->rect().contains(clip2->rect())) {
    366         return false;
    367     }
    368 
    369     // The src->dest mapping needs to be 1-to-1
    370     if (NULL == dbmr->srcRect()) {
    371         if (dbmr->bitmap().width() != dbmr->dstRect().width() ||
    372             dbmr->bitmap().height() != dbmr->dstRect().height()) {
    373             return false;
    374         }
    375     } else {
    376         if (dbmr->srcRect()->width() != dbmr->dstRect().width() ||
    377             dbmr->srcRect()->height() != dbmr->dstRect().height()) {
    378             return false;
    379         }
    380     }
    381 
    382     if (!dbmr->dstRect().contains(clip2->rect())) {
    383         return false;
    384     }
    385 
    386     const SkPaint* saveLayerPaint0 = saveLayer0->paint();
    387     const SkPaint* saveLayerPaint1 = saveLayer1->paint();
    388 
    389     if ((saveLayerPaint0 && !is_simple(*saveLayerPaint0)) ||
    390         (saveLayerPaint1 && !is_simple(*saveLayerPaint1))) {
    391         return false;
    392     }
    393 
    394     SkPaint* dbmrPaint = dbmr->paint();
    395 
    396     if (NULL == dbmrPaint) {
    397         return true;
    398     }
    399 
    400     if (saveLayerPaint0) {
    401         SkColor layerColor0 = saveLayerPaint0->getColor() | 0xFF000000; // force opaque
    402         if (dbmrPaint->getColor() != layerColor0) {
    403             return false;
    404         }
    405     }
    406 
    407     if (saveLayerPaint1) {
    408         SkColor layerColor1 = saveLayerPaint1->getColor() | 0xFF000000; // force opaque
    409         if (dbmrPaint->getColor() != layerColor1) {
    410             return false;
    411         }
    412     }
    413 
    414     return true;
    415 }
    416 
    417 // Reduce to a single drawBitmapRectToRect call by folding the clipRect's into
    418 // the src and dst Rects and the saveLayer paints into the drawBitmapRectToRect's
    419 // paint.
    420 static void apply_7(SkDebugCanvas* canvas, int curCommand) {
    421     SkSaveLayerCommand* saveLayer0 =
    422         (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand+2);
    423     SkSaveLayerCommand* saveLayer1 =
    424         (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand+5);
    425     SkClipRectCommand* clip2 =
    426         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+7);
    427     SkDrawBitmapRectCommand* dbmr =
    428         (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+8);
    429 
    430     SkScalar newSrcLeft = dbmr->srcRect()->fLeft + clip2->rect().fLeft - dbmr->dstRect().fLeft;
    431     SkScalar newSrcTop = dbmr->srcRect()->fTop + clip2->rect().fTop - dbmr->dstRect().fTop;
    432 
    433     SkRect newSrc = SkRect::MakeXYWH(newSrcLeft, newSrcTop,
    434                                      clip2->rect().width(), clip2->rect().height());
    435 
    436     dbmr->setSrcRect(newSrc);
    437     dbmr->setDstRect(clip2->rect());
    438 
    439     SkColor color = 0xFF000000;
    440     int a0, a1;
    441 
    442     const SkPaint* saveLayerPaint0 = saveLayer0->paint();
    443     if (saveLayerPaint0) {
    444         color = saveLayerPaint0->getColor();
    445         a0 = SkColorGetA(color);
    446     } else {
    447         a0 = 0xFF;
    448     }
    449 
    450     const SkPaint* saveLayerPaint1 = saveLayer1->paint();
    451     if (saveLayerPaint1) {
    452         color = saveLayerPaint1->getColor();
    453         a1 = SkColorGetA(color);
    454     } else {
    455         a1 = 0xFF;
    456     }
    457 
    458     int newA = SkMulDiv255Round(a0, a1);
    459     SkASSERT(newA <= 0xFF);
    460 
    461     SkPaint* dbmrPaint = dbmr->paint();
    462 
    463     if (dbmrPaint) {
    464         SkColor newColor = SkColorSetA(dbmrPaint->getColor(), newA);
    465         dbmrPaint->setColor(newColor);
    466     } else {
    467         SkColor newColor = SkColorSetA(color, newA);
    468 
    469         SkPaint newPaint;
    470         newPaint.setColor(newColor);
    471         dbmr->setPaint(newPaint);
    472     }
    473 
    474     // remove everything except the drawbitmaprect
    475     canvas->deleteDrawCommandAt(curCommand+13);   // restore
    476     canvas->deleteDrawCommandAt(curCommand+12);   // restore
    477     canvas->deleteDrawCommandAt(curCommand+11);   // restore
    478     canvas->deleteDrawCommandAt(curCommand+10);   // restore
    479     canvas->deleteDrawCommandAt(curCommand+9);    // restore
    480     canvas->deleteDrawCommandAt(curCommand+7);    // clipRect
    481     canvas->deleteDrawCommandAt(curCommand+6);    // save
    482     canvas->deleteDrawCommandAt(curCommand+5);    // saveLayer
    483     canvas->deleteDrawCommandAt(curCommand+4);    // clipRect
    484     canvas->deleteDrawCommandAt(curCommand+3);    // save
    485     canvas->deleteDrawCommandAt(curCommand+2);    // saveLayer
    486     canvas->deleteDrawCommandAt(curCommand+1);    // clipRect
    487     canvas->deleteDrawCommandAt(curCommand);      // save
    488 }
    489 
    490 // Check for:
    491 //    SAVE
    492 //       CLIP_RECT
    493 //       DRAWBITMAPRECTTORECT
    494 //    RESTORE
    495 // where:
    496 //      the drawBitmapRectToRect is a 1-1 copy from src to dest
    497 //      the clip rect is BW and a subset of the drawBitmapRectToRect's dest rect
    498 static bool check_8(SkDebugCanvas* canvas, int curCommand) {
    499     if (SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand)->getType() ||
    500         canvas->getSize() <= curCommand+4 ||
    501         SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() ||
    502         SkDrawCommand::kDrawBitmapRect_OpType != canvas->getDrawCommandAt(curCommand+2)->getType() ||
    503         SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+3)->getType()) {
    504         return false;
    505     }
    506 
    507     SkClipRectCommand* clip =
    508         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1);
    509     SkDrawBitmapRectCommand* dbmr =
    510         (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+2);
    511 
    512     if (clip->doAA() || SkRegion::kIntersect_Op != clip->op()) {
    513         return false;
    514     }
    515 
    516     // The src->dest mapping needs to be 1-to-1
    517     if (NULL == dbmr->srcRect()) {
    518         if (dbmr->bitmap().width() != dbmr->dstRect().width() ||
    519             dbmr->bitmap().height() != dbmr->dstRect().height()) {
    520             return false;
    521         }
    522     } else {
    523         if (dbmr->srcRect()->width() != dbmr->dstRect().width() ||
    524             dbmr->srcRect()->height() != dbmr->dstRect().height()) {
    525             return false;
    526         }
    527     }
    528 
    529     if (!dbmr->dstRect().contains(clip->rect())) {
    530         return false;
    531     }
    532 
    533     return true;
    534 }
    535 
    536 // Fold the clipRect into the drawBitmapRectToRect's src and dest rects
    537 static void apply_8(SkDebugCanvas* canvas, int curCommand) {
    538     SkClipRectCommand* clip =
    539         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1);
    540     SkDrawBitmapRectCommand* dbmr =
    541         (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+2);
    542 
    543     SkScalar newSrcLeft, newSrcTop;
    544 
    545     if (dbmr->srcRect()) {
    546         newSrcLeft = dbmr->srcRect()->fLeft + clip->rect().fLeft - dbmr->dstRect().fLeft;
    547         newSrcTop  = dbmr->srcRect()->fTop + clip->rect().fTop - dbmr->dstRect().fTop;
    548     } else {
    549         newSrcLeft = clip->rect().fLeft - dbmr->dstRect().fLeft;
    550         newSrcTop  = clip->rect().fTop - dbmr->dstRect().fTop;
    551     }
    552 
    553     SkRect newSrc = SkRect::MakeXYWH(newSrcLeft, newSrcTop,
    554                                      clip->rect().width(), clip->rect().height());
    555 
    556     dbmr->setSrcRect(newSrc);
    557     dbmr->setDstRect(clip->rect());
    558 
    559     // remove everything except the drawbitmaprect
    560     canvas->deleteDrawCommandAt(curCommand+3);
    561     canvas->deleteDrawCommandAt(curCommand+1);
    562     canvas->deleteDrawCommandAt(curCommand);
    563 }
    564 
    565 // Check for:
    566 //  SAVE
    567 //    CLIP_RECT
    568 //    DRAWBITMAPRECTTORECT
    569 //  RESTORE
    570 // where:
    571 //      clipRect is BW and encloses the DBMR2R's dest rect
    572 static bool check_9(SkDebugCanvas* canvas, int curCommand) {
    573     if (SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand)->getType() ||
    574         canvas->getSize() <= curCommand+4 ||
    575         SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() ||
    576         SkDrawCommand::kDrawBitmapRect_OpType != canvas->getDrawCommandAt(curCommand+2)->getType() ||
    577         SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+3)->getType()) {
    578         return false;
    579     }
    580 
    581     SkClipRectCommand* clip =
    582         (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1);
    583     SkDrawBitmapRectCommand* dbmr =
    584         (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+2);
    585 
    586     if (clip->doAA() || SkRegion::kIntersect_Op != clip->op()) {
    587         return false;
    588     }
    589 
    590     if (!clip->rect().contains(dbmr->dstRect())) {
    591         return false;
    592     }
    593 
    594     return true;
    595 }
    596 
    597 // remove everything except the drawbitmaprect
    598 static void apply_9(SkDebugCanvas* canvas, int curCommand) {
    599     canvas->deleteDrawCommandAt(curCommand+3);   // restore
    600     // drawBitmapRectToRect
    601     canvas->deleteDrawCommandAt(curCommand+1);   // clipRect
    602     canvas->deleteDrawCommandAt(curCommand);     // save
    603 }
    604 
    605 typedef bool (*PFCheck)(SkDebugCanvas* canvas, int curCommand);
    606 typedef void (*PFApply)(SkDebugCanvas* canvas, int curCommand);
    607 
    608 struct OptTableEntry {
    609     PFCheck fCheck;
    610     PFApply fApply;
    611     int fNumTimesApplied;
    612 } gOptTable[] = {
    613     { check_0, apply_0, 0 },
    614     { check_1, apply_1, 0 },
    615     { check_2, apply_2, 0 },
    616     { check_3, apply_3, 0 },
    617     { check_4, apply_4, 0 },
    618     { check_7, apply_7, 0 },
    619     { check_8, apply_8, 0 },
    620     { check_9, apply_9, 0 },
    621 };
    622 
    623 
    624 static int filter_picture(const SkString& inFile, const SkString& outFile) {
    625     SkAutoTUnref<SkPicture> inPicture;
    626 
    627     SkFILEStream inStream(inFile.c_str());
    628     if (inStream.isValid()) {
    629         inPicture.reset(SkPicture::CreateFromStream(&inStream));
    630     }
    631 
    632     if (NULL == inPicture.get()) {
    633         SkDebugf("Could not read file %s\n", inFile.c_str());
    634         return -1;
    635     }
    636 
    637     int localCount[SK_ARRAY_COUNT(gOptTable)];
    638 
    639     memset(localCount, 0, sizeof(localCount));
    640 
    641     SkDebugCanvas debugCanvas(SkScalarCeilToInt(inPicture->cullRect().width()),
    642                               SkScalarCeilToInt(inPicture->cullRect().height()));
    643     inPicture->playback(&debugCanvas);
    644 
    645     // delete the initial save and restore since replaying the commands will
    646     // re-add them
    647     if (debugCanvas.getSize() > 1) {
    648         debugCanvas.deleteDrawCommandAt(0);
    649         debugCanvas.deleteDrawCommandAt(debugCanvas.getSize()-1);
    650     }
    651 
    652     bool changed = true;
    653     int numBefore = debugCanvas.getSize();
    654 
    655     while (changed) {
    656         changed = false;
    657         for (int i = 0; i < debugCanvas.getSize(); ++i) {
    658             for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) {
    659                 if ((*gOptTable[opt].fCheck)(&debugCanvas, i)) {
    660                     (*gOptTable[opt].fApply)(&debugCanvas, i);
    661 
    662                     ++gOptTable[opt].fNumTimesApplied;
    663                     ++localCount[opt];
    664 
    665                     if (debugCanvas.getSize() == i) {
    666                         // the optimization removed all the remaining operations
    667                         break;
    668                     }
    669 
    670                     opt = 0;          // try all the opts all over again
    671                     changed = true;
    672                 }
    673             }
    674         }
    675     }
    676 
    677     int numAfter = debugCanvas.getSize();
    678 
    679     if (!outFile.isEmpty()) {
    680         SkPictureRecorder recorder;
    681         SkCanvas* canvas = recorder.beginRecording(inPicture->cullRect().width(),
    682                                                    inPicture->cullRect().height(),
    683                                                    NULL, 0);
    684         debugCanvas.draw(canvas);
    685         SkAutoTUnref<SkPicture> outPicture(recorder.endRecording());
    686 
    687         SkFILEWStream outStream(outFile.c_str());
    688 
    689         outPicture->serialize(&outStream);
    690     }
    691 
    692     bool someOptFired = false;
    693     for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) {
    694         if (0 != localCount[opt]) {
    695             SkDebugf("%d: %d ", opt, localCount[opt]);
    696             someOptFired = true;
    697         }
    698     }
    699 
    700     if (!someOptFired) {
    701         SkDebugf("No opts fired\n");
    702     } else {
    703         SkDebugf("\t before: %d after: %d delta: %d\n",
    704                  numBefore, numAfter, numBefore-numAfter);
    705     }
    706 
    707     return 0;
    708 }
    709 
    710 // This function is not marked as 'static' so it can be referenced externally
    711 // in the iOS build.
    712 int tool_main(int argc, char** argv); // suppress a warning on mac
    713 
    714 int tool_main(int argc, char** argv) {
    715 #if SK_ENABLE_INST_COUNT
    716     gPrintInstCount = true;
    717 #endif
    718 
    719     SkGraphics::Init();
    720 
    721     if (argc < 3) {
    722         usage();
    723         return -1;
    724     }
    725 
    726     SkString inFile, outFile, inDir, outDir;
    727 
    728     char* const* stop = argv + argc;
    729     for (++argv; argv < stop; ++argv) {
    730         if (strcmp(*argv, "-i") == 0) {
    731             argv++;
    732             if (argv < stop && **argv) {
    733                 inFile.set(*argv);
    734             } else {
    735                 SkDebugf("missing arg for -i\n");
    736                 usage();
    737                 return -1;
    738             }
    739         } else if (strcmp(*argv, "--input-dir") == 0) {
    740             argv++;
    741             if (argv < stop && **argv) {
    742                 inDir.set(*argv);
    743             } else {
    744                 SkDebugf("missing arg for --input-dir\n");
    745                 usage();
    746                 return -1;
    747             }
    748         } else if (strcmp(*argv, "--output-dir") == 0) {
    749             argv++;
    750             if (argv < stop && **argv) {
    751                 outDir.set(*argv);
    752             } else {
    753                 SkDebugf("missing arg for --output-dir\n");
    754                 usage();
    755                 return -1;
    756             }
    757         } else if (strcmp(*argv, "-o") == 0) {
    758             argv++;
    759             if (argv < stop && **argv) {
    760                 outFile.set(*argv);
    761             } else {
    762                 SkDebugf("missing arg for -o\n");
    763                 usage();
    764                 return -1;
    765             }
    766         } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) {
    767             usage();
    768             return 0;
    769         } else {
    770             SkDebugf("unknown arg %s\n", *argv);
    771             usage();
    772             return -1;
    773         }
    774     }
    775 
    776     SkOSFile::Iter iter(inDir.c_str(), "skp");
    777 
    778     SkString inputFilename, outputFilename;
    779     if (iter.next(&inputFilename)) {
    780 
    781         do {
    782             inFile = SkOSPath::Join(inDir.c_str(), inputFilename.c_str());
    783             if (!outDir.isEmpty()) {
    784                 outFile = SkOSPath::Join(outDir.c_str(), inputFilename.c_str());
    785             }
    786             SkDebugf("Executing %s\n", inputFilename.c_str());
    787             filter_picture(inFile, outFile);
    788         } while(iter.next(&inputFilename));
    789 
    790     } else if (!inFile.isEmpty()) {
    791         filter_picture(inFile, outFile);
    792     } else {
    793         usage();
    794         return -1;
    795     }
    796 
    797     for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) {
    798         SkDebugf("opt %d: %d\n", opt, gOptTable[opt].fNumTimesApplied);
    799     }
    800 
    801     SkGraphics::Term();
    802     return 0;
    803 }
    804 
    805 #if !defined SK_BUILD_FOR_IOS
    806 int main(int argc, char * const argv[]) {
    807     return tool_main(argc, (char**) argv);
    808 }
    809 #endif
    810