| /* | |
| * Copyright 2012 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "SkDebugCanvas.h" | |
| #include "SkDevice.h" | |
| #include "SkForceLinking.h" | |
| #include "SkGraphics.h" | |
| #include "SkImageDecoder.h" | |
| #include "SkImageEncoder.h" | |
| #include "SkOSFile.h" | |
| #include "SkPicture.h" | |
| #include "SkPictureRecord.h" | |
| #include "SkPictureRecorder.h" | |
| #include "SkStream.h" | |
| #include "picture_utils.h" | |
| __SK_FORCE_IMAGE_DECODER_LINKING; | |
| static void usage() { | |
| SkDebugf("Usage: filter -i inFile [-o outFile] [--input-dir path] [--output-dir path]\n"); | |
| SkDebugf(" [-h|--help]\n\n"); | |
| SkDebugf(" -i inFile : file to filter.\n"); | |
| SkDebugf(" -o outFile : result of filtering.\n"); | |
| SkDebugf(" --input-dir : process all files in dir with .skp extension.\n"); | |
| SkDebugf(" --output-dir : results of filtering the input dir.\n"); | |
| SkDebugf(" -h|--help : Show this help message.\n"); | |
| } | |
| // Is the supplied paint simply a color? | |
| static bool is_simple(const SkPaint& p) { | |
| return nullptr == p.getPathEffect() && | |
| nullptr == p.getShader() && | |
| nullptr == p.getXfermode() && | |
| nullptr == p.getMaskFilter() && | |
| nullptr == p.getColorFilter() && | |
| nullptr == p.getRasterizer() && | |
| nullptr == p.getLooper() && | |
| nullptr == p.getImageFilter(); | |
| } | |
| // Check for: | |
| // SAVE_LAYER | |
| // DRAW_BITMAP_RECT_TO_RECT | |
| // RESTORE | |
| // where the saveLayer's color can be moved into the drawBitmapRect | |
| static bool check_0(SkDebugCanvas* canvas, int curCommand) { | |
| if (SkDrawCommand::kSaveLayer_OpType != canvas->getDrawCommandAt(curCommand)->getType() || | |
| canvas->getSize() <= curCommand+2 || | |
| SkDrawCommand::kDrawBitmapRect_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() || | |
| SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+2)->getType()) { | |
| return false; | |
| } | |
| SkSaveLayerCommand* saveLayer = | |
| (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand); | |
| SkDrawBitmapRectCommand* dbmr = | |
| (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+1); | |
| const SkPaint* saveLayerPaint = saveLayer->paint(); | |
| SkPaint* dbmrPaint = dbmr->paint(); | |
| // For this optimization we only fold the saveLayer and drawBitmapRect | |
| // together if the saveLayer's draw is simple (i.e., no fancy effects) | |
| // and the only difference in the colors is their alpha value | |
| SkColor layerColor = saveLayerPaint->getColor() | 0xFF000000; // force opaque | |
| SkColor dbmrColor = dbmrPaint->getColor() | 0xFF000000; // force opaque | |
| // If either operation lacks a paint then the collapse is trivial | |
| return nullptr == saveLayerPaint || | |
| nullptr == dbmrPaint || | |
| (is_simple(*saveLayerPaint) && dbmrColor == layerColor); | |
| } | |
| // Fold the saveLayer's alpha into the drawBitmapRect and remove the saveLayer | |
| // and restore | |
| static void apply_0(SkDebugCanvas* canvas, int curCommand) { | |
| SkSaveLayerCommand* saveLayer = | |
| (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand); | |
| const SkPaint* saveLayerPaint = saveLayer->paint(); | |
| // if (nullptr == saveLayerPaint) the dbmr's paint doesn't need to be changed | |
| if (saveLayerPaint) { | |
| SkDrawBitmapRectCommand* dbmr = | |
| (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+1); | |
| SkPaint* dbmrPaint = dbmr->paint(); | |
| if (nullptr == dbmrPaint) { | |
| // if the DBMR doesn't have a paint just use the saveLayer's | |
| dbmr->setPaint(*saveLayerPaint); | |
| } else if (saveLayerPaint) { | |
| // Both paints are present so their alphas need to be combined | |
| SkColor color = saveLayerPaint->getColor(); | |
| int a0 = SkColorGetA(color); | |
| color = dbmrPaint->getColor(); | |
| int a1 = SkColorGetA(color); | |
| int newA = SkMulDiv255Round(a0, a1); | |
| SkASSERT(newA <= 0xFF); | |
| SkColor newColor = SkColorSetA(color, newA); | |
| dbmrPaint->setColor(newColor); | |
| } | |
| } | |
| canvas->deleteDrawCommandAt(curCommand+2); // restore | |
| canvas->deleteDrawCommandAt(curCommand); // saveLayer | |
| } | |
| // Check for: | |
| // SAVE_LAYER | |
| // SAVE | |
| // CLIP_RECT | |
| // DRAW_BITMAP_RECT_TO_RECT | |
| // RESTORE | |
| // RESTORE | |
| // where the saveLayer's color can be moved into the drawBitmapRect | |
| static bool check_1(SkDebugCanvas* canvas, int curCommand) { | |
| if (SkDrawCommand::kSaveLayer_OpType != canvas->getDrawCommandAt(curCommand)->getType() || | |
| canvas->getSize() <= curCommand+5 || | |
| SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() || | |
| SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+2)->getType() || | |
| SkDrawCommand::kDrawBitmapRect_OpType != canvas->getDrawCommandAt(curCommand+3)->getType() || | |
| SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+4)->getType() || | |
| SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+5)->getType()) { | |
| return false; | |
| } | |
| SkSaveLayerCommand* saveLayer = | |
| (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand); | |
| SkDrawBitmapRectCommand* dbmr = | |
| (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+3); | |
| const SkPaint* saveLayerPaint = saveLayer->paint(); | |
| SkPaint* dbmrPaint = dbmr->paint(); | |
| // For this optimization we only fold the saveLayer and drawBitmapRect | |
| // together if the saveLayer's draw is simple (i.e., no fancy effects) and | |
| // and the only difference in the colors is that the saveLayer's can have | |
| // an alpha while the drawBitmapRect's is opaque. | |
| // TODO: it should be possible to fold them together even if they both | |
| // have different non-255 alphas but this is low priority since we have | |
| // never seen that case | |
| // If either operation lacks a paint then the collapse is trivial | |
| SkColor layerColor = saveLayerPaint->getColor() | 0xFF000000; // force opaque | |
| return nullptr == saveLayerPaint || | |
| nullptr == dbmrPaint || | |
| (is_simple(*saveLayerPaint) && dbmrPaint->getColor() == layerColor); | |
| } | |
| // Fold the saveLayer's alpha into the drawBitmapRect and remove the saveLayer | |
| // and restore | |
| static void apply_1(SkDebugCanvas* canvas, int curCommand) { | |
| SkSaveLayerCommand* saveLayer = | |
| (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand); | |
| const SkPaint* saveLayerPaint = saveLayer->paint(); | |
| // if (nullptr == saveLayerPaint) the dbmr's paint doesn't need to be changed | |
| if (saveLayerPaint) { | |
| SkDrawBitmapRectCommand* dbmr = | |
| (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+3); | |
| SkPaint* dbmrPaint = dbmr->paint(); | |
| if (nullptr == dbmrPaint) { | |
| dbmr->setPaint(*saveLayerPaint); | |
| } else { | |
| SkColor newColor = SkColorSetA(dbmrPaint->getColor(), | |
| SkColorGetA(saveLayerPaint->getColor())); | |
| dbmrPaint->setColor(newColor); | |
| } | |
| } | |
| canvas->deleteDrawCommandAt(curCommand+5); // restore | |
| canvas->deleteDrawCommandAt(curCommand); // saveLayer | |
| } | |
| // Check for: | |
| // SAVE | |
| // CLIP_RECT | |
| // DRAW_RECT | |
| // RESTORE | |
| // where the rect is entirely within the clip and the clip is an intersect | |
| static bool check_2(SkDebugCanvas* canvas, int curCommand) { | |
| if (SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand)->getType() || | |
| canvas->getSize() <= curCommand+4 || | |
| SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() || | |
| SkDrawCommand::kDrawRect_OpType != canvas->getDrawCommandAt(curCommand+2)->getType() || | |
| SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+3)->getType()) { | |
| return false; | |
| } | |
| SkClipRectCommand* cr = | |
| (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1); | |
| SkDrawRectCommand* dr = | |
| (SkDrawRectCommand*) canvas->getDrawCommandAt(curCommand+2); | |
| if (SkRegion::kIntersect_Op != cr->op()) { | |
| return false; | |
| } | |
| return cr->rect().contains(dr->rect()); | |
| } | |
| // Remove everything but the drawRect | |
| static void apply_2(SkDebugCanvas* canvas, int curCommand) { | |
| canvas->deleteDrawCommandAt(curCommand+3); // restore | |
| // drawRect | |
| canvas->deleteDrawCommandAt(curCommand+1); // clipRect | |
| canvas->deleteDrawCommandAt(curCommand); // save | |
| } | |
| // Check for: | |
| // SAVE | |
| // CLIP_RRECT | |
| // DRAW_RECT | |
| // RESTORE | |
| // where the rect entirely encloses the clip | |
| static bool check_3(SkDebugCanvas* canvas, int curCommand) { | |
| if (SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand)->getType() || | |
| canvas->getSize() <= curCommand+4 || | |
| SkDrawCommand::kClipRRect_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() || | |
| SkDrawCommand::kDrawRect_OpType != canvas->getDrawCommandAt(curCommand+2)->getType() || | |
| SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+3)->getType()) { | |
| return false; | |
| } | |
| SkClipRRectCommand* crr = | |
| (SkClipRRectCommand*) canvas->getDrawCommandAt(curCommand+1); | |
| SkDrawRectCommand* dr = | |
| (SkDrawRectCommand*) canvas->getDrawCommandAt(curCommand+2); | |
| if (SkRegion::kIntersect_Op != crr->op()) { | |
| return false; | |
| } | |
| return dr->rect().contains(crr->rrect().rect()); | |
| } | |
| // Replace everything with a drawRRect with the paint from the drawRect | |
| // and the AA settings from the clipRRect | |
| static void apply_3(SkDebugCanvas* canvas, int curCommand) { | |
| canvas->deleteDrawCommandAt(curCommand+3); // restore | |
| SkClipRRectCommand* crr = | |
| (SkClipRRectCommand*) canvas->getDrawCommandAt(curCommand+1); | |
| SkDrawRectCommand* dr = | |
| (SkDrawRectCommand*) canvas->getDrawCommandAt(curCommand+2); | |
| // TODO: could skip paint re-creation if the AA settings already match | |
| SkPaint newPaint = dr->paint(); | |
| newPaint.setAntiAlias(crr->doAA()); | |
| SkDrawRRectCommand* drr = new SkDrawRRectCommand(crr->rrect(), newPaint); | |
| canvas->setDrawCommandAt(curCommand+2, drr); | |
| canvas->deleteDrawCommandAt(curCommand+1); // clipRRect | |
| canvas->deleteDrawCommandAt(curCommand); // save | |
| } | |
| // Check for: | |
| // SAVE | |
| // CLIP_RECT | |
| // DRAW_BITMAP_RECT_TO_RECT | |
| // RESTORE | |
| // where the rect and drawBitmapRect dst exactly match | |
| static bool check_4(SkDebugCanvas* canvas, int curCommand) { | |
| if (SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand)->getType() || | |
| canvas->getSize() <= curCommand+4 || | |
| SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() || | |
| SkDrawCommand::kDrawBitmapRect_OpType != canvas->getDrawCommandAt(curCommand+2)->getType() || | |
| SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+3)->getType()) { | |
| return false; | |
| } | |
| SkClipRectCommand* cr = | |
| (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1); | |
| SkDrawBitmapRectCommand* dbmr = | |
| (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+2); | |
| if (SkRegion::kIntersect_Op != cr->op()) { | |
| return false; | |
| } | |
| return dbmr->dstRect() == cr->rect(); | |
| } | |
| // Remove everything but the drawBitmapRect | |
| static void apply_4(SkDebugCanvas* canvas, int curCommand) { | |
| canvas->deleteDrawCommandAt(curCommand+3); // restore | |
| // drawBitmapRectToRect | |
| canvas->deleteDrawCommandAt(curCommand+1); // clipRect | |
| canvas->deleteDrawCommandAt(curCommand); // save | |
| } | |
| // Check for: | |
| // SAVE | |
| // CLIP_RECT | |
| // SAVE_LAYER | |
| // SAVE | |
| // CLIP_RECT | |
| // SAVE_LAYER | |
| // SAVE | |
| // CLIP_RECT | |
| // DRAWBITMAPRECTTORECT | |
| // RESTORE | |
| // RESTORE | |
| // RESTORE | |
| // RESTORE | |
| // RESTORE | |
| // where: | |
| // all the clipRect's are BW, nested, intersections | |
| // the drawBitmapRectToRect is a 1-1 copy from src to dest | |
| // the last (smallest) clip rect is a subset of the drawBitmapRectToRect's dest rect | |
| // all the saveLayer's paints can be rolled into the drawBitmapRectToRect's paint | |
| // This pattern is used by Google spreadsheet when drawing the toolbar buttons | |
| static bool check_7(SkDebugCanvas* canvas, int curCommand) { | |
| if (SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand)->getType() || | |
| canvas->getSize() <= curCommand+13 || | |
| SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() || | |
| SkDrawCommand::kSaveLayer_OpType != canvas->getDrawCommandAt(curCommand+2)->getType() || | |
| SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand+3)->getType() || | |
| SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+4)->getType() || | |
| SkDrawCommand::kSaveLayer_OpType != canvas->getDrawCommandAt(curCommand+5)->getType() || | |
| SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand+6)->getType() || | |
| SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+7)->getType() || | |
| SkDrawCommand::kDrawBitmapRect_OpType != canvas->getDrawCommandAt(curCommand+8)->getType() || | |
| SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+9)->getType() || | |
| SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+10)->getType() || | |
| SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+11)->getType() || | |
| SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+12)->getType() || | |
| SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+13)->getType()) { | |
| return false; | |
| } | |
| SkClipRectCommand* clip0 = | |
| (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1); | |
| SkSaveLayerCommand* saveLayer0 = | |
| (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand+2); | |
| SkClipRectCommand* clip1 = | |
| (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+4); | |
| SkSaveLayerCommand* saveLayer1 = | |
| (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand+5); | |
| SkClipRectCommand* clip2 = | |
| (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+7); | |
| SkDrawBitmapRectCommand* dbmr = | |
| (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+8); | |
| if (clip0->doAA() || clip1->doAA() || clip2->doAA()) { | |
| return false; | |
| } | |
| if (SkRegion::kIntersect_Op != clip0->op() || | |
| SkRegion::kIntersect_Op != clip1->op() || | |
| SkRegion::kIntersect_Op != clip2->op()) { | |
| return false; | |
| } | |
| if (!clip0->rect().contains(clip1->rect()) || | |
| !clip1->rect().contains(clip2->rect())) { | |
| return false; | |
| } | |
| // The src->dest mapping needs to be 1-to-1 | |
| if (nullptr == dbmr->srcRect()) { | |
| if (dbmr->bitmap().width() != dbmr->dstRect().width() || | |
| dbmr->bitmap().height() != dbmr->dstRect().height()) { | |
| return false; | |
| } | |
| } else { | |
| if (dbmr->srcRect()->width() != dbmr->dstRect().width() || | |
| dbmr->srcRect()->height() != dbmr->dstRect().height()) { | |
| return false; | |
| } | |
| } | |
| if (!dbmr->dstRect().contains(clip2->rect())) { | |
| return false; | |
| } | |
| const SkPaint* saveLayerPaint0 = saveLayer0->paint(); | |
| const SkPaint* saveLayerPaint1 = saveLayer1->paint(); | |
| if ((saveLayerPaint0 && !is_simple(*saveLayerPaint0)) || | |
| (saveLayerPaint1 && !is_simple(*saveLayerPaint1))) { | |
| return false; | |
| } | |
| SkPaint* dbmrPaint = dbmr->paint(); | |
| if (nullptr == dbmrPaint) { | |
| return true; | |
| } | |
| if (saveLayerPaint0) { | |
| SkColor layerColor0 = saveLayerPaint0->getColor() | 0xFF000000; // force opaque | |
| if (dbmrPaint->getColor() != layerColor0) { | |
| return false; | |
| } | |
| } | |
| if (saveLayerPaint1) { | |
| SkColor layerColor1 = saveLayerPaint1->getColor() | 0xFF000000; // force opaque | |
| if (dbmrPaint->getColor() != layerColor1) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| // Reduce to a single drawBitmapRectToRect call by folding the clipRect's into | |
| // the src and dst Rects and the saveLayer paints into the drawBitmapRectToRect's | |
| // paint. | |
| static void apply_7(SkDebugCanvas* canvas, int curCommand) { | |
| SkSaveLayerCommand* saveLayer0 = | |
| (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand+2); | |
| SkSaveLayerCommand* saveLayer1 = | |
| (SkSaveLayerCommand*) canvas->getDrawCommandAt(curCommand+5); | |
| SkClipRectCommand* clip2 = | |
| (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+7); | |
| SkDrawBitmapRectCommand* dbmr = | |
| (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+8); | |
| SkScalar newSrcLeft = dbmr->srcRect()->fLeft + clip2->rect().fLeft - dbmr->dstRect().fLeft; | |
| SkScalar newSrcTop = dbmr->srcRect()->fTop + clip2->rect().fTop - dbmr->dstRect().fTop; | |
| SkRect newSrc = SkRect::MakeXYWH(newSrcLeft, newSrcTop, | |
| clip2->rect().width(), clip2->rect().height()); | |
| dbmr->setSrcRect(newSrc); | |
| dbmr->setDstRect(clip2->rect()); | |
| SkColor color = 0xFF000000; | |
| int a0, a1; | |
| const SkPaint* saveLayerPaint0 = saveLayer0->paint(); | |
| if (saveLayerPaint0) { | |
| color = saveLayerPaint0->getColor(); | |
| a0 = SkColorGetA(color); | |
| } else { | |
| a0 = 0xFF; | |
| } | |
| const SkPaint* saveLayerPaint1 = saveLayer1->paint(); | |
| if (saveLayerPaint1) { | |
| color = saveLayerPaint1->getColor(); | |
| a1 = SkColorGetA(color); | |
| } else { | |
| a1 = 0xFF; | |
| } | |
| int newA = SkMulDiv255Round(a0, a1); | |
| SkASSERT(newA <= 0xFF); | |
| SkPaint* dbmrPaint = dbmr->paint(); | |
| if (dbmrPaint) { | |
| SkColor newColor = SkColorSetA(dbmrPaint->getColor(), newA); | |
| dbmrPaint->setColor(newColor); | |
| } else { | |
| SkColor newColor = SkColorSetA(color, newA); | |
| SkPaint newPaint; | |
| newPaint.setColor(newColor); | |
| dbmr->setPaint(newPaint); | |
| } | |
| // remove everything except the drawbitmaprect | |
| canvas->deleteDrawCommandAt(curCommand+13); // restore | |
| canvas->deleteDrawCommandAt(curCommand+12); // restore | |
| canvas->deleteDrawCommandAt(curCommand+11); // restore | |
| canvas->deleteDrawCommandAt(curCommand+10); // restore | |
| canvas->deleteDrawCommandAt(curCommand+9); // restore | |
| canvas->deleteDrawCommandAt(curCommand+7); // clipRect | |
| canvas->deleteDrawCommandAt(curCommand+6); // save | |
| canvas->deleteDrawCommandAt(curCommand+5); // saveLayer | |
| canvas->deleteDrawCommandAt(curCommand+4); // clipRect | |
| canvas->deleteDrawCommandAt(curCommand+3); // save | |
| canvas->deleteDrawCommandAt(curCommand+2); // saveLayer | |
| canvas->deleteDrawCommandAt(curCommand+1); // clipRect | |
| canvas->deleteDrawCommandAt(curCommand); // save | |
| } | |
| // Check for: | |
| // SAVE | |
| // CLIP_RECT | |
| // DRAWBITMAPRECTTORECT | |
| // RESTORE | |
| // where: | |
| // the drawBitmapRectToRect is a 1-1 copy from src to dest | |
| // the clip rect is BW and a subset of the drawBitmapRectToRect's dest rect | |
| static bool check_8(SkDebugCanvas* canvas, int curCommand) { | |
| if (SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand)->getType() || | |
| canvas->getSize() <= curCommand+4 || | |
| SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() || | |
| SkDrawCommand::kDrawBitmapRect_OpType != canvas->getDrawCommandAt(curCommand+2)->getType() || | |
| SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+3)->getType()) { | |
| return false; | |
| } | |
| SkClipRectCommand* clip = | |
| (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1); | |
| SkDrawBitmapRectCommand* dbmr = | |
| (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+2); | |
| if (clip->doAA() || SkRegion::kIntersect_Op != clip->op()) { | |
| return false; | |
| } | |
| // The src->dest mapping needs to be 1-to-1 | |
| if (nullptr == dbmr->srcRect()) { | |
| if (dbmr->bitmap().width() != dbmr->dstRect().width() || | |
| dbmr->bitmap().height() != dbmr->dstRect().height()) { | |
| return false; | |
| } | |
| } else { | |
| if (dbmr->srcRect()->width() != dbmr->dstRect().width() || | |
| dbmr->srcRect()->height() != dbmr->dstRect().height()) { | |
| return false; | |
| } | |
| } | |
| if (!dbmr->dstRect().contains(clip->rect())) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| // Fold the clipRect into the drawBitmapRectToRect's src and dest rects | |
| static void apply_8(SkDebugCanvas* canvas, int curCommand) { | |
| SkClipRectCommand* clip = | |
| (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1); | |
| SkDrawBitmapRectCommand* dbmr = | |
| (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+2); | |
| SkScalar newSrcLeft, newSrcTop; | |
| if (dbmr->srcRect()) { | |
| newSrcLeft = dbmr->srcRect()->fLeft + clip->rect().fLeft - dbmr->dstRect().fLeft; | |
| newSrcTop = dbmr->srcRect()->fTop + clip->rect().fTop - dbmr->dstRect().fTop; | |
| } else { | |
| newSrcLeft = clip->rect().fLeft - dbmr->dstRect().fLeft; | |
| newSrcTop = clip->rect().fTop - dbmr->dstRect().fTop; | |
| } | |
| SkRect newSrc = SkRect::MakeXYWH(newSrcLeft, newSrcTop, | |
| clip->rect().width(), clip->rect().height()); | |
| dbmr->setSrcRect(newSrc); | |
| dbmr->setDstRect(clip->rect()); | |
| // remove everything except the drawbitmaprect | |
| canvas->deleteDrawCommandAt(curCommand+3); | |
| canvas->deleteDrawCommandAt(curCommand+1); | |
| canvas->deleteDrawCommandAt(curCommand); | |
| } | |
| // Check for: | |
| // SAVE | |
| // CLIP_RECT | |
| // DRAWBITMAPRECTTORECT | |
| // RESTORE | |
| // where: | |
| // clipRect is BW and encloses the DBMR2R's dest rect | |
| static bool check_9(SkDebugCanvas* canvas, int curCommand) { | |
| if (SkDrawCommand::kSave_OpType != canvas->getDrawCommandAt(curCommand)->getType() || | |
| canvas->getSize() <= curCommand+4 || | |
| SkDrawCommand::kClipRect_OpType != canvas->getDrawCommandAt(curCommand+1)->getType() || | |
| SkDrawCommand::kDrawBitmapRect_OpType != canvas->getDrawCommandAt(curCommand+2)->getType() || | |
| SkDrawCommand::kRestore_OpType != canvas->getDrawCommandAt(curCommand+3)->getType()) { | |
| return false; | |
| } | |
| SkClipRectCommand* clip = | |
| (SkClipRectCommand*) canvas->getDrawCommandAt(curCommand+1); | |
| SkDrawBitmapRectCommand* dbmr = | |
| (SkDrawBitmapRectCommand*) canvas->getDrawCommandAt(curCommand+2); | |
| if (clip->doAA() || SkRegion::kIntersect_Op != clip->op()) { | |
| return false; | |
| } | |
| if (!clip->rect().contains(dbmr->dstRect())) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| // remove everything except the drawbitmaprect | |
| static void apply_9(SkDebugCanvas* canvas, int curCommand) { | |
| canvas->deleteDrawCommandAt(curCommand+3); // restore | |
| // drawBitmapRectToRect | |
| canvas->deleteDrawCommandAt(curCommand+1); // clipRect | |
| canvas->deleteDrawCommandAt(curCommand); // save | |
| } | |
| typedef bool (*PFCheck)(SkDebugCanvas* canvas, int curCommand); | |
| typedef void (*PFApply)(SkDebugCanvas* canvas, int curCommand); | |
| struct OptTableEntry { | |
| PFCheck fCheck; | |
| PFApply fApply; | |
| int fNumTimesApplied; | |
| } gOptTable[] = { | |
| { check_0, apply_0, 0 }, | |
| { check_1, apply_1, 0 }, | |
| { check_2, apply_2, 0 }, | |
| { check_3, apply_3, 0 }, | |
| { check_4, apply_4, 0 }, | |
| { check_7, apply_7, 0 }, | |
| { check_8, apply_8, 0 }, | |
| { check_9, apply_9, 0 }, | |
| }; | |
| static int filter_picture(const SkString& inFile, const SkString& outFile) { | |
| SkAutoTUnref<SkPicture> inPicture; | |
| SkFILEStream inStream(inFile.c_str()); | |
| if (inStream.isValid()) { | |
| inPicture.reset(SkPicture::CreateFromStream(&inStream)); | |
| } | |
| if (nullptr == inPicture.get()) { | |
| SkDebugf("Could not read file %s\n", inFile.c_str()); | |
| return -1; | |
| } | |
| int localCount[SK_ARRAY_COUNT(gOptTable)]; | |
| memset(localCount, 0, sizeof(localCount)); | |
| SkDebugCanvas debugCanvas(SkScalarCeilToInt(inPicture->cullRect().width()), | |
| SkScalarCeilToInt(inPicture->cullRect().height())); | |
| inPicture->playback(&debugCanvas); | |
| // delete the initial save and restore since replaying the commands will | |
| // re-add them | |
| if (debugCanvas.getSize() > 1) { | |
| debugCanvas.deleteDrawCommandAt(0); | |
| debugCanvas.deleteDrawCommandAt(debugCanvas.getSize()-1); | |
| } | |
| bool changed = true; | |
| int numBefore = debugCanvas.getSize(); | |
| while (changed) { | |
| changed = false; | |
| for (int i = 0; i < debugCanvas.getSize(); ++i) { | |
| for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) { | |
| if ((*gOptTable[opt].fCheck)(&debugCanvas, i)) { | |
| (*gOptTable[opt].fApply)(&debugCanvas, i); | |
| ++gOptTable[opt].fNumTimesApplied; | |
| ++localCount[opt]; | |
| if (debugCanvas.getSize() == i) { | |
| // the optimization removed all the remaining operations | |
| break; | |
| } | |
| opt = 0; // try all the opts all over again | |
| changed = true; | |
| } | |
| } | |
| } | |
| } | |
| int numAfter = debugCanvas.getSize(); | |
| if (!outFile.isEmpty()) { | |
| SkPictureRecorder recorder; | |
| SkCanvas* canvas = recorder.beginRecording(inPicture->cullRect().width(), | |
| inPicture->cullRect().height(), | |
| nullptr, 0); | |
| debugCanvas.draw(canvas); | |
| SkAutoTUnref<SkPicture> outPicture(recorder.endRecording()); | |
| SkFILEWStream outStream(outFile.c_str()); | |
| outPicture->serialize(&outStream); | |
| } | |
| bool someOptFired = false; | |
| for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) { | |
| if (0 != localCount[opt]) { | |
| SkDebugf("%d: %d ", opt, localCount[opt]); | |
| someOptFired = true; | |
| } | |
| } | |
| if (!someOptFired) { | |
| SkDebugf("No opts fired\n"); | |
| } else { | |
| SkDebugf("\t before: %d after: %d delta: %d\n", | |
| numBefore, numAfter, numBefore-numAfter); | |
| } | |
| return 0; | |
| } | |
| // This function is not marked as 'static' so it can be referenced externally | |
| // in the iOS build. | |
| int tool_main(int argc, char** argv); // suppress a warning on mac | |
| int tool_main(int argc, char** argv) { | |
| SkGraphics::Init(); | |
| if (argc < 3) { | |
| usage(); | |
| return -1; | |
| } | |
| SkString inFile, outFile, inDir, outDir; | |
| char* const* stop = argv + argc; | |
| for (++argv; argv < stop; ++argv) { | |
| if (strcmp(*argv, "-i") == 0) { | |
| argv++; | |
| if (argv < stop && **argv) { | |
| inFile.set(*argv); | |
| } else { | |
| SkDebugf("missing arg for -i\n"); | |
| usage(); | |
| return -1; | |
| } | |
| } else if (strcmp(*argv, "--input-dir") == 0) { | |
| argv++; | |
| if (argv < stop && **argv) { | |
| inDir.set(*argv); | |
| } else { | |
| SkDebugf("missing arg for --input-dir\n"); | |
| usage(); | |
| return -1; | |
| } | |
| } else if (strcmp(*argv, "--output-dir") == 0) { | |
| argv++; | |
| if (argv < stop && **argv) { | |
| outDir.set(*argv); | |
| } else { | |
| SkDebugf("missing arg for --output-dir\n"); | |
| usage(); | |
| return -1; | |
| } | |
| } else if (strcmp(*argv, "-o") == 0) { | |
| argv++; | |
| if (argv < stop && **argv) { | |
| outFile.set(*argv); | |
| } else { | |
| SkDebugf("missing arg for -o\n"); | |
| usage(); | |
| return -1; | |
| } | |
| } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) { | |
| usage(); | |
| return 0; | |
| } else { | |
| SkDebugf("unknown arg %s\n", *argv); | |
| usage(); | |
| return -1; | |
| } | |
| } | |
| SkOSFile::Iter iter(inDir.c_str(), "skp"); | |
| SkString inputFilename, outputFilename; | |
| if (iter.next(&inputFilename)) { | |
| do { | |
| inFile = SkOSPath::Join(inDir.c_str(), inputFilename.c_str()); | |
| if (!outDir.isEmpty()) { | |
| outFile = SkOSPath::Join(outDir.c_str(), inputFilename.c_str()); | |
| } | |
| SkDebugf("Executing %s\n", inputFilename.c_str()); | |
| filter_picture(inFile, outFile); | |
| } while(iter.next(&inputFilename)); | |
| } else if (!inFile.isEmpty()) { | |
| filter_picture(inFile, outFile); | |
| } else { | |
| usage(); | |
| return -1; | |
| } | |
| for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) { | |
| SkDebugf("opt %d: %d\n", opt, gOptTable[opt].fNumTimesApplied); | |
| } | |
| return 0; | |
| } | |
| #if !defined SK_BUILD_FOR_IOS | |
| int main(int argc, char * const argv[]) { | |
| return tool_main(argc, (char**) argv); | |
| } | |
| #endif |