1 /* libs/graphics/sgl/SkScan_Hairline.cpp
2 **
3 ** Copyright 2006, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 ** http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17
18 #include "SkScan.h"
19 #include "SkBlitter.h"
20 #include "SkRegion.h"
21 #include "SkFDot6.h"
22
horiline(int x,int stopx,SkFixed fy,SkFixed dy,SkBlitter * blitter)23 static void horiline(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitter)
24 {
25 SkASSERT(x < stopx);
26
27 do {
28 blitter->blitH(x, fy >> 16, 1);
29 fy += dy;
30 } while (++x < stopx);
31 }
32
vertline(int y,int stopy,SkFixed fx,SkFixed dx,SkBlitter * blitter)33 static void vertline(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitter)
34 {
35 SkASSERT(y < stopy);
36
37 do {
38 blitter->blitH(fx >> 16, y, 1);
39 fx += dx;
40 } while (++y < stopy);
41 }
42
HairLine(const SkPoint & pt0,const SkPoint & pt1,const SkRegion * clip,SkBlitter * blitter)43 void SkScan::HairLine(const SkPoint& pt0, const SkPoint& pt1, const SkRegion* clip, SkBlitter* blitter)
44 {
45 SkBlitterClipper clipper;
46
47 SkFDot6 x0 = SkScalarToFDot6(pt0.fX);
48 SkFDot6 y0 = SkScalarToFDot6(pt0.fY);
49 SkFDot6 x1 = SkScalarToFDot6(pt1.fX);
50 SkFDot6 y1 = SkScalarToFDot6(pt1.fY);
51
52 if (clip)
53 {
54 SkRect r;
55 SkIRect ir;
56 SkPoint pts[2];
57
58 pts[0] = pt0;
59 pts[1] = pt1;
60 r.set(pts, 2);
61 r.roundOut(&ir);
62
63 // if we're given a horizontal or vertical line
64 // this rect could be empty (in area), in which case
65 // clip->quickReject() will always return true.
66 // hence we bloat the rect to avoid that case
67 if (ir.width() == 0)
68 ir.fRight += 1;
69 if (ir.height() == 0)
70 ir.fBottom += 1;
71
72 if (clip->quickReject(ir))
73 return;
74 if (clip->quickContains(ir))
75 clip = NULL;
76 else
77 {
78 blitter = clipper.apply(blitter, clip);
79 }
80 }
81
82 SkFDot6 dx = x1 - x0;
83 SkFDot6 dy = y1 - y0;
84
85 if (SkAbs32(dx) > SkAbs32(dy)) // mostly horizontal
86 {
87 if (x0 > x1) // we want to go left-to-right
88 {
89 SkTSwap<SkFDot6>(x0, x1);
90 SkTSwap<SkFDot6>(y0, y1);
91 }
92 int ix0 = SkFDot6Round(x0);
93 int ix1 = SkFDot6Round(x1);
94 if (ix0 == ix1) // too short to draw
95 return;
96
97 SkFixed slope = SkFixedDiv(dy, dx);
98 SkFixed startY = SkFDot6ToFixed(y0) + (slope * ((32 - x0) & 63) >> 6);
99
100 horiline(ix0, ix1, startY, slope, blitter);
101 }
102 else // mostly vertical
103 {
104 if (y0 > y1) // we want to go top-to-bottom
105 {
106 SkTSwap<SkFDot6>(x0, x1);
107 SkTSwap<SkFDot6>(y0, y1);
108 }
109 int iy0 = SkFDot6Round(y0);
110 int iy1 = SkFDot6Round(y1);
111 if (iy0 == iy1) // too short to draw
112 return;
113
114 SkFixed slope = SkFixedDiv(dx, dy);
115 SkFixed startX = SkFDot6ToFixed(x0) + (slope * ((32 - y0) & 63) >> 6);
116
117 vertline(iy0, iy1, startX, slope, blitter);
118 }
119 }
120
121 // we don't just draw 4 lines, 'cause that can leave a gap in the bottom-right
122 // and double-hit the top-left.
HairRect(const SkRect & rect,const SkRegion * clip,SkBlitter * blitter)123 void SkScan::HairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter)
124 {
125 SkBlitterClipper clipper;
126 SkIRect r;
127
128 r.set(SkScalarToFixed(rect.fLeft) >> 16,
129 SkScalarToFixed(rect.fTop) >> 16,
130 (SkScalarToFixed(rect.fRight) >> 16) + 1,
131 (SkScalarToFixed(rect.fBottom) >> 16) + 1);
132
133 if (clip)
134 {
135 if (clip->quickReject(r))
136 return;
137 if (!clip->quickContains(r))
138 blitter = clipper.apply(blitter, clip);
139 }
140
141 int width = r.width();
142 int height = r.height();
143
144 if ((width | height) == 0)
145 return;
146 if (width <= 2 || height <= 2)
147 {
148 blitter->blitRect(r.fLeft, r.fTop, width, height);
149 return;
150 }
151 // if we get here, we know we have 4 segments to draw
152 blitter->blitH(r.fLeft, r.fTop, width); // top
153 blitter->blitRect(r.fLeft, r.fTop + 1, 1, height - 2); // left
154 blitter->blitRect(r.fRight - 1, r.fTop + 1, 1, height - 2); // right
155 blitter->blitH(r.fLeft, r.fBottom - 1, width); // bottom
156 }
157
158 /////////////////////////////////////////////////////////////////////////////////////////////////
159
160 #include "SkPath.h"
161 #include "SkGeometry.h"
162
quad_too_curvy(const SkPoint pts[3])163 static bool quad_too_curvy(const SkPoint pts[3])
164 {
165 return true;
166 }
167
compute_int_quad_dist(const SkPoint pts[3])168 static int compute_int_quad_dist(const SkPoint pts[3]) {
169 // compute the vector between the control point ([1]) and the middle of the
170 // line connecting the start and end ([0] and [2])
171 SkScalar dx = SkScalarHalf(pts[0].fX + pts[2].fX) - pts[1].fX;
172 SkScalar dy = SkScalarHalf(pts[0].fY + pts[2].fY) - pts[1].fY;
173 // we want everyone to be positive
174 dx = SkScalarAbs(dx);
175 dy = SkScalarAbs(dy);
176 // convert to whole pixel values (use ceiling to be conservative)
177 int idx = SkScalarCeil(dx);
178 int idy = SkScalarCeil(dy);
179 // use the cheap approx for distance
180 if (idx > idy) {
181 return idx + (idy >> 1);
182 } else {
183 return idy + (idx >> 1);
184 }
185 }
186
hairquad(const SkPoint pts[3],const SkRegion * clip,SkBlitter * blitter,int level,void (* lineproc)(const SkPoint &,const SkPoint &,const SkRegion * clip,SkBlitter *))187 static void hairquad(const SkPoint pts[3], const SkRegion* clip, SkBlitter* blitter, int level,
188 void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*))
189 {
190 #if 1
191 if (level > 0 && quad_too_curvy(pts))
192 {
193 SkPoint tmp[5];
194
195 SkChopQuadAtHalf(pts, tmp);
196 hairquad(tmp, clip, blitter, level - 1, lineproc);
197 hairquad(&tmp[2], clip, blitter, level - 1, lineproc);
198 }
199 else
200 lineproc(pts[0], pts[2], clip, blitter);
201 #else
202 int count = 1 << level;
203 const SkScalar dt = SkFixedToScalar(SK_Fixed1 >> level);
204 SkScalar t = dt;
205 SkPoint prevPt = pts[0];
206 for (int i = 1; i < count; i++) {
207 SkPoint nextPt;
208 SkEvalQuadAt(pts, t, &nextPt);
209 lineproc(prevPt, nextPt, clip, blitter);
210 t += dt;
211 prevPt = nextPt;
212 }
213 // draw the last line explicitly to 1.0, in case t didn't match that exactly
214 lineproc(prevPt, pts[2], clip, blitter);
215 #endif
216 }
217
cubic_too_curvy(const SkPoint pts[4])218 static bool cubic_too_curvy(const SkPoint pts[4])
219 {
220 return true;
221 }
222
haircubic(const SkPoint pts[4],const SkRegion * clip,SkBlitter * blitter,int level,void (* lineproc)(const SkPoint &,const SkPoint &,const SkRegion *,SkBlitter *))223 static void haircubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter, int level,
224 void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*))
225 {
226 if (level > 0 && cubic_too_curvy(pts))
227 {
228 SkPoint tmp[7];
229
230 SkChopCubicAt(pts, tmp, SK_Scalar1/2);
231 haircubic(tmp, clip, blitter, level - 1, lineproc);
232 haircubic(&tmp[3], clip, blitter, level - 1, lineproc);
233 }
234 else
235 lineproc(pts[0], pts[3], clip, blitter);
236 }
237
238 #define kMaxCubicSubdivideLevel 6
239 #define kMaxQuadSubdivideLevel 5
240
hair_path(const SkPath & path,const SkRegion * clip,SkBlitter * blitter,void (* lineproc)(const SkPoint &,const SkPoint &,const SkRegion *,SkBlitter *))241 static void hair_path(const SkPath& path, const SkRegion* clip, SkBlitter* blitter,
242 void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*))
243 {
244 if (path.isEmpty())
245 return;
246
247 const SkIRect* clipR = NULL;
248
249 if (clip)
250 {
251 SkIRect ibounds;
252 path.getBounds().roundOut(&ibounds);
253 ibounds.inset(-1, -1);
254
255 if (clip->quickReject(ibounds))
256 return;
257
258 if (clip->quickContains(ibounds))
259 clip = NULL;
260 else
261 clipR = &clip->getBounds();
262 }
263
264 SkPath::Iter iter(path, false);
265 SkPoint pts[4];
266 SkPath::Verb verb;
267
268 while ((verb = iter.next(pts)) != SkPath::kDone_Verb)
269 {
270 switch (verb) {
271 case SkPath::kLine_Verb:
272 lineproc(pts[0], pts[1], clip, blitter);
273 break;
274 case SkPath::kQuad_Verb: {
275 int d = compute_int_quad_dist(pts);
276 /* quadratics approach the line connecting their start and end points
277 4x closer with each subdivision, so we compute the number of
278 subdivisions to be the minimum need to get that distance to be less
279 than a pixel.
280 */
281 int level = (33 - SkCLZ(d)) >> 1;
282 // SkDebugf("----- distance %d computedLevel %d\n", d, computedLevel);
283 // sanity check on level (from the previous version)
284 if (level > kMaxQuadSubdivideLevel) {
285 level = kMaxQuadSubdivideLevel;
286 }
287 hairquad(pts, clip, blitter, level, lineproc);
288 break;
289 }
290 case SkPath::kCubic_Verb:
291 haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc);
292 break;
293 default:
294 break;
295 }
296 }
297 }
298
HairPath(const SkPath & path,const SkRegion * clip,SkBlitter * blitter)299 void SkScan::HairPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitter)
300 {
301 hair_path(path, clip, blitter, SkScan::HairLine);
302 }
303
AntiHairPath(const SkPath & path,const SkRegion * clip,SkBlitter * blitter)304 void SkScan::AntiHairPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitter)
305 {
306 hair_path(path, clip, blitter, SkScan::AntiHairLine);
307 }
308
309 ////////////////////////////////////////////////////////////////////////////////
310
FrameRect(const SkRect & r,SkScalar diameter,const SkRegion * clip,SkBlitter * blitter)311 void SkScan::FrameRect(const SkRect& r, SkScalar diameter, const SkRegion* clip, SkBlitter* blitter)
312 {
313 SkASSERT(diameter > 0);
314
315 if (r.isEmpty())
316 return;
317
318 SkScalar radius = diameter / 2;
319 SkRect outer, tmp;
320
321 outer.set( r.fLeft - radius, r.fTop - radius,
322 r.fRight + radius, r.fBottom + radius);
323
324 if (r.width() <= diameter || r.height() <= diameter)
325 {
326 SkScan::FillRect(outer, clip, blitter);
327 return;
328 }
329
330 tmp.set(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + diameter);
331 SkScan::FillRect(tmp, clip, blitter);
332 tmp.fTop = outer.fBottom - diameter;
333 tmp.fBottom = outer.fBottom;
334 SkScan::FillRect(tmp, clip, blitter);
335
336 tmp.set(outer.fLeft, outer.fTop + diameter, outer.fLeft + diameter, outer.fBottom - diameter);
337 SkScan::FillRect(tmp, clip, blitter);
338 tmp.fLeft = outer.fRight - diameter;
339 tmp.fRight = outer.fRight;
340 SkScan::FillRect(tmp, clip, blitter);
341 }
342
343