blob: 3133ada8e55da5849965b9e1253056e178e24302 [file] [log] [blame]
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +00001/*
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
robertphillips@google.com3b0a9fe2013-01-31 15:56:22 +00008#include "SkDebugCanvas.h"
robertphillips@google.com801cee12012-10-19 19:06:11 +00009#include "SkDevice.h"
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +000010#include "SkGraphics.h"
robertphillips@google.com4e4d75b2012-11-12 18:03:19 +000011#include "SkImageDecoder.h"
robertphillips@google.com801cee12012-10-19 19:06:11 +000012#include "SkImageEncoder.h"
djsollen@google.coma09e8832012-11-13 18:50:33 +000013#include "SkOSFile.h"
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +000014#include "SkPicture.h"
robertphillips@google.com801cee12012-10-19 19:06:11 +000015#include "SkPicturePlayback.h"
16#include "SkPictureRecord.h"
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +000017#include "SkStream.h"
djsollen@google.coma09e8832012-11-13 18:50:33 +000018#include "picture_utils.h"
robertphillips@google.comd3d377f2012-12-07 20:56:13 +000019#include "path_utils.h"
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +000020
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +000021static void usage() {
robertphillips@google.comd3d377f2012-12-07 20:56:13 +000022 SkDebugf("Usage: filter -i inFile [-o outFile] [--input-dir path] [--output-dir path]\n");
robertphillips@google.com3b0a9fe2013-01-31 15:56:22 +000023 SkDebugf(" [-h|--help]\n\n");
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +000024 SkDebugf(" -i inFile : file to file.\n");
25 SkDebugf(" -o outFile : result of filtering.\n");
djsollen@google.coma09e8832012-11-13 18:50:33 +000026 SkDebugf(" --input-dir : process all files in dir with .skp extension.\n");
27 SkDebugf(" --output-dir : results of filtering the input dir.\n");
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +000028 SkDebugf(" -h|--help : Show this help message.\n");
29}
30
robertphillips@google.com3b0a9fe2013-01-31 15:56:22 +000031// 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}
robertphillips@google.comd3d377f2012-12-07 20:56:13 +000042
robertphillips@google.com73743552013-02-05 20:51:49 +000043// 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
skia.committer@gmail.com3d18d062013-02-14 07:01:34 +000061 // For this optimization we only fold the saveLayer and drawBitmapRect
robertphillips@google.com1780a3c2013-02-13 13:27:44 +000062 // 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
robertphillips@google.com73743552013-02-05 20:51:49 +000071 return NULL == saveLayerPaint ||
72 NULL == dbmrPaint ||
robertphillips@google.com1780a3c2013-02-13 13:27:44 +000073 (is_simple(*saveLayerPaint) && dbmrPaint->getColor() == layerColor);
robertphillips@google.com73743552013-02-05 20:51:49 +000074}
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
skia.committer@gmail.com3d18d062013-02-14 07:01:34 +0000126 // For this optimization we only fold the saveLayer and drawBitmapRect
robertphillips@google.com1780a3c2013-02-13 13:27:44 +0000127 // 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
robertphillips@google.com73743552013-02-05 20:51:49 +0000136 return NULL == saveLayerPaint ||
137 NULL == dbmrPaint ||
robertphillips@google.com1780a3c2013-02-13 13:27:44 +0000138 (is_simple(*saveLayerPaint) && dbmrPaint->getColor() == layerColor);
robertphillips@google.com73743552013-02-05 20:51:49 +0000139}
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
robertphillips@google.com3b0a9fe2013-01-31 15:56:22 +0000179static int filter_picture(const SkString& inFile, const SkString& outFile) {
djsollen@google.coma09e8832012-11-13 18:50:33 +0000180 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
robertphillips@google.com73743552013-02-05 20:51:49 +0000192 int localCount[SK_ARRAY_COUNT(gOptTable)];
193
194 memset(localCount, 0, sizeof(localCount));
195
robertphillips@google.com3b0a9fe2013-01-31 15:56:22 +0000196 SkDebugCanvas debugCanvas(inPicture->width(), inPicture->height());
197 debugCanvas.setBounds(inPicture->width(), inPicture->height());
198 inPicture->draw(&debugCanvas);
djsollen@google.coma09e8832012-11-13 18:50:33 +0000199
robertphillips@google.com3b0a9fe2013-01-31 15:56:22 +0000200 const SkTDArray<SkDrawCommand*>& commands = debugCanvas.getDrawCommands();
djsollen@google.coma09e8832012-11-13 18:50:33 +0000201
skia.committer@gmail.comae683922013-02-06 07:01:54 +0000202 // hide the initial save and restore since replaying the commands will
robertphillips@google.com73743552013-02-05 20:51:49 +0000203 // re-add them
204 if (commands.count() > 0) {
205 commands[0]->setVisible(false);
206 commands[commands.count()-1]->setVisible(false);
207 }
208
robertphillips@google.com3b0a9fe2013-01-31 15:56:22 +0000209 for (int i = 0; i < commands.count(); ++i) {
robertphillips@google.com73743552013-02-05 20:51:49 +0000210 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];
robertphillips@google.com3b0a9fe2013-01-31 15:56:22 +0000215 }
216 }
217 }
djsollen@google.coma09e8832012-11-13 18:50:33 +0000218
219 if (!outFile.isEmpty()) {
robertphillips@google.com3b0a9fe2013-01-31 15:56:22 +0000220 SkPicture outPicture;
221
222 SkCanvas* canvas = outPicture.beginRecording(inPicture->width(), inPicture->height());
223 debugCanvas.draw(canvas);
224 outPicture.endRecording();
225
djsollen@google.coma09e8832012-11-13 18:50:33 +0000226 SkFILEWStream outStream(outFile.c_str());
227
228 outPicture.serialize(&outStream);
229 }
230
robertphillips@google.com73743552013-02-05 20:51:49 +0000231 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
djsollen@google.coma09e8832012-11-13 18:50:33 +0000245 return 0;
246}
247
tfarina@chromium.orga5b7cc02012-10-08 14:41:10 +0000248// This function is not marked as 'static' so it can be referenced externally
249// in the iOS build.
humper@google.com05af1af2013-01-07 16:47:43 +0000250int tool_main(int argc, char** argv); // suppress a warning on mac
251
caryclark@google.com9598f422012-10-09 12:32:37 +0000252int tool_main(int argc, char** argv) {
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000253 SkGraphics::Init();
254
robertphillips@google.com801cee12012-10-19 19:06:11 +0000255 if (argc < 3) {
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000256 usage();
257 return -1;
258 }
259
robertphillips@google.com3b0a9fe2013-01-31 15:56:22 +0000260 SkString inFile, outFile, inDir, outDir;
robertphillips@google.com801cee12012-10-19 19:06:11 +0000261
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000262 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 {
robertphillips@google.com801cee12012-10-19 19:06:11 +0000269 SkDebugf("missing arg for -i\n");
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000270 usage();
271 return -1;
272 }
djsollen@google.coma09e8832012-11-13 18:50:33 +0000273 } 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 }
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000291 } else if (strcmp(*argv, "-o") == 0) {
292 argv++;
293 if (argv < stop && **argv) {
294 outFile.set(*argv);
295 } else {
robertphillips@google.com801cee12012-10-19 19:06:11 +0000296 SkDebugf("missing arg for -o\n");
297 usage();
298 return -1;
299 }
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000300 } 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
djsollen@google.coma09e8832012-11-13 18:50:33 +0000310 SkOSFile::Iter iter(inDir.c_str(), "skp");
humper@google.com05af1af2013-01-07 16:47:43 +0000311
djsollen@google.coma09e8832012-11-13 18:50:33 +0000312 SkString inputFilename, outputFilename;
313 if (iter.next(&inputFilename)) {
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000314
djsollen@google.coma09e8832012-11-13 18:50:33 +0000315 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());
robertphillips@google.com3b0a9fe2013-01-31 15:56:22 +0000321 filter_picture(inFile, outFile);
djsollen@google.coma09e8832012-11-13 18:50:33 +0000322 } while(iter.next(&inputFilename));
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000323
djsollen@google.coma09e8832012-11-13 18:50:33 +0000324 } else if (!inFile.isEmpty()) {
robertphillips@google.com3b0a9fe2013-01-31 15:56:22 +0000325 filter_picture(inFile, outFile);
djsollen@google.coma09e8832012-11-13 18:50:33 +0000326 } else {
327 usage();
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000328 return -1;
329 }
330
robertphillips@google.com73743552013-02-05 20:51:49 +0000331 for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) {
332 SkDebugf("opt %d: %d\n", opt, gOptTable[opt].fNumTimesApplied);
333 }
334
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000335 SkGraphics::Term();
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000336 return 0;
337}
338
339#if !defined SK_BUILD_FOR_IOS
340int main(int argc, char * const argv[]) {
caryclark@google.com9598f422012-10-09 12:32:37 +0000341 return tool_main(argc, (char**) argv);
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000342}
343#endif