filtermain.cpp revision 363e546ed626b6dbbc42f5db87b3594bc0b5944b
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 "SkDevice.h"
9#include "SkGraphics.h"
10#include "SkImageDecoder.h"
11#include "SkImageEncoder.h"
12#include "SkOSFile.h"
13#include "SkPicture.h"
14#include "SkPicturePlayback.h"
15#include "SkPictureRecord.h"
16#include "SkStream.h"
17#include "picture_utils.h"
18#include "path_utils.h"
19
20static void usage() {
21    SkDebugf("Usage: filter -i inFile [-o outFile] [--input-dir path] [--output-dir path]\n");
22    SkDebugf("                        [-p pathFile] [-t textureDir] [-h|--help]\n\n");
23    SkDebugf("    -i inFile  : file to file.\n");
24    SkDebugf("    -o outFile : result of filtering.\n");
25    SkDebugf("    --input-dir : process all files in dir with .skp extension.\n");
26    SkDebugf("    --output-dir : results of filtering the input dir.\n");
27    SkDebugf("    -p pathFile : file in which to place compileable path data.\n");
28    SkDebugf("    -t textureDir : directory in which to place textures. (only available w/ single file)\n");
29    SkDebugf("    -h|--help  : Show this help message.\n");
30}
31
32// SkFilterRecord allows the filter to manipulate the read in SkPicture
33class SkFilterRecord : public SkPictureRecord {
34public:
35    SkFilterRecord(uint32_t recordFlags, SkDevice* device, SkFILEWStream* pathStream)
36        : INHERITED(recordFlags, device)
37        , fTransSkipped(0)
38        , fTransTot(0)
39        , fScalesSkipped(0)
40        , 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);
59    }
60
61    virtual bool translate(SkScalar dx, SkScalar dy) SK_OVERRIDE {
62        ++fTransTot;
63
64#if 0
65        if (0 == dx && 0 == dy) {
66            ++fTransSkipped;
67            return true;
68        }
69#endif
70
71        return INHERITED::translate(dx, dy);
72    }
73
74    virtual bool scale(SkScalar sx, SkScalar sy) SK_OVERRIDE {
75        ++fScalesTot;
76
77#if 0
78        if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
79            ++fScalesSkipped;
80            return true;
81        }
82#endif
83
84        return INHERITED::scale(sx, sy);
85    }
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
108    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
120    SkFILEWStream* fPathStream;
121private:
122    typedef SkPictureRecord INHERITED;
123};
124
125// Wrap SkPicture to allow installation of a SkFilterRecord object
126class SkFilterPicture : public SkPicture {
127public:
128    SkFilterPicture(int width, int height, SkPictureRecord* record) {
129        fWidth = width;
130        fHeight = height;
131        fRecord = record;
132        SkSafeRef(fRecord);
133    }
134
135private:
136    typedef SkPicture INHERITED;
137};
138
139static bool PNGEncodeBitmapToStream(SkWStream* stream, const SkBitmap& bitmap) {
140    return SkImageEncoder::EncodeStream(stream, bitmap, SkImageEncoder::kPNG_Type, 100);
141}
142
143int filter_picture(const SkString& inFile, const SkString& outFile,
144                   const SkString& textureDir, SkFILEWStream *pathStream) {
145    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
161    SkAutoTUnref<SkFilterRecord> filterRecord(SkNEW_ARGS(SkFilterRecord, (0, dev, pathStream)));
162
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
184// This function is not marked as 'static' so it can be referenced externally
185// in the iOS build.
186int tool_main(int argc, char** argv) {
187    SkGraphics::Init();
188
189    if (argc < 3) {
190        usage();
191        return -1;
192    }
193
194    SkString inFile, outFile, inDir, outDir, textureDir, pathFile;
195
196    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 {
203                SkDebugf("missing arg for -i\n");
204                usage();
205                return -1;
206            }
207        } 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            }
225        } else if (strcmp(*argv, "-o") == 0) {
226            argv++;
227            if (argv < stop && **argv) {
228                outFile.set(*argv);
229            } else {
230                SkDebugf("missing arg for -o\n");
231                usage();
232                return -1;
233            }
234        } 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            }
243        } 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");
249                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
262    if(!inDir.isEmpty() && !textureDir.isEmpty()) {
263        SkDebugf("ERROR: The textureDir option is not permitted when passing an input directory.\n");
264        usage();
265        return -1;
266    }
267
268    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
281    SkOSFile::Iter iter(inDir.c_str(), "skp");
282    int failures = 0;
283    SkString inputFilename, outputFilename;
284    if (iter.next(&inputFilename)) {
285
286        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());
292            filter_picture(inFile, outFile, textureDir, pathStream);
293        } while(iter.next(&inputFilename));
294
295    } else if (!inFile.isEmpty()) {
296        filter_picture(inFile, outFile, textureDir, pathStream);
297    } else {
298        usage();
299        if (NULL != pathStream) {
300            delete pathStream;
301            pathStream = NULL;
302        }
303        return -1;
304    }
305
306    if (NULL != pathStream) {
307        sk_tools::dump_path_suffix(pathStream);
308        delete pathStream;
309        pathStream = NULL;
310    }
311
312    SkGraphics::Term();
313    return 0;
314}
315
316#if !defined SK_BUILD_FOR_IOS
317int main(int argc, char * const argv[]) {
318    return tool_main(argc, (char**) argv);
319}
320#endif
321