blob: 28020cd02afaf2545d078c08cfb9f40b89210f8f [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.com801cee12012-10-19 19:06:11 +00008#include "SkDevice.h"
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +00009#include "SkGraphics.h"
robertphillips@google.com4e4d75b2012-11-12 18:03:19 +000010#include "SkImageDecoder.h"
robertphillips@google.com801cee12012-10-19 19:06:11 +000011#include "SkImageEncoder.h"
djsollen@google.coma09e8832012-11-13 18:50:33 +000012#include "SkOSFile.h"
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +000013#include "SkPicture.h"
robertphillips@google.com801cee12012-10-19 19:06:11 +000014#include "SkPicturePlayback.h"
15#include "SkPictureRecord.h"
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +000016#include "SkStream.h"
djsollen@google.coma09e8832012-11-13 18:50:33 +000017#include "picture_utils.h"
robertphillips@google.comd3d377f2012-12-07 20:56:13 +000018#include "path_utils.h"
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +000019
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +000020static void usage() {
robertphillips@google.comd3d377f2012-12-07 20:56:13 +000021 SkDebugf("Usage: filter -i inFile [-o outFile] [--input-dir path] [--output-dir path]\n");
22 SkDebugf(" [-p pathFile] [-t textureDir] [-h|--help]\n\n");
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +000023 SkDebugf(" -i inFile : file to file.\n");
24 SkDebugf(" -o outFile : result of filtering.\n");
djsollen@google.coma09e8832012-11-13 18:50:33 +000025 SkDebugf(" --input-dir : process all files in dir with .skp extension.\n");
26 SkDebugf(" --output-dir : results of filtering the input dir.\n");
robertphillips@google.comd3d377f2012-12-07 20:56:13 +000027 SkDebugf(" -p pathFile : file in which to place compileable path data.\n");
djsollen@google.coma09e8832012-11-13 18:50:33 +000028 SkDebugf(" -t textureDir : directory in which to place textures. (only available w/ single file)\n");
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +000029 SkDebugf(" -h|--help : Show this help message.\n");
30}
31
robertphillips@google.com801cee12012-10-19 19:06:11 +000032// SkFilterRecord allows the filter to manipulate the read in SkPicture
33class SkFilterRecord : public SkPictureRecord {
34public:
robertphillips@google.comd3d377f2012-12-07 20:56:13 +000035 SkFilterRecord(uint32_t recordFlags, SkDevice* device, SkFILEWStream* pathStream)
robertphillips@google.com63f11272012-10-24 19:30:41 +000036 : INHERITED(recordFlags, device)
37 , fTransSkipped(0)
38 , fTransTot(0)
39 , fScalesSkipped(0)
robertphillips@google.comd3d377f2012-12-07 20:56:13 +000040 , fScalesTot(0)
41 , fPathStream(pathStream) {
42 }
43
44 virtual ~SkFilterRecord() {
45 }
46
47 virtual bool clipPath(const SkPath& path, SkRegion::Op op, bool doAntiAlias) SK_OVERRIDE {
48 if (!path.isRect(NULL) && 4 < path.countPoints()) {
49 sk_tools::dump_path(fPathStream, path);
50 }
51 return INHERITED::clipPath(path, op, doAntiAlias);
52 }
53
54 virtual void drawPath(const SkPath& path, const SkPaint& p) SK_OVERRIDE {
55 if (!path.isRect(NULL) && 4 < path.countPoints()) {
56 sk_tools::dump_path(fPathStream, path);
57 }
58 INHERITED::drawPath(path, p);
robertphillips@google.com63f11272012-10-24 19:30:41 +000059 }
60
61 virtual bool translate(SkScalar dx, SkScalar dy) SK_OVERRIDE {
62 ++fTransTot;
63
robertphillips@google.com4e4d75b2012-11-12 18:03:19 +000064#if 0
robertphillips@google.com63f11272012-10-24 19:30:41 +000065 if (0 == dx && 0 == dy) {
66 ++fTransSkipped;
67 return true;
68 }
robertphillips@google.com4e4d75b2012-11-12 18:03:19 +000069#endif
robertphillips@google.com63f11272012-10-24 19:30:41 +000070
71 return INHERITED::translate(dx, dy);
72 }
73
74 virtual bool scale(SkScalar sx, SkScalar sy) SK_OVERRIDE {
75 ++fScalesTot;
76
robertphillips@google.com4e4d75b2012-11-12 18:03:19 +000077#if 0
robertphillips@google.com63f11272012-10-24 19:30:41 +000078 if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
79 ++fScalesSkipped;
80 return true;
81 }
robertphillips@google.com4e4d75b2012-11-12 18:03:19 +000082#endif
robertphillips@google.com63f11272012-10-24 19:30:41 +000083
84 return INHERITED::scale(sx, sy);
robertphillips@google.com801cee12012-10-19 19:06:11 +000085 }
86
87 void saveImages(const SkString& path) {
88 SkTRefArray<SkBitmap>* bitmaps = fBitmapHeap->extractBitmaps();
89
90 if (NULL != bitmaps) {
91 for (int i = 0; i < bitmaps->count(); ++i) {
92 SkString filename(path);
93 if (!path.endsWith("\\")) {
94 filename.append("\\");
95 }
96 filename.append("image");
97 filename.appendS32(i);
98 filename.append(".png");
99
100 SkImageEncoder::EncodeFile(filename.c_str(), (*bitmaps)[i],
101 SkImageEncoder::kPNG_Type, 0);
102 }
103 }
104
105 bitmaps->unref();
106 }
107
robertphillips@google.com63f11272012-10-24 19:30:41 +0000108 void report() {
109 SkDebugf("%d Trans skipped (out of %d)\n", fTransSkipped, fTransTot);
110 SkDebugf("%d Scales skipped (out of %d)\n", fScalesSkipped, fScalesTot);
111 }
112
113protected:
114 int fTransSkipped;
115 int fTransTot;
116
117 int fScalesSkipped;
118 int fScalesTot;
119
robertphillips@google.comd3d377f2012-12-07 20:56:13 +0000120 SkFILEWStream* fPathStream;
robertphillips@google.com801cee12012-10-19 19:06:11 +0000121private:
122 typedef SkPictureRecord INHERITED;
123};
124
robertphillips@google.com63f11272012-10-24 19:30:41 +0000125// Wrap SkPicture to allow installation of a SkFilterRecord object
126class SkFilterPicture : public SkPicture {
127public:
robertphillips@google.com831c7262012-10-25 14:45:08 +0000128 SkFilterPicture(int width, int height, SkPictureRecord* record) {
129 fWidth = width;
130 fHeight = height;
robertphillips@google.com63f11272012-10-24 19:30:41 +0000131 fRecord = record;
132 SkSafeRef(fRecord);
133 }
134
135private:
136 typedef SkPicture INHERITED;
137};
138
robertphillips@google.com4e4d75b2012-11-12 18:03:19 +0000139static bool PNGEncodeBitmapToStream(SkWStream* stream, const SkBitmap& bitmap) {
140 return SkImageEncoder::EncodeStream(stream, bitmap, SkImageEncoder::kPNG_Type, 100);
141}
robertphillips@google.com63f11272012-10-24 19:30:41 +0000142
robertphillips@google.comd3d377f2012-12-07 20:56:13 +0000143int filter_picture(const SkString& inFile, const SkString& outFile,
144 const SkString& textureDir, SkFILEWStream *pathStream) {
djsollen@google.coma09e8832012-11-13 18:50:33 +0000145 SkPicture* inPicture = NULL;
146
147 SkFILEStream inStream(inFile.c_str());
148 if (inStream.isValid()) {
149 inPicture = SkNEW_ARGS(SkPicture, (&inStream, NULL, &SkImageDecoder::DecodeStream));
150 }
151
152 if (NULL == inPicture) {
153 SkDebugf("Could not read file %s\n", inFile.c_str());
154 return -1;
155 }
156
157 SkBitmap bm;
158 bm.setConfig(SkBitmap::kNo_Config, inPicture->width(), inPicture->height());
159 SkAutoTUnref<SkDevice> dev(SkNEW_ARGS(SkDevice, (bm)));
160
robertphillips@google.comd3d377f2012-12-07 20:56:13 +0000161 SkAutoTUnref<SkFilterRecord> filterRecord(SkNEW_ARGS(SkFilterRecord, (0, dev, pathStream)));
djsollen@google.coma09e8832012-11-13 18:50:33 +0000162
163 // Playback the read in picture to the SkFilterRecorder to allow filtering
164 filterRecord->beginRecording();
165 inPicture->draw(filterRecord);
166 filterRecord->endRecording();
167
168 filterRecord->report();
169
170 if (!outFile.isEmpty()) {
171 SkFilterPicture outPicture(inPicture->width(), inPicture->height(), filterRecord);
172 SkFILEWStream outStream(outFile.c_str());
173
174 outPicture.serialize(&outStream);
175 }
176
177 if (!textureDir.isEmpty()) {
178 filterRecord->saveImages(textureDir);
179 }
180
181 return 0;
182}
183
tfarina@chromium.orga5b7cc02012-10-08 14:41:10 +0000184// This function is not marked as 'static' so it can be referenced externally
185// in the iOS build.
caryclark@google.com9598f422012-10-09 12:32:37 +0000186int tool_main(int argc, char** argv) {
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000187 SkGraphics::Init();
188
robertphillips@google.com801cee12012-10-19 19:06:11 +0000189 if (argc < 3) {
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000190 usage();
191 return -1;
192 }
193
robertphillips@google.comd3d377f2012-12-07 20:56:13 +0000194 SkString inFile, outFile, inDir, outDir, textureDir, pathFile;
robertphillips@google.com801cee12012-10-19 19:06:11 +0000195
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000196 char* const* stop = argv + argc;
197 for (++argv; argv < stop; ++argv) {
198 if (strcmp(*argv, "-i") == 0) {
199 argv++;
200 if (argv < stop && **argv) {
201 inFile.set(*argv);
202 } else {
robertphillips@google.com801cee12012-10-19 19:06:11 +0000203 SkDebugf("missing arg for -i\n");
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000204 usage();
205 return -1;
206 }
djsollen@google.coma09e8832012-11-13 18:50:33 +0000207 } else if (strcmp(*argv, "--input-dir") == 0) {
208 argv++;
209 if (argv < stop && **argv) {
210 inDir.set(*argv);
211 } else {
212 SkDebugf("missing arg for --input-dir\n");
213 usage();
214 return -1;
215 }
216 } else if (strcmp(*argv, "--output-dir") == 0) {
217 argv++;
218 if (argv < stop && **argv) {
219 outDir.set(*argv);
220 } else {
221 SkDebugf("missing arg for --output-dir\n");
222 usage();
223 return -1;
224 }
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000225 } else if (strcmp(*argv, "-o") == 0) {
226 argv++;
227 if (argv < stop && **argv) {
228 outFile.set(*argv);
229 } else {
robertphillips@google.com801cee12012-10-19 19:06:11 +0000230 SkDebugf("missing arg for -o\n");
231 usage();
232 return -1;
233 }
robertphillips@google.comd3d377f2012-12-07 20:56:13 +0000234 } else if (strcmp(*argv, "-p") == 0) {
235 argv++;
236 if (argv < stop && **argv) {
237 pathFile.set(*argv);
238 } else {
239 SkDebugf("missing arg for -p\n");
240 usage();
241 return -1;
242 }
robertphillips@google.com801cee12012-10-19 19:06:11 +0000243 } else if (strcmp(*argv, "-t") == 0) {
244 argv++;
245 if (argv < stop && **argv) {
246 textureDir.set(*argv);
247 } else {
248 SkDebugf("missing arg for -t\n");
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000249 usage();
250 return -1;
251 }
252 } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) {
253 usage();
254 return 0;
255 } else {
256 SkDebugf("unknown arg %s\n", *argv);
257 usage();
258 return -1;
259 }
260 }
261
djsollen@google.coma09e8832012-11-13 18:50:33 +0000262 if(!inDir.isEmpty() && !textureDir.isEmpty()) {
263 SkDebugf("ERROR: The textureDir option is not permitted when passing an input directory.\n");
robertphillips@google.com801cee12012-10-19 19:06:11 +0000264 usage();
265 return -1;
266 }
267
robertphillips@google.comd3d377f2012-12-07 20:56:13 +0000268 SkFILEWStream *pathStream = NULL;
269
270 if (!pathFile.isEmpty()) {
271 pathStream = new SkFILEWStream(pathFile.c_str());
272 if (!pathStream->isValid()) {
273 SkDebugf("Could open path file %s\n", pathFile.c_str());
274 delete pathStream;
275 return -1;
276 }
277
278 sk_tools::dump_path_prefix(pathStream);
279 }
280
djsollen@google.coma09e8832012-11-13 18:50:33 +0000281 SkOSFile::Iter iter(inDir.c_str(), "skp");
282 int failures = 0;
283 SkString inputFilename, outputFilename;
284 if (iter.next(&inputFilename)) {
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000285
djsollen@google.coma09e8832012-11-13 18:50:33 +0000286 do {
287 sk_tools::make_filepath(&inFile, inDir, inputFilename);
288 if (!outDir.isEmpty()) {
289 sk_tools::make_filepath(&outFile, outDir, inputFilename);
290 }
291 SkDebugf("Executing %s\n", inputFilename.c_str());
robertphillips@google.comd3d377f2012-12-07 20:56:13 +0000292 filter_picture(inFile, outFile, textureDir, pathStream);
djsollen@google.coma09e8832012-11-13 18:50:33 +0000293 } while(iter.next(&inputFilename));
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000294
djsollen@google.coma09e8832012-11-13 18:50:33 +0000295 } else if (!inFile.isEmpty()) {
robertphillips@google.comd3d377f2012-12-07 20:56:13 +0000296 filter_picture(inFile, outFile, textureDir, pathStream);
djsollen@google.coma09e8832012-11-13 18:50:33 +0000297 } else {
298 usage();
robertphillips@google.comd3d377f2012-12-07 20:56:13 +0000299 if (NULL != pathStream) {
300 delete pathStream;
301 pathStream = NULL;
302 }
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000303 return -1;
304 }
305
robertphillips@google.comd3d377f2012-12-07 20:56:13 +0000306 if (NULL != pathStream) {
307 sk_tools::dump_path_suffix(pathStream);
308 delete pathStream;
309 pathStream = NULL;
310 }
311
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000312 SkGraphics::Term();
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000313 return 0;
314}
315
316#if !defined SK_BUILD_FOR_IOS
317int main(int argc, char * const argv[]) {
caryclark@google.com9598f422012-10-09 12:32:37 +0000318 return tool_main(argc, (char**) argv);
robertphillips@google.comc7e4a5a2012-10-04 13:00:33 +0000319}
320#endif