blob: 5c9dcab5366c9b49759d5b277fa6eda37853bc39 [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
reed@google.com873cb1e2010-12-23 15:00:45 +00009#include "GrContext.h"
10#include "SkGpuCanvas.h"
11#include "SkEGLContext.h"
12#include "SkDevice.h"
13
reed@google.com07700442010-12-20 19:46:07 +000014#ifdef SK_SUPPORT_PDF
15 #include "SkPDFDevice.h"
16 #include "SkPDFDocument.h"
17#endif
reed@android.com00dae862009-06-10 15:38:48 +000018
19using namespace skiagm;
20
21// need to explicitly declare this, or we get some weird infinite loop llist
22template GMRegistry* GMRegistry::gHead;
23
24class Iter {
25public:
26 Iter() {
reed@android.comdd0ac282009-06-20 02:38:16 +000027 fReg = GMRegistry::Head();
reed@android.com00dae862009-06-10 15:38:48 +000028 }
29
reed@android.comdd0ac282009-06-20 02:38:16 +000030 GM* next() {
reed@android.com00dae862009-06-10 15:38:48 +000031 if (fReg) {
reed@android.comdd0ac282009-06-20 02:38:16 +000032 GMRegistry::Factory fact = fReg->factory();
reed@android.com00dae862009-06-10 15:38:48 +000033 fReg = fReg->next();
reed@android.comdd0ac282009-06-20 02:38:16 +000034 return fact(0);
reed@android.com00dae862009-06-10 15:38:48 +000035 }
36 return NULL;
37 }
38
39 static int Count() {
reed@android.comdd0ac282009-06-20 02:38:16 +000040 const GMRegistry* reg = GMRegistry::Head();
reed@android.com00dae862009-06-10 15:38:48 +000041 int count = 0;
42 while (reg) {
43 count += 1;
44 reg = reg->next();
45 }
46 return count;
47 }
48
49private:
50 const GMRegistry* fReg;
51};
52
reed@android.com8015dd82009-06-21 00:49:18 +000053static SkString make_name(const char shortName[], const char configName[]) {
54 SkString name(shortName);
55 name.appendf("_%s", configName);
56 return name;
57}
58
reed@google.com07700442010-12-20 19:46:07 +000059static SkString make_filename(const char path[], const SkString& name, const char suffix[]) {
reed@android.com8015dd82009-06-21 00:49:18 +000060 SkString filename(path);
61 if (filename.size() && filename[filename.size() - 1] != '/') {
62 filename.append("/");
reed@android.com00dae862009-06-10 15:38:48 +000063 }
reed@google.com07700442010-12-20 19:46:07 +000064 filename.appendf("%s.%s", name.c_str(), suffix);
reed@android.com8015dd82009-06-21 00:49:18 +000065 return filename;
66}
67
reed@android.comb9b9a182009-07-08 02:54:47 +000068/* since PNG insists on unpremultiplying our alpha, we take no precision chances
69 and force all pixels to be 100% opaque, otherwise on compare we may not get
70 a perfect match.
71 */
72static void force_all_opaque(const SkBitmap& bitmap) {
73 SkAutoLockPixels lock(bitmap);
74 for (int y = 0; y < bitmap.height(); y++) {
75 for (int x = 0; x < bitmap.width(); x++) {
76 *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT);
77 }
78 }
79}
80
81static bool write_bitmap(const SkString& path, const SkBitmap& bitmap) {
82 SkBitmap copy;
83 bitmap.copyTo(&copy, SkBitmap::kARGB_8888_Config);
84 force_all_opaque(copy);
85 return SkImageEncoder::EncodeFile(path.c_str(), copy,
86 SkImageEncoder::kPNG_Type, 100);
87}
88
reed@google.com3d3f0922010-12-20 21:10:29 +000089static inline SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1) {
90 int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
91 int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
92 int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
93 return SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db));
94}
95
96static void compute_diff(const SkBitmap& target, const SkBitmap& base,
97 SkBitmap* diff) {
98 SkAutoLockPixels alp(*diff);
99
100 const int w = target.width();
101 const int h = target.height();
102 for (int y = 0; y < h; y++) {
103 for (int x = 0; x < w; x++) {
104 SkPMColor c0 = *base.getAddr32(x, y);
105 SkPMColor c1 = *target.getAddr32(x, y);
106 SkPMColor d = 0;
107 if (c0 != c1) {
108 d = compute_diff_pmcolor(c0, c1);
109 }
110 *diff->getAddr32(x, y) = d;
111 }
112 }
113}
114
115static bool compare(const SkBitmap& target, const SkBitmap& base,
116 const SkString& name, SkBitmap* diff) {
reed@android.comb9b9a182009-07-08 02:54:47 +0000117 SkBitmap copy;
118 const SkBitmap* bm = &target;
119 if (target.config() != SkBitmap::kARGB_8888_Config) {
120 target.copyTo(&copy, SkBitmap::kARGB_8888_Config);
121 bm = &copy;
122 }
123
124 force_all_opaque(*bm);
125
126 const int w = bm->width();
127 const int h = bm->height();
128 if (w != base.width() || h != base.height()) {
129 SkDebugf("---- dimensions mismatch for %s base [%d %d] current [%d %d]\n",
130 name.c_str(), base.width(), base.height(), w, h);
reed@google.com3d3f0922010-12-20 21:10:29 +0000131 return false;
reed@android.comb9b9a182009-07-08 02:54:47 +0000132 }
133
134 SkAutoLockPixels bmLock(*bm);
135 SkAutoLockPixels baseLock(base);
136
137 for (int y = 0; y < h; y++) {
138 for (int x = 0; x < w; x++) {
139 SkPMColor c0 = *base.getAddr32(x, y);
140 SkPMColor c1 = *bm->getAddr32(x, y);
141 if (c0 != c1) {
142 SkDebugf("----- pixel mismatch for %s at [%d %d] base 0x%08X current 0x%08X\n",
143 name.c_str(), x, y, c0, c1);
reed@google.com3d3f0922010-12-20 21:10:29 +0000144
145 if (diff) {
146 diff->setConfig(SkBitmap::kARGB_8888_Config, w, h);
147 diff->allocPixels();
148 compute_diff(*bm, base, diff);
149 }
150 return false;
reed@android.comb9b9a182009-07-08 02:54:47 +0000151 }
152 }
153 }
reed@google.com3d3f0922010-12-20 21:10:29 +0000154
155 // they're equal
156 return true;
reed@android.com8015dd82009-06-21 00:49:18 +0000157}
reed@android.com00dae862009-06-10 15:38:48 +0000158
reed@google.com07700442010-12-20 19:46:07 +0000159static void write_pdf(GM* gm, const char writePath[]) {
160#ifdef SK_SUPPORT_PDF
161 SkISize size = gm->getISize();
162 SkPDFDevice* dev = new SkPDFDevice(size.width(), size.height());
163 SkAutoUnref aur(dev);
164
165 {
166 SkCanvas c(dev);
167 gm->draw(&c);
168 }
169
170 SkDynamicMemoryWStream output;
171 SkPDFDocument doc;
172 doc.appendPage(dev);
173 doc.emitPDF(&output);
174
175 SkString shortName(gm->shortName());
176 SkString path = make_filename(writePath, shortName, "pdf");
177 SkFILEWStream stream(path.c_str());
178 stream.write(output.getStream(), output.getOffset());
179#endif
180}
181
reed@android.com00dae862009-06-10 15:38:48 +0000182static const struct {
183 SkBitmap::Config fConfig;
184 bool fUsePicture;
185 const char* fName;
186} gRec[] = {
187 { SkBitmap::kARGB_8888_Config, false, "8888" },
188 { SkBitmap::kARGB_4444_Config, false, "4444" },
189 { SkBitmap::kRGB_565_Config, false, "565" },
reed@android.com00dae862009-06-10 15:38:48 +0000190};
191
192int main (int argc, char * const argv[]) {
193 SkAutoGraphics ag;
194
reed@android.com8015dd82009-06-21 00:49:18 +0000195 const char* writePath = NULL; // if non-null, where we write the originals
196 const char* readPath = NULL; // if non-null, were we read from to compare
reed@google.com3d3f0922010-12-20 21:10:29 +0000197 const char* diffPath = NULL; // if non-null, where we write our diffs (from compare)
reed@android.com8015dd82009-06-21 00:49:18 +0000198
199 char* const* stop = argv + argc;
200 for (++argv; argv < stop; ++argv) {
201 if (strcmp(*argv, "-w") == 0) {
202 argv++;
203 if (argv < stop && **argv) {
204 writePath = *argv;
205 }
206 } else if (strcmp(*argv, "-r") == 0) {
207 argv++;
208 if (argv < stop && **argv) {
209 readPath = *argv;
210 }
reed@google.com3d3f0922010-12-20 21:10:29 +0000211 } else if (strcmp(*argv, "-d") == 0) {
212 argv++;
213 if (argv < stop && **argv) {
214 diffPath = *argv;
215 }
216 }
217 }
reed@google.com873cb1e2010-12-23 15:00:45 +0000218
219 // setup a GL context for drawing offscreen
220 SkEGLContext eglContext;
221 eglContext.init(1024, 1024);
222 GrContext* context = GrContext::CreateGLShaderContext();
223
reed@android.com00dae862009-06-10 15:38:48 +0000224 Iter iter;
225 GM* gm;
reed@android.com00f883e2010-12-14 17:46:14 +0000226
227 if (readPath) {
228 fprintf(stderr, "reading from %s\n", readPath);
229 } else if (writePath) {
230 fprintf(stderr, "writing to %s\n", writePath);
231 }
232
reed@android.com00dae862009-06-10 15:38:48 +0000233 while ((gm = iter.next()) != NULL) {
234 SkISize size = gm->getISize();
reed@google.com3d3f0922010-12-20 21:10:29 +0000235 SkDebugf("drawing... %s [%d %d]\n", gm->shortName(),
reed@android.com8015dd82009-06-21 00:49:18 +0000236 size.width(), size.height());
237
reed@android.com00dae862009-06-10 15:38:48 +0000238 SkBitmap bitmap;
reed@android.comdd0ac282009-06-20 02:38:16 +0000239 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
reed@android.com00dae862009-06-10 15:38:48 +0000240 bitmap.setConfig(gRec[i].fConfig, size.width(), size.height());
241 bitmap.allocPixels();
242 bitmap.eraseColor(0);
243 SkCanvas canvas(bitmap);
244
245 gm->draw(&canvas);
reed@google.com873cb1e2010-12-23 15:00:45 +0000246 {
247 SkGpuCanvas gc(context);
248 gc.setDevice(gc.createDevice(bitmap.config(), bitmap.width(), bitmap.height(),
249 bitmap.isOpaque(), false))->unref();
250 gm->draw(&gc);
251 }
reed@android.com8015dd82009-06-21 00:49:18 +0000252 SkString name = make_name(gm->shortName(), gRec[i].fName);
253
254 if (writePath) {
reed@google.com07700442010-12-20 19:46:07 +0000255 SkString path = make_filename(writePath, name, "png");
reed@android.comb9b9a182009-07-08 02:54:47 +0000256 bool success = write_bitmap(path, bitmap);
reed@android.com8015dd82009-06-21 00:49:18 +0000257 if (!success) {
258 fprintf(stderr, "FAILED to write %s\n", path.c_str());
259 }
reed@google.com07700442010-12-20 19:46:07 +0000260 write_pdf(gm, writePath);
reed@android.com8015dd82009-06-21 00:49:18 +0000261 } else if (readPath) {
reed@google.com07700442010-12-20 19:46:07 +0000262 SkString path = make_filename(readPath, name, "png");
reed@android.com8015dd82009-06-21 00:49:18 +0000263 SkBitmap orig;
264 bool success = SkImageDecoder::DecodeFile(path.c_str(), &orig,
265 SkBitmap::kARGB_8888_Config,
266 SkImageDecoder::kDecodePixels_Mode, NULL);
267 if (success) {
reed@google.com3d3f0922010-12-20 21:10:29 +0000268 SkBitmap diffBitmap;
269 success = compare(bitmap, orig, name, diffPath ? &diffBitmap : NULL);
270 if (!success && diffPath) {
271 SkString diffName = make_filename(diffPath, name, ".diff.png");
272 fprintf(stderr, "Writing %s\n", diffName.c_str());
273 write_bitmap(diffName, diffBitmap);
274 }
reed@android.com8015dd82009-06-21 00:49:18 +0000275 } else {
276 fprintf(stderr, "FAILED to read %s\n", path.c_str());
277 }
278 }
reed@android.com00dae862009-06-10 15:38:48 +0000279 }
280 SkDELETE(gm);
281 }
282 return 0;
283}
reed@android.comdd0ac282009-06-20 02:38:16 +0000284
285///////////////////////////////////////////////////////////////////////////////
286
287using namespace skiagm;
288
289GM::GM() {}
290GM::~GM() {}
291
292void GM::draw(SkCanvas* canvas) {
293 this->onDraw(canvas);
294}
295
296