filtermain.cpp revision febc0ec41b4cff6ea69f2b89d72c0d330d198283
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(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(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 167// Check for: 168// SAVE 169// CLIP_RECT 170// DRAW_RECT 171// RESTORE 172// where the rect is entirely within the clip and the clip is an intersect 173static bool check_2(const SkTDArray<SkDrawCommand*>& commands, int curCommand) { 174 if (SAVE != commands[curCommand]->getType() || 175 commands.count() <= curCommand+4 || 176 CLIP_RECT != commands[curCommand+1]->getType() || 177 DRAW_RECT != commands[curCommand+2]->getType() || 178 RESTORE != commands[curCommand+3]->getType()) 179 return false; 180 181 ClipRect* cr = (ClipRect*) commands[curCommand+1]; 182 DrawRectC* dr = (DrawRectC*) commands[curCommand+2]; 183 184 if (SkRegion::kIntersect_Op != cr->op()) { 185 return false; 186 } 187 188 return cr->rect().contains(dr->rect()); 189} 190 191// Remove everything but the drawRect 192static void apply_2(SkTDArray<SkDrawCommand*>& commands, int curCommand) { 193 Save* save = (Save*) commands[curCommand]; 194 ClipRect* cr = (ClipRect*) commands[curCommand+1]; 195 Restore* restore = (Restore*) commands[curCommand+3]; 196 197 save->setVisible(false); 198 cr->setVisible(false); 199 // leave the drawRect alone 200 restore->setVisible(false); 201} 202 203// Check for: 204// SAVE 205// CLIP_RRECT 206// DRAW_RECT 207// RESTORE 208// where the rect entirely encloses the clip 209static bool check_3(const SkTDArray<SkDrawCommand*>& commands, int curCommand) { 210 if (SAVE != commands[curCommand]->getType() || 211 commands.count() <= curCommand+4 || 212 CLIP_RRECT != commands[curCommand+1]->getType() || 213 DRAW_RECT != commands[curCommand+2]->getType() || 214 RESTORE != commands[curCommand+3]->getType()) 215 return false; 216 217 ClipRRect* crr = (ClipRRect*) commands[curCommand+1]; 218 DrawRectC* dr = (DrawRectC*) commands[curCommand+2]; 219 220 if (SkRegion::kIntersect_Op != crr->op()) { 221 return false; 222 } 223 224 return dr->rect().contains(crr->rrect().rect()); 225} 226 227// Replace everything with a drawRRect with the paint from the drawRect 228// and the AA settings from the clipRRect 229static void apply_3(SkTDArray<SkDrawCommand*>& commands, int curCommand) { 230 Save* save = (Save*) commands[curCommand]; 231 ClipRRect* crr = (ClipRRect*) commands[curCommand+1]; 232 DrawRectC* dr = (DrawRectC*) commands[curCommand+2]; 233 Restore* restore = (Restore*) commands[curCommand+3]; 234 235 save->setVisible(false); 236 crr->setVisible(false); 237 dr->setVisible(false); 238 restore->setVisible(false); 239 240 // TODO: could skip paint re-creation if the AA settings already match 241 SkPaint newPaint = *dr->paint(); 242 newPaint.setAntiAlias(crr->doAA()); 243 DrawRRect* drr = new DrawRRect(crr->rrect(), newPaint); 244 commands[curCommand+2] = drr; 245} 246 247// Check for: 248// SAVE 249// CLIP_RECT 250// DRAW_BITMAP_RECT_TO_RECT 251// RESTORE 252// where the rect and drawBitmapRect dst exactly match 253static bool check_4(const SkTDArray<SkDrawCommand*>& commands, int curCommand) { 254 if (SAVE != commands[curCommand]->getType() || 255 commands.count() <= curCommand+4 || 256 CLIP_RECT != commands[curCommand+1]->getType() || 257 DRAW_BITMAP_RECT_TO_RECT != commands[curCommand+2]->getType() || 258 RESTORE != commands[curCommand+3]->getType()) 259 return false; 260 261 ClipRect* cr = (ClipRect*) commands[curCommand+1]; 262 DrawBitmapRect* dbmr = (DrawBitmapRect*) commands[curCommand+2]; 263 264 if (SkRegion::kIntersect_Op != cr->op()) { 265 return false; 266 } 267 268 return dbmr->dstRect() == cr->rect(); 269} 270 271// Remove everything but the drawBitmapRect 272static void apply_4(SkTDArray<SkDrawCommand*>& commands, int curCommand) { 273 Save* save = (Save*) commands[curCommand]; 274 ClipRect* cr = (ClipRect*) commands[curCommand+1]; 275 Restore* restore = (Restore*) commands[curCommand+3]; 276 277 save->setVisible(false); 278 cr->setVisible(false); 279 // leave drawBitmapRect alone 280 restore->setVisible(false); 281} 282 283typedef bool (*PFCheck)(const SkTDArray<SkDrawCommand*>& commands, int curCommand); 284typedef void (*PFApply)(SkTDArray<SkDrawCommand*>& commands, int curCommand); 285 286struct OptTableEntry { 287 PFCheck fCheck; 288 PFApply fApply; 289 int fNumTimesApplied; 290} gOptTable[] = { 291 { check_0, apply_0, 0 }, 292 { check_1, apply_1, 0 }, 293 { check_2, apply_2, 0 }, 294 { check_3, apply_3, 0 }, 295 { check_4, apply_4, 0 }, 296}; 297 298static int filter_picture(const SkString& inFile, const SkString& outFile) { 299 SkPicture* inPicture = NULL; 300 301 SkFILEStream inStream(inFile.c_str()); 302 if (inStream.isValid()) { 303 inPicture = SkNEW_ARGS(SkPicture, (&inStream, NULL, &SkImageDecoder::DecodeMemory)); 304 } 305 306 if (NULL == inPicture) { 307 SkDebugf("Could not read file %s\n", inFile.c_str()); 308 return -1; 309 } 310 311 int localCount[SK_ARRAY_COUNT(gOptTable)]; 312 313 memset(localCount, 0, sizeof(localCount)); 314 315 SkDebugCanvas debugCanvas(inPicture->width(), inPicture->height()); 316 debugCanvas.setBounds(inPicture->width(), inPicture->height()); 317 inPicture->draw(&debugCanvas); 318 319 SkTDArray<SkDrawCommand*>& commands = debugCanvas.getDrawCommands(); 320 321 // hide the initial save and restore since replaying the commands will 322 // re-add them 323 if (commands.count() > 0) { 324 commands[0]->setVisible(false); 325 commands[commands.count()-1]->setVisible(false); 326 } 327 328 for (int i = 0; i < commands.count(); ++i) { 329 for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) { 330 if ((*gOptTable[opt].fCheck)(commands, i)) { 331 (*gOptTable[opt].fApply)(commands, i); 332 ++gOptTable[opt].fNumTimesApplied; 333 ++localCount[opt]; 334 } 335 } 336 } 337 338 if (!outFile.isEmpty()) { 339 SkPicture outPicture; 340 341 SkCanvas* canvas = outPicture.beginRecording(inPicture->width(), inPicture->height()); 342 debugCanvas.draw(canvas); 343 outPicture.endRecording(); 344 345 SkFILEWStream outStream(outFile.c_str()); 346 347 outPicture.serialize(&outStream); 348 } 349 350 bool someOptFired = false; 351 for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) { 352 if (0 != localCount[opt]) { 353 SkDebugf("%d: %d ", opt, localCount[opt]); 354 someOptFired = true; 355 } 356 } 357 358 if (!someOptFired) { 359 SkDebugf("No opts fired\n"); 360 } else { 361 SkDebugf("\n"); 362 } 363 364 return 0; 365} 366 367// This function is not marked as 'static' so it can be referenced externally 368// in the iOS build. 369int tool_main(int argc, char** argv); // suppress a warning on mac 370 371int tool_main(int argc, char** argv) { 372 SkGraphics::Init(); 373 374 if (argc < 3) { 375 usage(); 376 return -1; 377 } 378 379 SkString inFile, outFile, inDir, outDir; 380 381 char* const* stop = argv + argc; 382 for (++argv; argv < stop; ++argv) { 383 if (strcmp(*argv, "-i") == 0) { 384 argv++; 385 if (argv < stop && **argv) { 386 inFile.set(*argv); 387 } else { 388 SkDebugf("missing arg for -i\n"); 389 usage(); 390 return -1; 391 } 392 } else if (strcmp(*argv, "--input-dir") == 0) { 393 argv++; 394 if (argv < stop && **argv) { 395 inDir.set(*argv); 396 } else { 397 SkDebugf("missing arg for --input-dir\n"); 398 usage(); 399 return -1; 400 } 401 } else if (strcmp(*argv, "--output-dir") == 0) { 402 argv++; 403 if (argv < stop && **argv) { 404 outDir.set(*argv); 405 } else { 406 SkDebugf("missing arg for --output-dir\n"); 407 usage(); 408 return -1; 409 } 410 } else if (strcmp(*argv, "-o") == 0) { 411 argv++; 412 if (argv < stop && **argv) { 413 outFile.set(*argv); 414 } else { 415 SkDebugf("missing arg for -o\n"); 416 usage(); 417 return -1; 418 } 419 } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) { 420 usage(); 421 return 0; 422 } else { 423 SkDebugf("unknown arg %s\n", *argv); 424 usage(); 425 return -1; 426 } 427 } 428 429 SkOSFile::Iter iter(inDir.c_str(), "skp"); 430 431 SkString inputFilename, outputFilename; 432 if (iter.next(&inputFilename)) { 433 434 do { 435 sk_tools::make_filepath(&inFile, inDir, inputFilename); 436 if (!outDir.isEmpty()) { 437 sk_tools::make_filepath(&outFile, outDir, inputFilename); 438 } 439 SkDebugf("Executing %s\n", inputFilename.c_str()); 440 filter_picture(inFile, outFile); 441 } while(iter.next(&inputFilename)); 442 443 } else if (!inFile.isEmpty()) { 444 filter_picture(inFile, outFile); 445 } else { 446 usage(); 447 return -1; 448 } 449 450 for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) { 451 SkDebugf("opt %d: %d\n", opt, gOptTable[opt].fNumTimesApplied); 452 } 453 454 SkGraphics::Term(); 455 return 0; 456} 457 458#if !defined SK_BUILD_FOR_IOS 459int main(int argc, char * const argv[]) { 460 return tool_main(argc, (char**) argv); 461} 462#endif 463