Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / tests / AAClipTest.cpp
1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7
8 #include "SkAAClip.h"
9 #include "SkCanvas.h"
10 #include "SkMask.h"
11 #include "SkPath.h"
12 #include "SkRandom.h"
13 #include "Test.h"
14
15 static bool operator==(const SkMask& a, const SkMask& b) {
16     if (a.fFormat != b.fFormat || a.fBounds != b.fBounds) {
17         return false;
18     }
19     if (!a.fImage && !b.fImage) {
20         return true;
21     }
22     if (!a.fImage || !b.fImage) {
23         return false;
24     }
25
26     size_t wbytes = a.fBounds.width();
27     switch (a.fFormat) {
28         case SkMask::kBW_Format:
29             wbytes = (wbytes + 7) >> 3;
30             break;
31         case SkMask::kA8_Format:
32         case SkMask::k3D_Format:
33             break;
34         case SkMask::kLCD16_Format:
35             wbytes <<= 1;
36             break;
37         case SkMask::kLCD32_Format:
38         case SkMask::kARGB32_Format:
39             wbytes <<= 2;
40             break;
41         default:
42             SkDEBUGFAIL("unknown mask format");
43             return false;
44     }
45
46     const int h = a.fBounds.height();
47     const char* aptr = (const char*)a.fImage;
48     const char* bptr = (const char*)b.fImage;
49     for (int y = 0; y < h; ++y) {
50         if (memcmp(aptr, bptr, wbytes)) {
51             return false;
52         }
53         aptr += wbytes;
54         bptr += wbytes;
55     }
56     return true;
57 }
58
59 static void copyToMask(const SkRegion& rgn, SkMask* mask) {
60     mask->fFormat = SkMask::kA8_Format;
61
62     if (rgn.isEmpty()) {
63         mask->fBounds.setEmpty();
64         mask->fRowBytes = 0;
65         mask->fImage = NULL;
66         return;
67     }
68
69     mask->fBounds = rgn.getBounds();
70     mask->fRowBytes = mask->fBounds.width();
71     mask->fImage = SkMask::AllocImage(mask->computeImageSize());
72     sk_bzero(mask->fImage, mask->computeImageSize());
73
74     SkImageInfo info = SkImageInfo::Make(mask->fBounds.width(),
75                                          mask->fBounds.height(),
76                                          kAlpha_8_SkColorType,
77                                          kPremul_SkAlphaType);
78     SkBitmap bitmap;
79     bitmap.installPixels(info, mask->fImage, mask->fRowBytes);
80
81     // canvas expects its coordinate system to always be 0,0 in the top/left
82     // so we translate the rgn to match that before drawing into the mask.
83     //
84     SkRegion tmpRgn(rgn);
85     tmpRgn.translate(-rgn.getBounds().fLeft, -rgn.getBounds().fTop);
86
87     SkCanvas canvas(bitmap);
88     canvas.clipRegion(tmpRgn);
89     canvas.drawColor(SK_ColorBLACK);
90 }
91
92 static SkIRect rand_rect(SkRandom& rand, int n) {
93     int x = rand.nextS() % n;
94     int y = rand.nextS() % n;
95     int w = rand.nextU() % n;
96     int h = rand.nextU() % n;
97     return SkIRect::MakeXYWH(x, y, w, h);
98 }
99
100 static void make_rand_rgn(SkRegion* rgn, SkRandom& rand) {
101     int count = rand.nextU() % 20;
102     for (int i = 0; i < count; ++i) {
103         rgn->op(rand_rect(rand, 100), SkRegion::kXOR_Op);
104     }
105 }
106
107 static bool operator==(const SkRegion& rgn, const SkAAClip& aaclip) {
108     SkMask mask0, mask1;
109
110     copyToMask(rgn, &mask0);
111     aaclip.copyToMask(&mask1);
112     bool eq = (mask0 == mask1);
113
114     SkMask::FreeImage(mask0.fImage);
115     SkMask::FreeImage(mask1.fImage);
116     return eq;
117 }
118
119 static bool equalsAAClip(const SkRegion& rgn) {
120     SkAAClip aaclip;
121     aaclip.setRegion(rgn);
122     return rgn == aaclip;
123 }
124
125 static void setRgnToPath(SkRegion* rgn, const SkPath& path) {
126     SkIRect ir;
127     path.getBounds().round(&ir);
128     rgn->setPath(path, SkRegion(ir));
129 }
130
131 // aaclip.setRegion should create idential masks to the region
132 static void test_rgn(skiatest::Reporter* reporter) {
133     SkRandom rand;
134     for (int i = 0; i < 1000; i++) {
135         SkRegion rgn;
136         make_rand_rgn(&rgn, rand);
137         REPORTER_ASSERT(reporter, equalsAAClip(rgn));
138     }
139
140     {
141         SkRegion rgn;
142         SkPath path;
143         path.addCircle(0, 0, SkIntToScalar(30));
144         setRgnToPath(&rgn, path);
145         REPORTER_ASSERT(reporter, equalsAAClip(rgn));
146
147         path.reset();
148         path.moveTo(0, 0);
149         path.lineTo(SkIntToScalar(100), 0);
150         path.lineTo(SkIntToScalar(100 - 20), SkIntToScalar(20));
151         path.lineTo(SkIntToScalar(20), SkIntToScalar(20));
152         setRgnToPath(&rgn, path);
153         REPORTER_ASSERT(reporter, equalsAAClip(rgn));
154     }
155 }
156
157 static const SkRegion::Op gRgnOps[] = {
158     SkRegion::kDifference_Op,
159     SkRegion::kIntersect_Op,
160     SkRegion::kUnion_Op,
161     SkRegion::kXOR_Op,
162     SkRegion::kReverseDifference_Op,
163     SkRegion::kReplace_Op
164 };
165
166 static const char* gRgnOpNames[] = {
167     "DIFF", "INTERSECT", "UNION", "XOR", "REVERSE_DIFF", "REPLACE"
168 };
169
170 static void imoveTo(SkPath& path, int x, int y) {
171     path.moveTo(SkIntToScalar(x), SkIntToScalar(y));
172 }
173
174 static void icubicTo(SkPath& path, int x0, int y0, int x1, int y1, int x2, int y2) {
175     path.cubicTo(SkIntToScalar(x0), SkIntToScalar(y0),
176                  SkIntToScalar(x1), SkIntToScalar(y1),
177                  SkIntToScalar(x2), SkIntToScalar(y2));
178 }
179
180 static void test_path_bounds(skiatest::Reporter* reporter) {
181     SkPath path;
182     SkAAClip clip;
183     const int height = 40;
184     const SkScalar sheight = SkIntToScalar(height);
185
186     path.addOval(SkRect::MakeWH(sheight, sheight));
187     REPORTER_ASSERT(reporter, sheight == path.getBounds().height());
188     clip.setPath(path, NULL, true);
189     REPORTER_ASSERT(reporter, height == clip.getBounds().height());
190
191     // this is the trimmed height of this cubic (with aa). The critical thing
192     // for this test is that it is less than height, which represents just
193     // the bounds of the path's control-points.
194     //
195     // This used to fail until we tracked the MinY in the BuilderBlitter.
196     //
197     const int teardrop_height = 12;
198     path.reset();
199     imoveTo(path, 0, 20);
200     icubicTo(path, 40, 40, 40, 0, 0, 20);
201     REPORTER_ASSERT(reporter, sheight == path.getBounds().height());
202     clip.setPath(path, NULL, true);
203     REPORTER_ASSERT(reporter, teardrop_height == clip.getBounds().height());
204 }
205
206 static void test_empty(skiatest::Reporter* reporter) {
207     SkAAClip clip0, clip1;
208
209     REPORTER_ASSERT(reporter, clip0.isEmpty());
210     REPORTER_ASSERT(reporter, clip0.getBounds().isEmpty());
211     REPORTER_ASSERT(reporter, clip1 == clip0);
212
213     clip0.translate(10, 10);    // should have no effect on empty
214     REPORTER_ASSERT(reporter, clip0.isEmpty());
215     REPORTER_ASSERT(reporter, clip0.getBounds().isEmpty());
216     REPORTER_ASSERT(reporter, clip1 == clip0);
217
218     SkIRect r = { 10, 10, 40, 50 };
219     clip0.setRect(r);
220     REPORTER_ASSERT(reporter, !clip0.isEmpty());
221     REPORTER_ASSERT(reporter, !clip0.getBounds().isEmpty());
222     REPORTER_ASSERT(reporter, clip0 != clip1);
223     REPORTER_ASSERT(reporter, clip0.getBounds() == r);
224
225     clip0.setEmpty();
226     REPORTER_ASSERT(reporter, clip0.isEmpty());
227     REPORTER_ASSERT(reporter, clip0.getBounds().isEmpty());
228     REPORTER_ASSERT(reporter, clip1 == clip0);
229
230     SkMask mask;
231     mask.fImage = NULL;
232     clip0.copyToMask(&mask);
233     REPORTER_ASSERT(reporter, NULL == mask.fImage);
234     REPORTER_ASSERT(reporter, mask.fBounds.isEmpty());
235 }
236
237 static void rand_irect(SkIRect* r, int N, SkRandom& rand) {
238     r->setXYWH(0, 0, rand.nextU() % N, rand.nextU() % N);
239     int dx = rand.nextU() % (2*N);
240     int dy = rand.nextU() % (2*N);
241     // use int dx,dy to make the subtract be signed
242     r->offset(N - dx, N - dy);
243 }
244
245 static void test_irect(skiatest::Reporter* reporter) {
246     SkRandom rand;
247
248     for (int i = 0; i < 10000; i++) {
249         SkAAClip clip0, clip1;
250         SkRegion rgn0, rgn1;
251         SkIRect r0, r1;
252
253         rand_irect(&r0, 10, rand);
254         rand_irect(&r1, 10, rand);
255         clip0.setRect(r0);
256         clip1.setRect(r1);
257         rgn0.setRect(r0);
258         rgn1.setRect(r1);
259         for (size_t j = 0; j < SK_ARRAY_COUNT(gRgnOps); ++j) {
260             SkRegion::Op op = gRgnOps[j];
261             SkAAClip clip2;
262             SkRegion rgn2;
263             bool nonEmptyAA = clip2.op(clip0, clip1, op);
264             bool nonEmptyBW = rgn2.op(rgn0, rgn1, op);
265             if (nonEmptyAA != nonEmptyBW || clip2.getBounds() != rgn2.getBounds()) {
266                 SkDebugf("[%d %d %d %d] %s [%d %d %d %d] = BW:[%d %d %d %d] AA:[%d %d %d %d]\n",
267                          r0.fLeft, r0.fTop, r0.right(), r0.bottom(),
268                          gRgnOpNames[j],
269                          r1.fLeft, r1.fTop, r1.right(), r1.bottom(),
270                          rgn2.getBounds().fLeft, rgn2.getBounds().fTop,
271                          rgn2.getBounds().right(), rgn2.getBounds().bottom(),
272                          clip2.getBounds().fLeft, clip2.getBounds().fTop,
273                          clip2.getBounds().right(), clip2.getBounds().bottom());
274             }
275             REPORTER_ASSERT(reporter, nonEmptyAA == nonEmptyBW);
276             REPORTER_ASSERT(reporter, clip2.getBounds() == rgn2.getBounds());
277
278             SkMask maskBW, maskAA;
279             copyToMask(rgn2, &maskBW);
280             clip2.copyToMask(&maskAA);
281             SkAutoMaskFreeImage freeBW(maskBW.fImage);
282             SkAutoMaskFreeImage freeAA(maskAA.fImage);
283             REPORTER_ASSERT(reporter, maskBW == maskAA);
284         }
285     }
286 }
287
288 static void test_path_with_hole(skiatest::Reporter* reporter) {
289     static const uint8_t gExpectedImage[] = {
290         0xFF, 0xFF, 0xFF, 0xFF,
291         0xFF, 0xFF, 0xFF, 0xFF,
292         0x00, 0x00, 0x00, 0x00,
293         0x00, 0x00, 0x00, 0x00,
294         0xFF, 0xFF, 0xFF, 0xFF,
295         0xFF, 0xFF, 0xFF, 0xFF,
296     };
297     SkMask expected;
298     expected.fBounds.set(0, 0, 4, 6);
299     expected.fRowBytes = 4;
300     expected.fFormat = SkMask::kA8_Format;
301     expected.fImage = (uint8_t*)gExpectedImage;
302
303     SkPath path;
304     path.addRect(SkRect::MakeXYWH(0, 0,
305                                   SkIntToScalar(4), SkIntToScalar(2)));
306     path.addRect(SkRect::MakeXYWH(0, SkIntToScalar(4),
307                                   SkIntToScalar(4), SkIntToScalar(2)));
308
309     for (int i = 0; i < 2; ++i) {
310         SkAAClip clip;
311         clip.setPath(path, NULL, 1 == i);
312
313         SkMask mask;
314         clip.copyToMask(&mask);
315         SkAutoMaskFreeImage freeM(mask.fImage);
316
317         REPORTER_ASSERT(reporter, expected == mask);
318     }
319 }
320
321 static void test_really_a_rect(skiatest::Reporter* reporter) {
322     SkRRect rrect;
323     rrect.setRectXY(SkRect::MakeWH(100, 100), 5, 5);
324
325     SkPath path;
326     path.addRRect(rrect);
327
328     SkAAClip clip;
329     clip.setPath(path);
330
331     REPORTER_ASSERT(reporter, clip.getBounds() == SkIRect::MakeWH(100, 100));
332     REPORTER_ASSERT(reporter, !clip.isRect());
333
334     // This rect should intersect the clip, but slice-out all of the "soft" parts,
335     // leaving just a rect.
336     const SkIRect ir = SkIRect::MakeLTRB(10, -10, 50, 90);
337     
338     clip.op(ir, SkRegion::kIntersect_Op);
339
340     REPORTER_ASSERT(reporter, clip.getBounds() == SkIRect::MakeLTRB(10, 0, 50, 90));
341     // the clip recognized that that it is just a rect!
342     REPORTER_ASSERT(reporter, clip.isRect());
343 }
344
345 #include "SkRasterClip.h"
346
347 static void copyToMask(const SkRasterClip& rc, SkMask* mask) {
348     if (rc.isAA()) {
349         rc.aaRgn().copyToMask(mask);
350     } else {
351         copyToMask(rc.bwRgn(), mask);
352     }
353 }
354
355 static bool operator==(const SkRasterClip& a, const SkRasterClip& b) {
356     if (a.isEmpty()) {
357         return b.isEmpty();
358     }
359     if (b.isEmpty()) {
360         return false;
361     }
362
363     SkMask ma, mb;
364     copyToMask(a, &ma);
365     copyToMask(b, &mb);
366     SkAutoMaskFreeImage aCleanUp(ma.fImage);
367     SkAutoMaskFreeImage bCleanUp(mb.fImage);
368
369     return ma == mb;
370 }
371
372 static void did_dx_affect(skiatest::Reporter* reporter, const SkScalar dx[],
373                           size_t count, bool changed) {
374     SkIRect ir = { 0, 0, 10, 10 };
375
376     for (size_t i = 0; i < count; ++i) {
377         SkRect r;
378         r.set(ir);
379
380         SkRasterClip rc0(ir);
381         SkRasterClip rc1(ir);
382         SkRasterClip rc2(ir);
383
384         rc0.op(r, SkRegion::kIntersect_Op, false);
385         r.offset(dx[i], 0);
386         rc1.op(r, SkRegion::kIntersect_Op, true);
387         r.offset(-2*dx[i], 0);
388         rc2.op(r, SkRegion::kIntersect_Op, true);
389
390         REPORTER_ASSERT(reporter, changed != (rc0 == rc1));
391         REPORTER_ASSERT(reporter, changed != (rc0 == rc2));
392     }
393 }
394
395 static void test_nearly_integral(skiatest::Reporter* reporter) {
396     // All of these should generate equivalent rasterclips
397
398     static const SkScalar gSafeX[] = {
399         0, SK_Scalar1/1000, SK_Scalar1/100, SK_Scalar1/10,
400     };
401     did_dx_affect(reporter, gSafeX, SK_ARRAY_COUNT(gSafeX), false);
402
403     static const SkScalar gUnsafeX[] = {
404         SK_Scalar1/4, SK_Scalar1/3,
405     };
406     did_dx_affect(reporter, gUnsafeX, SK_ARRAY_COUNT(gUnsafeX), true);
407 }
408
409 static void test_regressions() {
410     // these should not assert in the debug build
411     // bug was introduced in rev. 3209
412     {
413         SkAAClip clip;
414         SkRect r;
415         r.fLeft = 129.892181f;
416         r.fTop = 10.3999996f;
417         r.fRight = 130.892181f;
418         r.fBottom = 20.3999996f;
419         clip.setRect(r, true);
420     }
421 }
422
423 DEF_TEST(AAClip, reporter) {
424     test_empty(reporter);
425     test_path_bounds(reporter);
426     test_irect(reporter);
427     test_rgn(reporter);
428     test_path_with_hole(reporter);
429     test_regressions();
430     test_nearly_integral(reporter);
431     test_really_a_rect(reporter);
432 }