filtermain.cpp revision 3d18d063f0c6b97b25b88707cfbc1c8cb584caa0
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 "SkGraphics.h" 11#include "SkImageDecoder.h" 12#include "SkImageEncoder.h" 13#include "SkOSFile.h" 14#include "SkPicture.h" 15#include "SkPicturePlayback.h" 16#include "SkPictureRecord.h" 17#include "SkStream.h" 18#include "picture_utils.h" 19#include "path_utils.h" 20 21static void usage() { 22 SkDebugf("Usage: filter -i inFile [-o outFile] [--input-dir path] [--output-dir path]\n"); 23 SkDebugf(" [-h|--help]\n\n"); 24 SkDebugf(" -i inFile : file to file.\n"); 25 SkDebugf(" -o outFile : result of filtering.\n"); 26 SkDebugf(" --input-dir : process all files in dir with .skp extension.\n"); 27 SkDebugf(" --output-dir : results of filtering the input dir.\n"); 28 SkDebugf(" -h|--help : Show this help message.\n"); 29} 30 31// Is the supplied paint simply a color? 32static bool is_simple(const SkPaint& p) { 33 return NULL == p.getPathEffect() && 34 NULL == p.getShader() && 35 NULL == p.getXfermode() && 36 NULL == p.getMaskFilter() && 37 NULL == p.getColorFilter() && 38 NULL == p.getRasterizer() && 39 NULL == p.getLooper() && 40 NULL == p.getImageFilter(); 41} 42 43// Check for: 44// SAVE_LAYER 45// DRAW_BITMAP_RECT_TO_RECT 46// RESTORE 47// where the saveLayer's color can be moved into the drawBitmapRect 48static bool check_0(const SkTDArray<SkDrawCommand*>& commands, int curCommand) { 49 if (SAVE_LAYER != commands[curCommand]->getType() || 50 commands.count() <= curCommand+2 || 51 DRAW_BITMAP_RECT_TO_RECT != commands[curCommand+1]->getType() || 52 RESTORE != commands[curCommand+2]->getType()) 53 return false; 54 55 SaveLayer* saveLayer = (SaveLayer*) commands[curCommand]; 56 DrawBitmapRect* dbmr = (DrawBitmapRect*) commands[curCommand+1]; 57 58 const SkPaint* saveLayerPaint = saveLayer->paint(); 59 SkPaint* dbmrPaint = dbmr->paint(); 60 61 // For this optimization we only fold the saveLayer and drawBitmapRect 62 // together if the saveLayer's draw is simple (i.e., no fancy effects) and 63 // and the only difference in the colors is that the saveLayer's can have 64 // an alpha while the drawBitmapRect's is opaque. 65 // TODO: it should be possible to fold them together even if they both 66 // have different non-255 alphas but this is low priority since we have 67 // never seen that case 68 // If either operation lacks a paint then the collapse is trivial 69 SkColor layerColor = saveLayerPaint->getColor() | 0xFF000000; // force opaque 70 71 return NULL == saveLayerPaint || 72 NULL == dbmrPaint || 73 (is_simple(*saveLayerPaint) && dbmrPaint->getColor() == layerColor); 74} 75 76// Fold the saveLayer's alpha into the drawBitmapRect and remove the saveLayer 77// and restore 78static void apply_0(const SkTDArray<SkDrawCommand*>& commands, int curCommand) { 79 SaveLayer* saveLayer = (SaveLayer*) commands[curCommand]; 80 DrawBitmapRect* dbmr = (DrawBitmapRect*) commands[curCommand+1]; 81 Restore* restore = (Restore*) commands[curCommand+2]; 82 83 const SkPaint* saveLayerPaint = saveLayer->paint(); 84 SkPaint* dbmrPaint = dbmr->paint(); 85 86 if (NULL == saveLayerPaint) { 87 saveLayer->setVisible(false); 88 restore->setVisible(false); 89 } else if (NULL == dbmrPaint) { 90 saveLayer->setVisible(false); 91 dbmr->setPaint(*saveLayerPaint); 92 restore->setVisible(false); 93 } else { 94 saveLayer->setVisible(false); 95 SkColor newColor = SkColorSetA(dbmrPaint->getColor(), 96 SkColorGetA(saveLayerPaint->getColor())); 97 dbmrPaint->setColor(newColor); 98 restore->setVisible(false); 99 } 100} 101 102// Check for: 103// SAVE_LAYER 104// SAVE 105// CLIP_RECT 106// DRAW_BITMAP_RECT_TO_RECT 107// RESTORE 108// RESTORE 109// where the saveLayer's color can be moved into the drawBitmapRect 110static bool check_1(const SkTDArray<SkDrawCommand*>& commands, int curCommand) { 111 if (SAVE_LAYER != commands[curCommand]->getType() || 112 commands.count() <= curCommand+5 || 113 SAVE != commands[curCommand+1]->getType() || 114 CLIP_RECT != commands[curCommand+2]->getType() || 115 DRAW_BITMAP_RECT_TO_RECT != commands[curCommand+3]->getType() || 116 RESTORE != commands[curCommand+4]->getType() || 117 RESTORE != commands[curCommand+5]->getType()) 118 return false; 119 120 SaveLayer* saveLayer = (SaveLayer*) commands[curCommand]; 121 DrawBitmapRect* dbmr = (DrawBitmapRect*) commands[curCommand+3]; 122 123 const SkPaint* saveLayerPaint = saveLayer->paint(); 124 SkPaint* dbmrPaint = dbmr->paint(); 125 126 // For this optimization we only fold the saveLayer and drawBitmapRect 127 // together if the saveLayer's draw is simple (i.e., no fancy effects) and 128 // and the only difference in the colors is that the saveLayer's can have 129 // an alpha while the drawBitmapRect's is opaque. 130 // TODO: it should be possible to fold them together even if they both 131 // have different non-255 alphas but this is low priority since we have 132 // never seen that case 133 // If either operation lacks a paint then the collapse is trivial 134 SkColor layerColor = saveLayerPaint->getColor() | 0xFF000000; // force opaque 135 136 return NULL == saveLayerPaint || 137 NULL == dbmrPaint || 138 (is_simple(*saveLayerPaint) && dbmrPaint->getColor() == layerColor); 139} 140 141// Fold the saveLayer's alpha into the drawBitmapRect and remove the saveLayer 142// and restore 143static void apply_1(const SkTDArray<SkDrawCommand*>& commands, int curCommand) { 144 SaveLayer* saveLayer = (SaveLayer*) commands[curCommand]; 145 DrawBitmapRect* dbmr = (DrawBitmapRect*) commands[curCommand+3]; 146 Restore* restore = (Restore*) commands[curCommand+5]; 147 148 const SkPaint* saveLayerPaint = saveLayer->paint(); 149 SkPaint* dbmrPaint = dbmr->paint(); 150 151 if (NULL == saveLayerPaint) { 152 saveLayer->setVisible(false); 153 restore->setVisible(false); 154 } else if (NULL == dbmrPaint) { 155 saveLayer->setVisible(false); 156 dbmr->setPaint(*saveLayerPaint); 157 restore->setVisible(false); 158 } else { 159 saveLayer->setVisible(false); 160 SkColor newColor = SkColorSetA(dbmrPaint->getColor(), 161 SkColorGetA(saveLayerPaint->getColor())); 162 dbmrPaint->setColor(newColor); 163 restore->setVisible(false); 164 } 165} 166 167typedef bool (*PFCheck)(const SkTDArray<SkDrawCommand*>& commands, int curCommand); 168typedef void (*PFApply)(const SkTDArray<SkDrawCommand*>& commands, int curCommand); 169 170struct OptTableEntry { 171 PFCheck fCheck; 172 PFApply fApply; 173 int fNumTimesApplied; 174} gOptTable[] = { 175 { check_0, apply_0, 0 }, 176 { check_1, apply_1, 0 }, 177}; 178 179static int filter_picture(const SkString& inFile, const SkString& outFile) { 180 SkPicture* inPicture = NULL; 181 182 SkFILEStream inStream(inFile.c_str()); 183 if (inStream.isValid()) { 184 inPicture = SkNEW_ARGS(SkPicture, (&inStream, NULL, &SkImageDecoder::DecodeStream)); 185 } 186 187 if (NULL == inPicture) { 188 SkDebugf("Could not read file %s\n", inFile.c_str()); 189 return -1; 190 } 191 192 int localCount[SK_ARRAY_COUNT(gOptTable)]; 193 194 memset(localCount, 0, sizeof(localCount)); 195 196 SkDebugCanvas debugCanvas(inPicture->width(), inPicture->height()); 197 debugCanvas.setBounds(inPicture->width(), inPicture->height()); 198 inPicture->draw(&debugCanvas); 199 200 const SkTDArray<SkDrawCommand*>& commands = debugCanvas.getDrawCommands(); 201 202 // hide the initial save and restore since replaying the commands will 203 // re-add them 204 if (commands.count() > 0) { 205 commands[0]->setVisible(false); 206 commands[commands.count()-1]->setVisible(false); 207 } 208 209 for (int i = 0; i < commands.count(); ++i) { 210 for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) { 211 if ((*gOptTable[opt].fCheck)(commands, i)) { 212 (*gOptTable[opt].fApply)(commands, i); 213 ++gOptTable[opt].fNumTimesApplied; 214 ++localCount[opt]; 215 } 216 } 217 } 218 219 if (!outFile.isEmpty()) { 220 SkPicture outPicture; 221 222 SkCanvas* canvas = outPicture.beginRecording(inPicture->width(), inPicture->height()); 223 debugCanvas.draw(canvas); 224 outPicture.endRecording(); 225 226 SkFILEWStream outStream(outFile.c_str()); 227 228 outPicture.serialize(&outStream); 229 } 230 231 bool someOptFired = false; 232 for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) { 233 if (0 != localCount[opt]) { 234 SkDebugf("%d: %d ", opt, localCount[opt]); 235 someOptFired = true; 236 } 237 } 238 239 if (!someOptFired) { 240 SkDebugf("No opts fired\n"); 241 } else { 242 SkDebugf("\n"); 243 } 244 245 return 0; 246} 247 248// This function is not marked as 'static' so it can be referenced externally 249// in the iOS build. 250int tool_main(int argc, char** argv); // suppress a warning on mac 251 252int tool_main(int argc, char** argv) { 253 SkGraphics::Init(); 254 255 if (argc < 3) { 256 usage(); 257 return -1; 258 } 259 260 SkString inFile, outFile, inDir, outDir; 261 262 char* const* stop = argv + argc; 263 for (++argv; argv < stop; ++argv) { 264 if (strcmp(*argv, "-i") == 0) { 265 argv++; 266 if (argv < stop && **argv) { 267 inFile.set(*argv); 268 } else { 269 SkDebugf("missing arg for -i\n"); 270 usage(); 271 return -1; 272 } 273 } else if (strcmp(*argv, "--input-dir") == 0) { 274 argv++; 275 if (argv < stop && **argv) { 276 inDir.set(*argv); 277 } else { 278 SkDebugf("missing arg for --input-dir\n"); 279 usage(); 280 return -1; 281 } 282 } else if (strcmp(*argv, "--output-dir") == 0) { 283 argv++; 284 if (argv < stop && **argv) { 285 outDir.set(*argv); 286 } else { 287 SkDebugf("missing arg for --output-dir\n"); 288 usage(); 289 return -1; 290 } 291 } else if (strcmp(*argv, "-o") == 0) { 292 argv++; 293 if (argv < stop && **argv) { 294 outFile.set(*argv); 295 } else { 296 SkDebugf("missing arg for -o\n"); 297 usage(); 298 return -1; 299 } 300 } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) { 301 usage(); 302 return 0; 303 } else { 304 SkDebugf("unknown arg %s\n", *argv); 305 usage(); 306 return -1; 307 } 308 } 309 310 SkOSFile::Iter iter(inDir.c_str(), "skp"); 311 312 SkString inputFilename, outputFilename; 313 if (iter.next(&inputFilename)) { 314 315 do { 316 sk_tools::make_filepath(&inFile, inDir, inputFilename); 317 if (!outDir.isEmpty()) { 318 sk_tools::make_filepath(&outFile, outDir, inputFilename); 319 } 320 SkDebugf("Executing %s\n", inputFilename.c_str()); 321 filter_picture(inFile, outFile); 322 } while(iter.next(&inputFilename)); 323 324 } else if (!inFile.isEmpty()) { 325 filter_picture(inFile, outFile); 326 } else { 327 usage(); 328 return -1; 329 } 330 331 for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) { 332 SkDebugf("opt %d: %d\n", opt, gOptTable[opt].fNumTimesApplied); 333 } 334 335 SkGraphics::Term(); 336 return 0; 337} 338 339#if !defined SK_BUILD_FOR_IOS 340int main(int argc, char * const argv[]) { 341 return tool_main(argc, (char**) argv); 342} 343#endif 344