blob: 0844458ba398fd9d30479414d94748c4215e3d78 [file] [log] [blame]
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001#include "EdgeWalker_Test.h"
2#include "Intersection_Tests.h"
3#include "SkBitmap.h"
4#include "SkCanvas.h"
5#include "SkPaint.h"
caryclark@google.comfb173422012-04-10 18:28:55 +00006#include <algorithm>
caryclark@google.comcd4421d2012-03-01 19:16:31 +00007
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00008static bool gShowPath = false;
caryclark@google.com198e0542012-03-30 18:47:02 +00009static bool gComparePaths = true;
caryclark@google.com752b60e2012-03-22 21:11:17 +000010static bool gDrawLastAsciiPaths = true;
caryclark@google.comcd4421d2012-03-01 19:16:31 +000011static bool gDrawAllAsciiPaths = false;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000012static bool gShowAsciiPaths = false;
caryclark@google.com752b60e2012-03-22 21:11:17 +000013static bool gComparePathsAssert = true;
caryclark@google.comcd4421d2012-03-01 19:16:31 +000014
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000015void showPath(const SkPath& path, const char* str) {
caryclark@google.com752b60e2012-03-22 21:11:17 +000016 SkDebugf("%s\n", !str ? "original:" : str);
caryclark@google.comcd4421d2012-03-01 19:16:31 +000017 SkPath::Iter iter(path, true);
18 uint8_t verb;
19 SkPoint pts[4];
20 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
21 switch (verb) {
22 case SkPath::kMove_Verb:
caryclark@google.comd88e0892012-03-27 13:23:51 +000023 SkDebugf("path.moveTo(%1.9g, %1.9g);\n", pts[0].fX, pts[0].fY);
caryclark@google.comcd4421d2012-03-01 19:16:31 +000024 continue;
25 case SkPath::kLine_Verb:
caryclark@google.comd88e0892012-03-27 13:23:51 +000026 SkDebugf("path.lineTo(%1.9g, %1.9g);\n", pts[1].fX, pts[1].fY);
caryclark@google.comcd4421d2012-03-01 19:16:31 +000027 break;
28 case SkPath::kQuad_Verb:
caryclark@google.comd88e0892012-03-27 13:23:51 +000029 SkDebugf("path.quadTo(%1.9g, %1.9g, %1.9g, %1.9g);\n",
caryclark@google.comcd4421d2012-03-01 19:16:31 +000030 pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
31 break;
32 case SkPath::kCubic_Verb:
caryclark@google.comd88e0892012-03-27 13:23:51 +000033 SkDebugf("path.cubicTo(%1.9g, %1.9g, %1.9g, %1.9g);\n",
caryclark@google.comcd4421d2012-03-01 19:16:31 +000034 pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
35 pts[3].fX, pts[3].fY);
36 break;
37 case SkPath::kClose_Verb:
38 SkDebugf("path.close();\n");
39 continue;
40 default:
41 SkDEBUGFAIL("bad verb");
42 return;
43 }
44 }
45}
46
caryclark@google.com198e0542012-03-30 18:47:02 +000047static int pathsDrawTheSame(const SkPath& one, const SkPath& two,
48 SkBitmap& bits, SkCanvas* c) {
49 SkCanvas* canvasPtr = c;
50 if (!c) {
51 canvasPtr = new SkCanvas(bits);
52 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +000053 const SkRect& bounds1 = one.getBounds();
54 const SkRect& bounds2 = two.getBounds();
55 SkRect larger = bounds1;
56 larger.join(bounds2);
caryclark@google.comcd4421d2012-03-01 19:16:31 +000057 int bitWidth = SkScalarCeil(larger.width()) + 2;
58 int bitHeight = SkScalarCeil(larger.height()) + 2;
caryclark@google.com198e0542012-03-30 18:47:02 +000059 if (bits.width() < bitWidth * 2 || bits.height() < bitHeight) {
60 if (bits.width() >= 200) {
61 SkDebugf("%s bitWidth=%d bitHeight=%d\n", __FUNCTION__, bitWidth, bitHeight);
62 }
63 bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
64 bits.allocPixels();
65 canvasPtr->setBitmapDevice(bits);
66 }
67 SkCanvas& canvas = *canvasPtr;
caryclark@google.comcd4421d2012-03-01 19:16:31 +000068 canvas.drawColor(SK_ColorWHITE);
69 SkPaint paint;
70 canvas.save();
71 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
72 canvas.drawPath(one, paint);
73 canvas.restore();
74 canvas.save();
75 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
76 canvas.drawPath(two, paint);
77 canvas.restore();
caryclark@google.com198e0542012-03-30 18:47:02 +000078 int errors = 0;
caryclark@google.comcd4421d2012-03-01 19:16:31 +000079 for (int y = 0; y < bitHeight; ++y) {
80 uint32_t* addr1 = bits.getAddr32(0, y);
81 uint32_t* addr2 = bits.getAddr32(bitWidth, y);
82 for (int x = 0; x < bitWidth; ++x) {
caryclark@google.com198e0542012-03-30 18:47:02 +000083 errors += addr1[x] != addr2[x];
caryclark@google.comcd4421d2012-03-01 19:16:31 +000084 }
85 }
caryclark@google.com198e0542012-03-30 18:47:02 +000086 if (!c) {
87 delete canvasPtr;
88 }
89 return errors;
90}
91
92void bitmapInit(SkBitmap& bits) {
caryclark@google.comcd4421d2012-03-01 19:16:31 +000093}
94
caryclark@google.com752b60e2012-03-22 21:11:17 +000095bool drawAsciiPaths(const SkPath& one, const SkPath& two,
caryclark@google.comcd4421d2012-03-01 19:16:31 +000096 bool drawPaths) {
97 if (!drawPaths) {
caryclark@google.com752b60e2012-03-22 21:11:17 +000098 return true;
caryclark@google.comcd4421d2012-03-01 19:16:31 +000099 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000100 if (gShowAsciiPaths) {
101 showPath(one, "one:");
102 showPath(two, "two:");
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000103 }
104 const SkRect& bounds1 = one.getBounds();
105 const SkRect& bounds2 = two.getBounds();
106 SkRect larger = bounds1;
107 larger.join(bounds2);
108 SkBitmap bits;
caryclark@google.com752b60e2012-03-22 21:11:17 +0000109 char out[256];
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000110 int bitWidth = SkScalarCeil(larger.width()) + 2;
caryclark@google.com752b60e2012-03-22 21:11:17 +0000111 if (bitWidth * 2 + 1 >= (int) sizeof(out)) {
112 return false;
113 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000114 int bitHeight = SkScalarCeil(larger.height()) + 2;
caryclark@google.com752b60e2012-03-22 21:11:17 +0000115 if (bitHeight >= (int) sizeof(out)) {
116 return false;
117 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000118 bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
119 bits.allocPixels();
120 SkCanvas canvas(bits);
121 canvas.drawColor(SK_ColorWHITE);
122 SkPaint paint;
123 canvas.save();
124 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
125 canvas.drawPath(one, paint);
126 canvas.restore();
127 canvas.save();
caryclark@google.comfb173422012-04-10 18:28:55 +0000128 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000129 canvas.drawPath(two, paint);
130 canvas.restore();
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000131 for (int y = 0; y < bitHeight; ++y) {
132 uint32_t* addr1 = bits.getAddr32(0, y);
133 int x;
134 char* outPtr = out;
135 for (x = 0; x < bitWidth; ++x) {
136 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
137 }
138 *outPtr++ = '|';
139 for (x = bitWidth; x < bitWidth * 2; ++x) {
140 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
141 }
142 *outPtr++ = '\0';
143 SkDebugf("%s\n", out);
144 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000145 return true;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000146}
147
caryclark@google.com198e0542012-03-30 18:47:02 +0000148static int scaledDrawTheSame(const SkPath& one, const SkPath& two,
149 int a, int b, bool drawPaths, SkBitmap& bitmap, SkCanvas* canvas) {
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000150 SkMatrix scale;
151 scale.reset();
caryclark@google.com752b60e2012-03-22 21:11:17 +0000152 float aScale = 1.21f;
153 float bScale = 1.11f;
154 scale.preScale(a * aScale, b * bScale);
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000155 SkPath scaledOne, scaledTwo;
156 one.transform(scale, &scaledOne);
157 two.transform(scale, &scaledTwo);
caryclark@google.com198e0542012-03-30 18:47:02 +0000158 int errors = pathsDrawTheSame(scaledOne, scaledTwo, bitmap, canvas);
159 if (errors == 0) {
160 return 0;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000161 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000162 while (!drawAsciiPaths(scaledOne, scaledTwo, drawPaths)) {
163 scale.reset();
164 aScale *= 0.5f;
165 bScale *= 0.5f;
166 scale.preScale(a * aScale, b * bScale);
167 one.transform(scale, &scaledOne);
168 two.transform(scale, &scaledTwo);
169 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000170 return errors;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000171}
172
caryclark@google.com198e0542012-03-30 18:47:02 +0000173static int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap,
174 SkCanvas* canvas) {
175 int errors = pathsDrawTheSame(one, two, bitmap, canvas);
176 if (errors == 0) {
177 return 0;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000178 }
179 drawAsciiPaths(one, two, gDrawAllAsciiPaths);
180 for (int x = 9; x <= 33; ++x) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000181 errors = scaledDrawTheSame(one, two, x, x - (x >> 2), gDrawAllAsciiPaths,
182 bitmap, canvas);
183 if (errors == 0) {
184 return 0;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000185 }
186 }
187 if (!gDrawAllAsciiPaths) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000188 const SkRect& bounds1 = one.getBounds();
189 const SkRect& bounds2 = two.getBounds();
190 SkRect larger = bounds1;
191 larger.join(bounds2);
192 SkScalar xScale = std::max(80.0f / larger.width(), 1.0f);
193 SkScalar yScale = std::max(60.0f / larger.height(), 1.0f);
194 errors = scaledDrawTheSame(one, two, xScale, yScale, false, bitmap, canvas);
195 if (errors > 8) {
196 scaledDrawTheSame(one, two, xScale, yScale, true, bitmap, canvas);
caryclark@google.com198e0542012-03-30 18:47:02 +0000197 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000198 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000199 if (errors > 0) SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors);
caryclark@google.comfb173422012-04-10 18:28:55 +0000200 if (errors > 31 && gComparePathsAssert) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000201 showPath(one);
202 showPath(two, "simplified:");
203 SkASSERT(0);
204 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000205 return errors;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000206}
207
208// doesn't work yet
209void comparePathsTiny(const SkPath& one, const SkPath& two) {
210 const SkRect& bounds1 = one.getBounds();
211 const SkRect& bounds2 = two.getBounds();
212 SkRect larger = bounds1;
213 larger.join(bounds2);
214 SkBitmap bits;
215 int bitWidth = SkScalarCeil(larger.width()) + 2;
216 int bitHeight = SkScalarCeil(larger.height()) + 2;
217 bits.setConfig(SkBitmap::kA1_Config, bitWidth * 2, bitHeight);
218 bits.allocPixels();
219 SkCanvas canvas(bits);
220 canvas.drawColor(SK_ColorWHITE);
221 SkPaint paint;
222 canvas.save();
223 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
224 canvas.drawPath(one, paint);
225 canvas.restore();
226 canvas.save();
227 canvas.translate(-bounds2.fLeft + 1, -bounds2.fTop + 1);
228 canvas.drawPath(two, paint);
229 canvas.restore();
230 for (int y = 0; y < bitHeight; ++y) {
231 uint8_t* addr1 = bits.getAddr1(0, y);
232 uint8_t* addr2 = bits.getAddr1(bitWidth, y);
233 for (int x = 0; x < bits.rowBytes(); ++x) {
234 SkASSERT(addr1[x] == addr2[x]);
235 }
236 }
237}
238
caryclark@google.com198e0542012-03-30 18:47:02 +0000239bool testSimplify(const SkPath& path, bool fill, SkPath& out, SkBitmap& bitmap,
240 SkCanvas* canvas) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000241 if (gShowPath) {
242 showPath(path);
243 }
244 simplify(path, fill, out);
caryclark@google.com752b60e2012-03-22 21:11:17 +0000245 if (!gComparePaths) {
246 return true;
247 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000248 return comparePaths(path, out, bitmap, canvas) == 0;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000249}