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 "SkCanvas.h"
9 #include "SkEdgeClipper.h"
10 #include "SkLineClipper.h"
11 #include "SkPath.h"
12 #include "Test.h"
13 
test_hairclipping(skiatest::Reporter * reporter)14 static void test_hairclipping(skiatest::Reporter* reporter) {
15     SkBitmap bm;
16     bm.allocN32Pixels(4, 4);
17     bm.eraseColor(SK_ColorWHITE);
18 
19     SkPaint paint;
20     paint.setAntiAlias(true);
21 
22     SkCanvas canvas(bm);
23     canvas.clipRect(SkRect::MakeWH(SkIntToScalar(4), SkIntToScalar(2)));
24     canvas.drawLine(1.5f, 1.5f,
25                     3.5f, 3.5f, paint);
26 
27     /**
28      *  We had a bug where we misinterpreted the bottom of the clip, and
29      *  would draw another pixel (to the right in this case) on the same
30      *  last scanline. i.e. we would draw to [2,1], even though this hairline
31      *  should just draw to [1,1], [2,2], [3,3] modulo the clip.
32      *
33      *  The result of this entire draw should be that we only draw to [1,1]
34      *
35      *  Fixed in rev. 3366
36      */
37     for (int y = 0; y < 4; ++y) {
38         for (int x = 0; x < 4; ++x) {
39             bool nonWhite = (1 == y) && (1 == x);
40             SkPMColor c = *bm.getAddr32(x, y);
41             if (nonWhite) {
42                 REPORTER_ASSERT(reporter, 0xFFFFFFFF != c);
43             } else {
44                 REPORTER_ASSERT(reporter, 0xFFFFFFFF == c);
45             }
46         }
47     }
48 }
49 
test_edgeclipper()50 static void test_edgeclipper() {
51     SkEdgeClipper clipper(false);
52 
53     const SkPoint pts[] = {
54         { 3.0995476e+010f,  42.929779f },
55         { -3.0995163e+010f, 51.050385f },
56         { -3.0995157e+010f, 51.050392f },
57         { -3.0995134e+010f, 51.050400f },
58     };
59 
60     const SkRect clip = { 0, 0, SkIntToScalar(300), SkIntToScalar(200) };
61 
62     // this should not assert, even though our choppers do a poor numerical
63     // job when computing their t values.
64     // http://code.google.com/p/skia/issues/detail?id=444
65     clipper.clipCubic(pts, clip);
66 }
67 
test_intersectline(skiatest::Reporter * reporter)68 static void test_intersectline(skiatest::Reporter* reporter) {
69     static const SkScalar L = 0;
70     static const SkScalar T = 0;
71     static const SkScalar R = SkIntToScalar(100);
72     static const SkScalar B = SkIntToScalar(100);
73     static const SkScalar CX = SkScalarHalf(L + R);
74     static const SkScalar CY = SkScalarHalf(T + B);
75     static const SkRect gR = { L, T, R, B };
76 
77     size_t i;
78     SkPoint dst[2];
79 
80     static const SkPoint gEmpty[] = {
81         // sides
82         { L, CY }, { L - 10, CY },
83         { R, CY }, { R + 10, CY },
84         { CX, T }, { CX, T - 10 },
85         { CX, B }, { CX, B + 10 },
86         // corners
87         { L, T }, { L - 10, T - 10 },
88         { L, B }, { L - 10, B + 10 },
89         { R, T }, { R + 10, T - 10 },
90         { R, B }, { R + 10, B + 10 },
91     };
92     for (i = 0; i < SK_ARRAY_COUNT(gEmpty); i += 2) {
93         bool valid = SkLineClipper::IntersectLine(&gEmpty[i], gR, dst);
94         if (valid) {
95             SkDebugf("----- [%d] %g %g -> %g %g\n", i/2, dst[0].fX, dst[0].fY, dst[1].fX, dst[1].fY);
96         }
97         REPORTER_ASSERT(reporter, !valid);
98     }
99 
100     static const SkPoint gFull[] = {
101         // diagonals, chords
102         { L, T }, { R, B },
103         { L, B }, { R, T },
104         { CX, T }, { CX, B },
105         { L, CY }, { R, CY },
106         { CX, T }, { R, CY },
107         { CX, T }, { L, CY },
108         { L, CY }, { CX, B },
109         { R, CY }, { CX, B },
110         // edges
111         { L, T }, { L, B },
112         { R, T }, { R, B },
113         { L, T }, { R, T },
114         { L, B }, { R, B },
115     };
116     for (i = 0; i < SK_ARRAY_COUNT(gFull); i += 2) {
117         bool valid = SkLineClipper::IntersectLine(&gFull[i], gR, dst);
118         if (!valid || memcmp(&gFull[i], dst, sizeof(dst))) {
119             SkDebugf("++++ [%d] %g %g -> %g %g\n", i/2, dst[0].fX, dst[0].fY, dst[1].fX, dst[1].fY);
120         }
121         REPORTER_ASSERT(reporter, valid && !memcmp(&gFull[i], dst, sizeof(dst)));
122     }
123 
124     static const SkPoint gPartial[] = {
125         { L - 10, CY }, { CX, CY }, { L, CY }, { CX, CY },
126         { CX, T - 10 }, { CX, CY }, { CX, T }, { CX, CY },
127         { R + 10, CY }, { CX, CY }, { R, CY }, { CX, CY },
128         { CX, B + 10 }, { CX, CY }, { CX, B }, { CX, CY },
129         // extended edges
130         { L, T - 10 }, { L, B + 10 }, { L, T }, { L, B },
131         { R, T - 10 }, { R, B + 10 }, { R, T }, { R, B },
132         { L - 10, T }, { R + 10, T }, { L, T }, { R, T },
133         { L - 10, B }, { R + 10, B }, { L, B }, { R, B },
134     };
135     for (i = 0; i < SK_ARRAY_COUNT(gPartial); i += 4) {
136         bool valid = SkLineClipper::IntersectLine(&gPartial[i], gR, dst);
137         if (!valid || memcmp(&gPartial[i+2], dst, sizeof(dst))) {
138             SkDebugf("++++ [%d] %g %g -> %g %g\n", i/2, dst[0].fX, dst[0].fY, dst[1].fX, dst[1].fY);
139         }
140         REPORTER_ASSERT(reporter, valid &&
141                                   !memcmp(&gPartial[i+2], dst, sizeof(dst)));
142     }
143 
144 }
145 
DEF_TEST(Clipper,reporter)146 DEF_TEST(Clipper, reporter) {
147     test_intersectline(reporter);
148     test_edgeclipper();
149     test_hairclipping(reporter);
150 }
151