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