blob: e406c7955f6e7a82e6830aca7d0b6fcf98739684 [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;
reed@google.com51df9e32010-12-23 19:29:18 +0000184 bool fUseGPU;
reed@android.com00dae862009-06-10 15:38:48 +0000185 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@google.com51df9e32010-12-23 19:29:18 +0000190 { SkBitmap::kARGB_8888_Config, true, "gpu" },
reed@android.com00dae862009-06-10 15:38:48 +0000191};
192
193int main (int argc, char * const argv[]) {
194 SkAutoGraphics ag;
195
reed@android.com8015dd82009-06-21 00:49:18 +0000196 const char* writePath = NULL; // if non-null, where we write the originals
197 const char* readPath = NULL; // if non-null, were we read from to compare
reed@google.com3d3f0922010-12-20 21:10:29 +0000198 const char* diffPath = NULL; // if non-null, where we write our diffs (from compare)
reed@android.com8015dd82009-06-21 00:49:18 +0000199
200 char* const* stop = argv + argc;
201 for (++argv; argv < stop; ++argv) {
202 if (strcmp(*argv, "-w") == 0) {
203 argv++;
204 if (argv < stop && **argv) {
205 writePath = *argv;
206 }
207 } else if (strcmp(*argv, "-r") == 0) {
208 argv++;
209 if (argv < stop && **argv) {
210 readPath = *argv;
211 }
reed@google.com3d3f0922010-12-20 21:10:29 +0000212 } else if (strcmp(*argv, "-d") == 0) {
213 argv++;
214 if (argv < stop && **argv) {
215 diffPath = *argv;
216 }
217 }
218 }
reed@google.com873cb1e2010-12-23 15:00:45 +0000219
220 // setup a GL context for drawing offscreen
reed@google.com37df17d2010-12-23 20:20:51 +0000221 GrContext* context = NULL;
reed@google.com873cb1e2010-12-23 15:00:45 +0000222 SkEGLContext eglContext;
reed@google.com37df17d2010-12-23 20:20:51 +0000223 if (eglContext.init(1024, 1024)) {
224 context = GrContext::CreateGLShaderContext();
225 }
reed@google.com873cb1e2010-12-23 15:00:45 +0000226
reed@android.com00dae862009-06-10 15:38:48 +0000227 Iter iter;
228 GM* gm;
reed@android.com00f883e2010-12-14 17:46:14 +0000229
230 if (readPath) {
231 fprintf(stderr, "reading from %s\n", readPath);
232 } else if (writePath) {
233 fprintf(stderr, "writing to %s\n", writePath);
234 }
235
reed@android.com00dae862009-06-10 15:38:48 +0000236 while ((gm = iter.next()) != NULL) {
237 SkISize size = gm->getISize();
reed@google.com3d3f0922010-12-20 21:10:29 +0000238 SkDebugf("drawing... %s [%d %d]\n", gm->shortName(),
reed@android.com8015dd82009-06-21 00:49:18 +0000239 size.width(), size.height());
240
reed@android.com00dae862009-06-10 15:38:48 +0000241 SkBitmap bitmap;
reed@android.comdd0ac282009-06-20 02:38:16 +0000242 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
reed@android.com00dae862009-06-10 15:38:48 +0000243 bitmap.setConfig(gRec[i].fConfig, size.width(), size.height());
244 bitmap.allocPixels();
245 bitmap.eraseColor(0);
246 SkCanvas canvas(bitmap);
247
reed@google.com51df9e32010-12-23 19:29:18 +0000248 if (gRec[i].fUseGPU) {
reed@google.com37df17d2010-12-23 20:20:51 +0000249 if (NULL == context) {
250 continue;
251 }
reed@google.com873cb1e2010-12-23 15:00:45 +0000252 SkGpuCanvas gc(context);
253 gc.setDevice(gc.createDevice(bitmap.config(), bitmap.width(), bitmap.height(),
254 bitmap.isOpaque(), false))->unref();
255 gm->draw(&gc);
reed@google.com51df9e32010-12-23 19:29:18 +0000256 gc.readPixels(&bitmap); // overwrite our previous allocation
257 } else {
258 gm->draw(&canvas);
reed@google.com873cb1e2010-12-23 15:00:45 +0000259 }
reed@android.com8015dd82009-06-21 00:49:18 +0000260 SkString name = make_name(gm->shortName(), gRec[i].fName);
261
262 if (writePath) {
reed@google.com07700442010-12-20 19:46:07 +0000263 SkString path = make_filename(writePath, name, "png");
reed@android.comb9b9a182009-07-08 02:54:47 +0000264 bool success = write_bitmap(path, bitmap);
reed@android.com8015dd82009-06-21 00:49:18 +0000265 if (!success) {
266 fprintf(stderr, "FAILED to write %s\n", path.c_str());
267 }
reed@google.com07700442010-12-20 19:46:07 +0000268 write_pdf(gm, writePath);
reed@android.com8015dd82009-06-21 00:49:18 +0000269 } else if (readPath) {
reed@google.com07700442010-12-20 19:46:07 +0000270 SkString path = make_filename(readPath, name, "png");
reed@android.com8015dd82009-06-21 00:49:18 +0000271 SkBitmap orig;
272 bool success = SkImageDecoder::DecodeFile(path.c_str(), &orig,
273 SkBitmap::kARGB_8888_Config,
274 SkImageDecoder::kDecodePixels_Mode, NULL);
275 if (success) {
reed@google.com3d3f0922010-12-20 21:10:29 +0000276 SkBitmap diffBitmap;
277 success = compare(bitmap, orig, name, diffPath ? &diffBitmap : NULL);
278 if (!success && diffPath) {
279 SkString diffName = make_filename(diffPath, name, ".diff.png");
280 fprintf(stderr, "Writing %s\n", diffName.c_str());
281 write_bitmap(diffName, diffBitmap);
282 }
reed@android.com8015dd82009-06-21 00:49:18 +0000283 } else {
284 fprintf(stderr, "FAILED to read %s\n", path.c_str());
285 }
286 }
reed@android.com00dae862009-06-10 15:38:48 +0000287 }
288 SkDELETE(gm);
289 }
290 return 0;
291}
reed@android.comdd0ac282009-06-20 02:38:16 +0000292
293///////////////////////////////////////////////////////////////////////////////
294
295using namespace skiagm;
296
297GM::GM() {}
298GM::~GM() {}
299
300void GM::draw(SkCanvas* canvas) {
301 this->onDraw(canvas);
302}
303
304