blob: c56edbd6040f100c8c26a7df87ae9506c246194d [file] [log] [blame]
reed@android.com00dae862009-06-10 15:38:48 +00001#include "gm.h"
reed@android.comb9b9a182009-07-08 02:54:47 +00002#include "SkColorPriv.h"
reed@android.com8015dd82009-06-21 00:49:18 +00003#include "SkGraphics.h"
4#include "SkImageDecoder.h"
5#include "SkImageEncoder.h"
reed@google.com07700442010-12-20 19:46:07 +00006#include "SkStream.h"
7#include "SkRefCnt.h"
8
9#ifdef SK_SUPPORT_PDF
10 #include "SkPDFDevice.h"
11 #include "SkPDFDocument.h"
12#endif
reed@android.com00dae862009-06-10 15:38:48 +000013
14using namespace skiagm;
15
16// need to explicitly declare this, or we get some weird infinite loop llist
17template GMRegistry* GMRegistry::gHead;
18
19class Iter {
20public:
21 Iter() {
reed@android.comdd0ac282009-06-20 02:38:16 +000022 fReg = GMRegistry::Head();
reed@android.com00dae862009-06-10 15:38:48 +000023 }
24
reed@android.comdd0ac282009-06-20 02:38:16 +000025 GM* next() {
reed@android.com00dae862009-06-10 15:38:48 +000026 if (fReg) {
reed@android.comdd0ac282009-06-20 02:38:16 +000027 GMRegistry::Factory fact = fReg->factory();
reed@android.com00dae862009-06-10 15:38:48 +000028 fReg = fReg->next();
reed@android.comdd0ac282009-06-20 02:38:16 +000029 return fact(0);
reed@android.com00dae862009-06-10 15:38:48 +000030 }
31 return NULL;
32 }
33
34 static int Count() {
reed@android.comdd0ac282009-06-20 02:38:16 +000035 const GMRegistry* reg = GMRegistry::Head();
reed@android.com00dae862009-06-10 15:38:48 +000036 int count = 0;
37 while (reg) {
38 count += 1;
39 reg = reg->next();
40 }
41 return count;
42 }
43
44private:
45 const GMRegistry* fReg;
46};
47
reed@android.com8015dd82009-06-21 00:49:18 +000048static SkString make_name(const char shortName[], const char configName[]) {
49 SkString name(shortName);
50 name.appendf("_%s", configName);
51 return name;
52}
53
reed@google.com07700442010-12-20 19:46:07 +000054static SkString make_filename(const char path[], const SkString& name, const char suffix[]) {
reed@android.com8015dd82009-06-21 00:49:18 +000055 SkString filename(path);
56 if (filename.size() && filename[filename.size() - 1] != '/') {
57 filename.append("/");
reed@android.com00dae862009-06-10 15:38:48 +000058 }
reed@google.com07700442010-12-20 19:46:07 +000059 filename.appendf("%s.%s", name.c_str(), suffix);
reed@android.com8015dd82009-06-21 00:49:18 +000060 return filename;
61}
62
reed@android.comb9b9a182009-07-08 02:54:47 +000063/* since PNG insists on unpremultiplying our alpha, we take no precision chances
64 and force all pixels to be 100% opaque, otherwise on compare we may not get
65 a perfect match.
66 */
67static void force_all_opaque(const SkBitmap& bitmap) {
68 SkAutoLockPixels lock(bitmap);
69 for (int y = 0; y < bitmap.height(); y++) {
70 for (int x = 0; x < bitmap.width(); x++) {
71 *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT);
72 }
73 }
74}
75
76static bool write_bitmap(const SkString& path, const SkBitmap& bitmap) {
77 SkBitmap copy;
78 bitmap.copyTo(&copy, SkBitmap::kARGB_8888_Config);
79 force_all_opaque(copy);
80 return SkImageEncoder::EncodeFile(path.c_str(), copy,
81 SkImageEncoder::kPNG_Type, 100);
82}
83
reed@google.com3d3f0922010-12-20 21:10:29 +000084static inline SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1) {
85 int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
86 int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
87 int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
88 return SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db));
89}
90
91static void compute_diff(const SkBitmap& target, const SkBitmap& base,
92 SkBitmap* diff) {
93 SkAutoLockPixels alp(*diff);
94
95 const int w = target.width();
96 const int h = target.height();
97 for (int y = 0; y < h; y++) {
98 for (int x = 0; x < w; x++) {
99 SkPMColor c0 = *base.getAddr32(x, y);
100 SkPMColor c1 = *target.getAddr32(x, y);
101 SkPMColor d = 0;
102 if (c0 != c1) {
103 d = compute_diff_pmcolor(c0, c1);
104 }
105 *diff->getAddr32(x, y) = d;
106 }
107 }
108}
109
110static bool compare(const SkBitmap& target, const SkBitmap& base,
111 const SkString& name, SkBitmap* diff) {
reed@android.comb9b9a182009-07-08 02:54:47 +0000112 SkBitmap copy;
113 const SkBitmap* bm = &target;
114 if (target.config() != SkBitmap::kARGB_8888_Config) {
115 target.copyTo(&copy, SkBitmap::kARGB_8888_Config);
116 bm = &copy;
117 }
118
119 force_all_opaque(*bm);
120
121 const int w = bm->width();
122 const int h = bm->height();
123 if (w != base.width() || h != base.height()) {
124 SkDebugf("---- dimensions mismatch for %s base [%d %d] current [%d %d]\n",
125 name.c_str(), base.width(), base.height(), w, h);
reed@google.com3d3f0922010-12-20 21:10:29 +0000126 return false;
reed@android.comb9b9a182009-07-08 02:54:47 +0000127 }
128
129 SkAutoLockPixels bmLock(*bm);
130 SkAutoLockPixels baseLock(base);
131
132 for (int y = 0; y < h; y++) {
133 for (int x = 0; x < w; x++) {
134 SkPMColor c0 = *base.getAddr32(x, y);
135 SkPMColor c1 = *bm->getAddr32(x, y);
136 if (c0 != c1) {
137 SkDebugf("----- pixel mismatch for %s at [%d %d] base 0x%08X current 0x%08X\n",
138 name.c_str(), x, y, c0, c1);
reed@google.com3d3f0922010-12-20 21:10:29 +0000139
140 if (diff) {
141 diff->setConfig(SkBitmap::kARGB_8888_Config, w, h);
142 diff->allocPixels();
143 compute_diff(*bm, base, diff);
144 }
145 return false;
reed@android.comb9b9a182009-07-08 02:54:47 +0000146 }
147 }
148 }
reed@google.com3d3f0922010-12-20 21:10:29 +0000149
150 // they're equal
151 return true;
reed@android.com8015dd82009-06-21 00:49:18 +0000152}
reed@android.com00dae862009-06-10 15:38:48 +0000153
reed@google.com07700442010-12-20 19:46:07 +0000154static void write_pdf(GM* gm, const char writePath[]) {
155#ifdef SK_SUPPORT_PDF
156 SkISize size = gm->getISize();
157 SkPDFDevice* dev = new SkPDFDevice(size.width(), size.height());
158 SkAutoUnref aur(dev);
159
160 {
161 SkCanvas c(dev);
162 gm->draw(&c);
163 }
164
165 SkDynamicMemoryWStream output;
166 SkPDFDocument doc;
167 doc.appendPage(dev);
168 doc.emitPDF(&output);
169
170 SkString shortName(gm->shortName());
171 SkString path = make_filename(writePath, shortName, "pdf");
172 SkFILEWStream stream(path.c_str());
173 stream.write(output.getStream(), output.getOffset());
174#endif
175}
176
reed@android.com00dae862009-06-10 15:38:48 +0000177static const struct {
178 SkBitmap::Config fConfig;
179 bool fUsePicture;
180 const char* fName;
181} gRec[] = {
182 { SkBitmap::kARGB_8888_Config, false, "8888" },
183 { SkBitmap::kARGB_4444_Config, false, "4444" },
184 { SkBitmap::kRGB_565_Config, false, "565" },
reed@android.com00dae862009-06-10 15:38:48 +0000185};
186
187int main (int argc, char * const argv[]) {
188 SkAutoGraphics ag;
189
reed@android.com8015dd82009-06-21 00:49:18 +0000190 const char* writePath = NULL; // if non-null, where we write the originals
191 const char* readPath = NULL; // if non-null, were we read from to compare
reed@google.com3d3f0922010-12-20 21:10:29 +0000192 const char* diffPath = NULL; // if non-null, where we write our diffs (from compare)
reed@android.com8015dd82009-06-21 00:49:18 +0000193
194 char* const* stop = argv + argc;
195 for (++argv; argv < stop; ++argv) {
196 if (strcmp(*argv, "-w") == 0) {
197 argv++;
198 if (argv < stop && **argv) {
199 writePath = *argv;
200 }
201 } else if (strcmp(*argv, "-r") == 0) {
202 argv++;
203 if (argv < stop && **argv) {
204 readPath = *argv;
205 }
reed@google.com3d3f0922010-12-20 21:10:29 +0000206 } else if (strcmp(*argv, "-d") == 0) {
207 argv++;
208 if (argv < stop && **argv) {
209 diffPath = *argv;
210 }
211 }
212 }
reed@android.com8015dd82009-06-21 00:49:18 +0000213
reed@android.com00dae862009-06-10 15:38:48 +0000214 Iter iter;
215 GM* gm;
reed@android.com00f883e2010-12-14 17:46:14 +0000216
217 if (readPath) {
218 fprintf(stderr, "reading from %s\n", readPath);
219 } else if (writePath) {
220 fprintf(stderr, "writing to %s\n", writePath);
221 }
222
reed@android.com00dae862009-06-10 15:38:48 +0000223 while ((gm = iter.next()) != NULL) {
224 SkISize size = gm->getISize();
reed@google.com3d3f0922010-12-20 21:10:29 +0000225 SkDebugf("drawing... %s [%d %d]\n", gm->shortName(),
reed@android.com8015dd82009-06-21 00:49:18 +0000226 size.width(), size.height());
227
reed@android.com00dae862009-06-10 15:38:48 +0000228 SkBitmap bitmap;
reed@android.comdd0ac282009-06-20 02:38:16 +0000229 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
reed@android.com00dae862009-06-10 15:38:48 +0000230 bitmap.setConfig(gRec[i].fConfig, size.width(), size.height());
231 bitmap.allocPixels();
232 bitmap.eraseColor(0);
233 SkCanvas canvas(bitmap);
234
235 gm->draw(&canvas);
reed@google.com07700442010-12-20 19:46:07 +0000236
reed@android.com8015dd82009-06-21 00:49:18 +0000237 SkString name = make_name(gm->shortName(), gRec[i].fName);
238
239 if (writePath) {
reed@google.com07700442010-12-20 19:46:07 +0000240 SkString path = make_filename(writePath, name, "png");
reed@android.comb9b9a182009-07-08 02:54:47 +0000241 bool success = write_bitmap(path, bitmap);
reed@android.com8015dd82009-06-21 00:49:18 +0000242 if (!success) {
243 fprintf(stderr, "FAILED to write %s\n", path.c_str());
244 }
reed@google.com07700442010-12-20 19:46:07 +0000245 write_pdf(gm, writePath);
reed@android.com8015dd82009-06-21 00:49:18 +0000246 } else if (readPath) {
reed@google.com07700442010-12-20 19:46:07 +0000247 SkString path = make_filename(readPath, name, "png");
reed@android.com8015dd82009-06-21 00:49:18 +0000248 SkBitmap orig;
249 bool success = SkImageDecoder::DecodeFile(path.c_str(), &orig,
250 SkBitmap::kARGB_8888_Config,
251 SkImageDecoder::kDecodePixels_Mode, NULL);
252 if (success) {
reed@google.com3d3f0922010-12-20 21:10:29 +0000253 SkBitmap diffBitmap;
254 success = compare(bitmap, orig, name, diffPath ? &diffBitmap : NULL);
255 if (!success && diffPath) {
256 SkString diffName = make_filename(diffPath, name, ".diff.png");
257 fprintf(stderr, "Writing %s\n", diffName.c_str());
258 write_bitmap(diffName, diffBitmap);
259 }
reed@android.com8015dd82009-06-21 00:49:18 +0000260 } else {
261 fprintf(stderr, "FAILED to read %s\n", path.c_str());
262 }
263 }
reed@android.com00dae862009-06-10 15:38:48 +0000264 }
265 SkDELETE(gm);
266 }
267 return 0;
268}
reed@android.comdd0ac282009-06-20 02:38:16 +0000269
270///////////////////////////////////////////////////////////////////////////////
271
272using namespace skiagm;
273
274GM::GM() {}
275GM::~GM() {}
276
277void GM::draw(SkCanvas* canvas) {
278 this->onDraw(canvas);
279}
280
281