blob: 357a54e0f9039903f5639b4d41c94d258c1de833 [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"
reed@google.comd4dfd102011-01-18 21:05:42 +000011#include "SkGpuDevice.h"
reed@google.com873cb1e2010-12-23 15:00:45 +000012#include "SkEGLContext.h"
13#include "SkDevice.h"
14
reed@google.com07700442010-12-20 19:46:07 +000015#ifdef SK_SUPPORT_PDF
16 #include "SkPDFDevice.h"
17 #include "SkPDFDocument.h"
18#endif
reed@android.com00dae862009-06-10 15:38:48 +000019
20using namespace skiagm;
21
22// need to explicitly declare this, or we get some weird infinite loop llist
23template GMRegistry* GMRegistry::gHead;
24
25class Iter {
26public:
27 Iter() {
reed@android.comdd0ac282009-06-20 02:38:16 +000028 fReg = GMRegistry::Head();
reed@android.com00dae862009-06-10 15:38:48 +000029 }
reed@google.comd4dfd102011-01-18 21:05:42 +000030
reed@android.comdd0ac282009-06-20 02:38:16 +000031 GM* next() {
reed@android.com00dae862009-06-10 15:38:48 +000032 if (fReg) {
reed@android.comdd0ac282009-06-20 02:38:16 +000033 GMRegistry::Factory fact = fReg->factory();
reed@android.com00dae862009-06-10 15:38:48 +000034 fReg = fReg->next();
reed@android.comdd0ac282009-06-20 02:38:16 +000035 return fact(0);
reed@android.com00dae862009-06-10 15:38:48 +000036 }
37 return NULL;
38 }
reed@google.comd4dfd102011-01-18 21:05:42 +000039
reed@android.com00dae862009-06-10 15:38:48 +000040 static int Count() {
reed@android.comdd0ac282009-06-20 02:38:16 +000041 const GMRegistry* reg = GMRegistry::Head();
reed@android.com00dae862009-06-10 15:38:48 +000042 int count = 0;
43 while (reg) {
44 count += 1;
45 reg = reg->next();
46 }
47 return count;
48 }
reed@google.comd4dfd102011-01-18 21:05:42 +000049
reed@android.com00dae862009-06-10 15:38:48 +000050private:
51 const GMRegistry* fReg;
52};
53
reed@android.com8015dd82009-06-21 00:49:18 +000054static SkString make_name(const char shortName[], const char configName[]) {
55 SkString name(shortName);
56 name.appendf("_%s", configName);
57 return name;
58}
59
reed@google.com07700442010-12-20 19:46:07 +000060static SkString make_filename(const char path[], const SkString& name, const char suffix[]) {
reed@android.com8015dd82009-06-21 00:49:18 +000061 SkString filename(path);
62 if (filename.size() && filename[filename.size() - 1] != '/') {
63 filename.append("/");
reed@android.com00dae862009-06-10 15:38:48 +000064 }
reed@google.com07700442010-12-20 19:46:07 +000065 filename.appendf("%s.%s", name.c_str(), suffix);
reed@android.com8015dd82009-06-21 00:49:18 +000066 return filename;
67}
68
reed@android.comb9b9a182009-07-08 02:54:47 +000069/* since PNG insists on unpremultiplying our alpha, we take no precision chances
70 and force all pixels to be 100% opaque, otherwise on compare we may not get
71 a perfect match.
72 */
73static void force_all_opaque(const SkBitmap& bitmap) {
74 SkAutoLockPixels lock(bitmap);
75 for (int y = 0; y < bitmap.height(); y++) {
76 for (int x = 0; x < bitmap.width(); x++) {
77 *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT);
78 }
79 }
80}
81
82static bool write_bitmap(const SkString& path, const SkBitmap& bitmap) {
83 SkBitmap copy;
84 bitmap.copyTo(&copy, SkBitmap::kARGB_8888_Config);
85 force_all_opaque(copy);
86 return SkImageEncoder::EncodeFile(path.c_str(), copy,
87 SkImageEncoder::kPNG_Type, 100);
88}
89
reed@google.com3d3f0922010-12-20 21:10:29 +000090static inline SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1) {
91 int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
92 int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
93 int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
94 return SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db));
95}
96
97static void compute_diff(const SkBitmap& target, const SkBitmap& base,
98 SkBitmap* diff) {
99 SkAutoLockPixels alp(*diff);
100
101 const int w = target.width();
102 const int h = target.height();
103 for (int y = 0; y < h; y++) {
104 for (int x = 0; x < w; x++) {
105 SkPMColor c0 = *base.getAddr32(x, y);
106 SkPMColor c1 = *target.getAddr32(x, y);
107 SkPMColor d = 0;
108 if (c0 != c1) {
109 d = compute_diff_pmcolor(c0, c1);
110 }
111 *diff->getAddr32(x, y) = d;
112 }
113 }
114}
115
116static bool compare(const SkBitmap& target, const SkBitmap& base,
117 const SkString& name, SkBitmap* diff) {
reed@android.comb9b9a182009-07-08 02:54:47 +0000118 SkBitmap copy;
119 const SkBitmap* bm = &target;
120 if (target.config() != SkBitmap::kARGB_8888_Config) {
121 target.copyTo(&copy, SkBitmap::kARGB_8888_Config);
122 bm = &copy;
123 }
124
125 force_all_opaque(*bm);
126
127 const int w = bm->width();
128 const int h = bm->height();
129 if (w != base.width() || h != base.height()) {
130 SkDebugf("---- dimensions mismatch for %s base [%d %d] current [%d %d]\n",
131 name.c_str(), base.width(), base.height(), w, h);
reed@google.com3d3f0922010-12-20 21:10:29 +0000132 return false;
reed@android.comb9b9a182009-07-08 02:54:47 +0000133 }
134
135 SkAutoLockPixels bmLock(*bm);
136 SkAutoLockPixels baseLock(base);
137
138 for (int y = 0; y < h; y++) {
139 for (int x = 0; x < w; x++) {
140 SkPMColor c0 = *base.getAddr32(x, y);
141 SkPMColor c1 = *bm->getAddr32(x, y);
142 if (c0 != c1) {
143 SkDebugf("----- pixel mismatch for %s at [%d %d] base 0x%08X current 0x%08X\n",
144 name.c_str(), x, y, c0, c1);
reed@google.com3d3f0922010-12-20 21:10:29 +0000145
146 if (diff) {
147 diff->setConfig(SkBitmap::kARGB_8888_Config, w, h);
148 diff->allocPixels();
149 compute_diff(*bm, base, diff);
150 }
151 return false;
reed@android.comb9b9a182009-07-08 02:54:47 +0000152 }
153 }
154 }
reed@google.com3d3f0922010-12-20 21:10:29 +0000155
156 // they're equal
157 return true;
reed@android.com8015dd82009-06-21 00:49:18 +0000158}
reed@android.com00dae862009-06-10 15:38:48 +0000159
vandebo@chromium.org686abdf2011-02-03 23:00:40 +0000160static bool write_pdf(const SkString& path, const SkDynamicMemoryWStream& pdf) {
161 SkFILEWStream stream(path.c_str());
162 return stream.write(pdf.getStream(), pdf.getOffset());
reed@google.com07700442010-12-20 19:46:07 +0000163}
164
vandebo@chromium.org686abdf2011-02-03 23:00:40 +0000165enum Backend {
166 kRaster_Backend,
167 kGPU_Backend,
168 kPDF_Backend,
169};
170
reed@android.com00dae862009-06-10 15:38:48 +0000171static const struct {
172 SkBitmap::Config fConfig;
vandebo@chromium.org686abdf2011-02-03 23:00:40 +0000173 Backend fBackend;
reed@android.com00dae862009-06-10 15:38:48 +0000174 const char* fName;
175} gRec[] = {
vandebo@chromium.org686abdf2011-02-03 23:00:40 +0000176 { SkBitmap::kARGB_8888_Config, kRaster_Backend, "8888" },
177 { SkBitmap::kARGB_4444_Config, kRaster_Backend, "4444" },
178 { SkBitmap::kRGB_565_Config, kRaster_Backend, "565" },
179 { SkBitmap::kARGB_8888_Config, kGPU_Backend, "gpu" },
180#ifdef SK_SUPPORT_PDF
181 { SkBitmap::kARGB_8888_Config, kPDF_Backend, "pdf" },
182#endif
reed@android.com00dae862009-06-10 15:38:48 +0000183};
184
185int main (int argc, char * const argv[]) {
186 SkAutoGraphics ag;
reed@google.comd4dfd102011-01-18 21:05:42 +0000187
reed@android.com8015dd82009-06-21 00:49:18 +0000188 const char* writePath = NULL; // if non-null, where we write the originals
189 const char* readPath = NULL; // if non-null, were we read from to compare
reed@google.com3d3f0922010-12-20 21:10:29 +0000190 const char* diffPath = NULL; // if non-null, where we write our diffs (from compare)
reed@android.com8015dd82009-06-21 00:49:18 +0000191
192 char* const* stop = argv + argc;
193 for (++argv; argv < stop; ++argv) {
194 if (strcmp(*argv, "-w") == 0) {
195 argv++;
196 if (argv < stop && **argv) {
197 writePath = *argv;
198 }
199 } else if (strcmp(*argv, "-r") == 0) {
200 argv++;
201 if (argv < stop && **argv) {
202 readPath = *argv;
203 }
reed@google.com3d3f0922010-12-20 21:10:29 +0000204 } else if (strcmp(*argv, "-d") == 0) {
205 argv++;
206 if (argv < stop && **argv) {
207 diffPath = *argv;
208 }
209 }
210 }
reed@google.com873cb1e2010-12-23 15:00:45 +0000211
212 // setup a GL context for drawing offscreen
reed@google.com37df17d2010-12-23 20:20:51 +0000213 GrContext* context = NULL;
reed@google.com873cb1e2010-12-23 15:00:45 +0000214 SkEGLContext eglContext;
reed@google.com37df17d2010-12-23 20:20:51 +0000215 if (eglContext.init(1024, 1024)) {
216 context = GrContext::CreateGLShaderContext();
217 }
reed@google.com873cb1e2010-12-23 15:00:45 +0000218
reed@android.com00dae862009-06-10 15:38:48 +0000219 Iter iter;
220 GM* gm;
reed@android.com00f883e2010-12-14 17:46:14 +0000221
222 if (readPath) {
223 fprintf(stderr, "reading from %s\n", readPath);
224 } else if (writePath) {
225 fprintf(stderr, "writing to %s\n", writePath);
226 }
227
reed@android.com00dae862009-06-10 15:38:48 +0000228 while ((gm = iter.next()) != NULL) {
229 SkISize size = gm->getISize();
reed@google.com3d3f0922010-12-20 21:10:29 +0000230 SkDebugf("drawing... %s [%d %d]\n", gm->shortName(),
reed@android.com8015dd82009-06-21 00:49:18 +0000231 size.width(), size.height());
232
reed@android.com00dae862009-06-10 15:38:48 +0000233 SkBitmap bitmap;
vandebo@chromium.org686abdf2011-02-03 23:00:40 +0000234 SkDynamicMemoryWStream pdf;
reed@android.comdd0ac282009-06-20 02:38:16 +0000235 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
vandebo@chromium.org686abdf2011-02-03 23:00:40 +0000236 if (gRec[i].fBackend == kRaster_Backend ||
237 gRec[i].fBackend == kGPU_Backend) {
238 bitmap.setConfig(gRec[i].fConfig, size.width(), size.height());
239 bitmap.allocPixels();
240 bitmap.eraseColor(0);
241 SkCanvas canvas(bitmap);
reed@android.com00dae862009-06-10 15:38:48 +0000242
vandebo@chromium.org686abdf2011-02-03 23:00:40 +0000243 if (gRec[i].fBackend == kRaster_Backend) {
244 gm->draw(&canvas);
245 } else { // GPU
246 if (NULL == context) {
247 continue;
248 }
249 SkGpuCanvas gc(context,
250 SkGpuDevice::Current3DApiRenderTarget());
251 gc.setDevice(gc.createDevice(bitmap.config(),
252 bitmap.width(),
253 bitmap.height(),
254 bitmap.isOpaque(),
255 false))->unref();
256 gm->draw(&gc);
257 gc.readPixels(&bitmap); // overwrite our previous allocation
reed@google.com37df17d2010-12-23 20:20:51 +0000258 }
vandebo@chromium.org686abdf2011-02-03 23:00:40 +0000259 }
260 // TODO: Figure out a way to compare PDFs.
261 if (gRec[i].fBackend == kPDF_Backend && writePath) {
262#ifdef SK_SUPPORT_PDF
263 SkISize size = gm->getISize();
264 SkPDFDevice* dev = new SkPDFDevice(size.width(), size.height());
265 SkAutoUnref aur(dev);
266
267 SkCanvas c(dev);
268 gm->draw(&c);
269
270 SkPDFDocument doc;
271 doc.appendPage(dev);
272 doc.emitPDF(&pdf);
273#endif
reed@google.com873cb1e2010-12-23 15:00:45 +0000274 }
reed@android.com8015dd82009-06-21 00:49:18 +0000275 SkString name = make_name(gm->shortName(), gRec[i].fName);
276
277 if (writePath) {
vandebo@chromium.org686abdf2011-02-03 23:00:40 +0000278 SkString path;
279 bool success;
280 if (gRec[i].fBackend != kPDF_Backend) {
281 path = make_filename(writePath, name, "png");
282 success = write_bitmap(path, bitmap);
283 } else {
284 path = make_filename(writePath, name, "pdf");
285 success = write_pdf(path, pdf);
reed@android.com8015dd82009-06-21 00:49:18 +0000286 }
vandebo@chromium.org686abdf2011-02-03 23:00:40 +0000287 if (!success)
288 fprintf(stderr, "FAILED to write %s\n", path.c_str());
289 // TODO: Figure out a way to compare PDFs.
290 } else if (readPath && gRec[i].fBackend != kPDF_Backend) {
reed@google.com07700442010-12-20 19:46:07 +0000291 SkString path = make_filename(readPath, name, "png");
reed@android.com8015dd82009-06-21 00:49:18 +0000292 SkBitmap orig;
293 bool success = SkImageDecoder::DecodeFile(path.c_str(), &orig,
294 SkBitmap::kARGB_8888_Config,
295 SkImageDecoder::kDecodePixels_Mode, NULL);
296 if (success) {
reed@google.com3d3f0922010-12-20 21:10:29 +0000297 SkBitmap diffBitmap;
298 success = compare(bitmap, orig, name, diffPath ? &diffBitmap : NULL);
299 if (!success && diffPath) {
300 SkString diffName = make_filename(diffPath, name, ".diff.png");
301 fprintf(stderr, "Writing %s\n", diffName.c_str());
302 write_bitmap(diffName, diffBitmap);
303 }
reed@android.com8015dd82009-06-21 00:49:18 +0000304 } else {
305 fprintf(stderr, "FAILED to read %s\n", path.c_str());
306 }
307 }
reed@android.com00dae862009-06-10 15:38:48 +0000308 }
309 SkDELETE(gm);
310 }
311 return 0;
312}
reed@android.comdd0ac282009-06-20 02:38:16 +0000313
314///////////////////////////////////////////////////////////////////////////////
315
316using namespace skiagm;
317
318GM::GM() {}
319GM::~GM() {}
320
321void GM::draw(SkCanvas* canvas) {
322 this->onDraw(canvas);
323}
324
325