1 :
2 : /*
3 : * Copyright 2006 The Android Open Source Project
4 : *
5 : * Use of this source code is governed by a BSD-style license that can be
6 : * found in the LICENSE file.
7 : */
8 :
9 :
10 : #include "SkScan.h"
11 : #include "SkBlitter.h"
12 : #include "SkRasterClip.h"
13 : #include "SkFDot6.h"
14 : #include "SkLineClipper.h"
15 :
16 0 : static void horiline(int x, int stopx, SkFixed fy, SkFixed dy,
17 : SkBlitter* blitter) {
18 0 : SkASSERT(x < stopx);
19 :
20 0 : do {
21 0 : blitter->blitH(x, fy >> 16, 1);
22 0 : fy += dy;
23 : } while (++x < stopx);
24 0 : }
25 :
26 0 : static void vertline(int y, int stopy, SkFixed fx, SkFixed dx,
27 : SkBlitter* blitter) {
28 0 : SkASSERT(y < stopy);
29 :
30 0 : do {
31 0 : blitter->blitH(fx >> 16, y, 1);
32 0 : fx += dx;
33 : } while (++y < stopy);
34 0 : }
35 :
36 0 : void SkScan::HairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
37 : const SkRegion* clip, SkBlitter* blitter) {
38 0 : SkBlitterClipper clipper;
39 : SkRect r;
40 : SkIRect clipR, ptsR;
41 0 : SkPoint pts[2] = { pt0, pt1 };
42 :
43 0 : if (clip) {
44 : // Perform a clip in scalar space, so we catch huge values which might
45 : // be missed after we convert to SkFDot6 (overflow)
46 0 : r.set(clip->getBounds());
47 0 : if (!SkLineClipper::IntersectLine(pts, r, pts)) {
48 : return;
49 : }
50 : }
51 :
52 0 : SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
53 0 : SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
54 0 : SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
55 0 : SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
56 :
57 0 : if (clip) {
58 : // now perform clipping again, as the rounding to dot6 can wiggle us
59 : // our rects are really dot6 rects, but since we've already used
60 : // lineclipper, we know they will fit in 32bits (26.6)
61 0 : const SkIRect& bounds = clip->getBounds();
62 :
63 : clipR.set(SkIntToFDot6(bounds.fLeft), SkIntToFDot6(bounds.fTop),
64 0 : SkIntToFDot6(bounds.fRight), SkIntToFDot6(bounds.fBottom));
65 0 : ptsR.set(x0, y0, x1, y1);
66 0 : ptsR.sort();
67 :
68 : // outset the right and bottom, to account for how hairlines are
69 : // actually drawn, which may hit the pixel to the right or below of
70 : // the coordinate
71 0 : ptsR.fRight += SK_FDot6One;
72 0 : ptsR.fBottom += SK_FDot6One;
73 :
74 0 : if (!SkIRect::Intersects(ptsR, clipR)) {
75 : return;
76 : }
77 0 : if (clip->isRect() && clipR.contains(ptsR)) {
78 0 : clip = NULL;
79 : } else {
80 0 : blitter = clipper.apply(blitter, clip);
81 : }
82 : }
83 :
84 0 : SkFDot6 dx = x1 - x0;
85 0 : SkFDot6 dy = y1 - y0;
86 :
87 0 : if (SkAbs32(dx) > SkAbs32(dy)) { // mostly horizontal
88 0 : if (x0 > x1) { // we want to go left-to-right
89 0 : SkTSwap<SkFDot6>(x0, x1);
90 0 : SkTSwap<SkFDot6>(y0, y1);
91 : }
92 0 : int ix0 = SkFDot6Round(x0);
93 0 : int ix1 = SkFDot6Round(x1);
94 0 : if (ix0 == ix1) {// too short to draw
95 : return;
96 : }
97 :
98 0 : SkFixed slope = SkFixedDiv(dy, dx);
99 0 : SkFixed startY = SkFDot6ToFixed(y0) + (slope * ((32 - x0) & 63) >> 6);
100 :
101 0 : horiline(ix0, ix1, startY, slope, blitter);
102 : } else { // mostly vertical
103 0 : if (y0 > y1) { // we want to go top-to-bottom
104 0 : SkTSwap<SkFDot6>(x0, x1);
105 0 : SkTSwap<SkFDot6>(y0, y1);
106 : }
107 0 : int iy0 = SkFDot6Round(y0);
108 0 : int iy1 = SkFDot6Round(y1);
109 0 : if (iy0 == iy1) { // too short to draw
110 : return;
111 : }
112 :
113 0 : SkFixed slope = SkFixedDiv(dx, dy);
114 0 : SkFixed startX = SkFDot6ToFixed(x0) + (slope * ((32 - y0) & 63) >> 6);
115 :
116 0 : vertline(iy0, iy1, startX, slope, blitter);
117 : }
118 : }
119 :
120 : // we don't just draw 4 lines, 'cause that can leave a gap in the bottom-right
121 : // and double-hit the top-left.
122 : // TODO: handle huge coordinates on rect (before calling SkScalarToFixed)
123 0 : void SkScan::HairRect(const SkRect& rect, const SkRasterClip& clip,
124 : SkBlitter* blitter) {
125 0 : SkAAClipBlitterWrapper wrapper;
126 0 : SkBlitterClipper clipper;
127 : SkIRect r;
128 :
129 : r.set(SkScalarToFixed(rect.fLeft) >> 16,
130 : SkScalarToFixed(rect.fTop) >> 16,
131 : (SkScalarToFixed(rect.fRight) >> 16) + 1,
132 0 : (SkScalarToFixed(rect.fBottom) >> 16) + 1);
133 :
134 0 : if (clip.quickReject(r)) {
135 : return;
136 : }
137 0 : if (!clip.quickContains(r)) {
138 : const SkRegion* clipRgn;
139 0 : if (clip.isBW()) {
140 0 : clipRgn = &clip.bwRgn();
141 : } else {
142 0 : wrapper.init(clip, blitter);
143 0 : clipRgn = &wrapper.getRgn();
144 0 : blitter = wrapper.getBlitter();
145 : }
146 0 : blitter = clipper.apply(blitter, clipRgn);
147 : }
148 :
149 0 : int width = r.width();
150 0 : int height = r.height();
151 :
152 0 : if ((width | height) == 0) {
153 : return;
154 : }
155 0 : if (width <= 2 || height <= 2) {
156 0 : blitter->blitRect(r.fLeft, r.fTop, width, height);
157 : return;
158 : }
159 : // if we get here, we know we have 4 segments to draw
160 0 : blitter->blitH(r.fLeft, r.fTop, width); // top
161 0 : blitter->blitRect(r.fLeft, r.fTop + 1, 1, height - 2); // left
162 0 : blitter->blitRect(r.fRight - 1, r.fTop + 1, 1, height - 2); // right
163 0 : blitter->blitH(r.fLeft, r.fBottom - 1, width); // bottom
164 : }
165 :
166 : ///////////////////////////////////////////////////////////////////////////////
167 :
168 : #include "SkPath.h"
169 : #include "SkGeometry.h"
170 :
171 0 : static bool quad_too_curvy(const SkPoint pts[3]) {
172 0 : return true;
173 : }
174 :
175 0 : static int compute_int_quad_dist(const SkPoint pts[3]) {
176 : // compute the vector between the control point ([1]) and the middle of the
177 : // line connecting the start and end ([0] and [2])
178 0 : SkScalar dx = SkScalarHalf(pts[0].fX + pts[2].fX) - pts[1].fX;
179 0 : SkScalar dy = SkScalarHalf(pts[0].fY + pts[2].fY) - pts[1].fY;
180 : // we want everyone to be positive
181 0 : dx = SkScalarAbs(dx);
182 0 : dy = SkScalarAbs(dy);
183 : // convert to whole pixel values (use ceiling to be conservative)
184 0 : int idx = SkScalarCeil(dx);
185 0 : int idy = SkScalarCeil(dy);
186 : // use the cheap approx for distance
187 0 : if (idx > idy) {
188 0 : return idx + (idy >> 1);
189 : } else {
190 0 : return idy + (idx >> 1);
191 : }
192 : }
193 :
194 0 : static void hairquad(const SkPoint pts[3], const SkRegion* clip, SkBlitter* blitter, int level,
195 : void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*))
196 : {
197 : #if 1
198 0 : if (level > 0 && quad_too_curvy(pts))
199 : {
200 : SkPoint tmp[5];
201 :
202 0 : SkChopQuadAtHalf(pts, tmp);
203 0 : hairquad(tmp, clip, blitter, level - 1, lineproc);
204 0 : hairquad(&tmp[2], clip, blitter, level - 1, lineproc);
205 : }
206 : else
207 0 : lineproc(pts[0], pts[2], clip, blitter);
208 : #else
209 : int count = 1 << level;
210 : const SkScalar dt = SkFixedToScalar(SK_Fixed1 >> level);
211 : SkScalar t = dt;
212 : SkPoint prevPt = pts[0];
213 : for (int i = 1; i < count; i++) {
214 : SkPoint nextPt;
215 : SkEvalQuadAt(pts, t, &nextPt);
216 : lineproc(prevPt, nextPt, clip, blitter);
217 : t += dt;
218 : prevPt = nextPt;
219 : }
220 : // draw the last line explicitly to 1.0, in case t didn't match that exactly
221 : lineproc(prevPt, pts[2], clip, blitter);
222 : #endif
223 0 : }
224 :
225 0 : static bool cubic_too_curvy(const SkPoint pts[4])
226 : {
227 0 : return true;
228 : }
229 :
230 0 : static void haircubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter, int level,
231 : void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*))
232 : {
233 0 : if (level > 0 && cubic_too_curvy(pts))
234 : {
235 : SkPoint tmp[7];
236 :
237 0 : SkChopCubicAt(pts, tmp, SK_Scalar1/2);
238 0 : haircubic(tmp, clip, blitter, level - 1, lineproc);
239 0 : haircubic(&tmp[3], clip, blitter, level - 1, lineproc);
240 : }
241 : else
242 0 : lineproc(pts[0], pts[3], clip, blitter);
243 0 : }
244 :
245 : #define kMaxCubicSubdivideLevel 6
246 : #define kMaxQuadSubdivideLevel 5
247 :
248 0 : static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter,
249 : void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*))
250 : {
251 0 : if (path.isEmpty()) {
252 0 : return;
253 : }
254 :
255 0 : SkAAClipBlitterWrapper wrap;
256 0 : const SkIRect* clipR = NULL;
257 0 : const SkRegion* clip = NULL;
258 :
259 : {
260 : SkIRect ibounds;
261 0 : path.getBounds().roundOut(&ibounds);
262 0 : ibounds.inset(-1, -1);
263 :
264 0 : if (rclip.quickReject(ibounds)) {
265 : return;
266 : }
267 0 : if (!rclip.quickContains(ibounds)) {
268 0 : clipR = &rclip.getBounds();
269 0 : if (rclip.isBW()) {
270 0 : clip = &rclip.bwRgn();
271 : } else {
272 0 : wrap.init(rclip, blitter);
273 0 : blitter = wrap.getBlitter();
274 0 : clip = &wrap.getRgn();
275 : }
276 : }
277 : }
278 :
279 0 : SkPath::Iter iter(path, false);
280 : SkPoint pts[4];
281 : SkPath::Verb verb;
282 :
283 0 : while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
284 0 : switch (verb) {
285 : case SkPath::kLine_Verb:
286 0 : lineproc(pts[0], pts[1], clip, blitter);
287 0 : break;
288 : case SkPath::kQuad_Verb: {
289 0 : int d = compute_int_quad_dist(pts);
290 : /* quadratics approach the line connecting their start and end points
291 : 4x closer with each subdivision, so we compute the number of
292 : subdivisions to be the minimum need to get that distance to be less
293 : than a pixel.
294 : */
295 0 : int level = (33 - SkCLZ(d)) >> 1;
296 : // SkDebugf("----- distance %d computedLevel %d\n", d, computedLevel);
297 : // sanity check on level (from the previous version)
298 0 : if (level > kMaxQuadSubdivideLevel) {
299 0 : level = kMaxQuadSubdivideLevel;
300 : }
301 0 : hairquad(pts, clip, blitter, level, lineproc);
302 0 : break;
303 : }
304 : case SkPath::kCubic_Verb:
305 0 : haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc);
306 0 : break;
307 : default:
308 0 : break;
309 : }
310 : }
311 : }
312 :
313 0 : void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip,
314 : SkBlitter* blitter) {
315 0 : hair_path(path, clip, blitter, SkScan::HairLineRgn);
316 0 : }
317 :
318 0 : void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip,
319 : SkBlitter* blitter) {
320 0 : hair_path(path, clip, blitter, SkScan::AntiHairLineRgn);
321 0 : }
322 :
323 : ///////////////////////////////////////////////////////////////////////////////
324 :
325 0 : void SkScan::FrameRect(const SkRect& r, const SkPoint& strokeSize,
326 : const SkRasterClip& clip, SkBlitter* blitter) {
327 0 : SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
328 :
329 0 : if (strokeSize.fX < 0 || strokeSize.fY < 0) {
330 0 : return;
331 : }
332 :
333 0 : const SkScalar dx = strokeSize.fX;
334 0 : const SkScalar dy = strokeSize.fY;
335 0 : SkScalar rx = SkScalarHalf(dx);
336 0 : SkScalar ry = SkScalarHalf(dy);
337 : SkRect outer, tmp;
338 :
339 : outer.set(r.fLeft - rx, r.fTop - ry,
340 0 : r.fRight + rx, r.fBottom + ry);
341 :
342 0 : if (r.width() <= dx || r.height() <= dx) {
343 0 : SkScan::FillRect(outer, clip, blitter);
344 0 : return;
345 : }
346 :
347 0 : tmp.set(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + dy);
348 0 : SkScan::FillRect(tmp, clip, blitter);
349 0 : tmp.fTop = outer.fBottom - dy;
350 0 : tmp.fBottom = outer.fBottom;
351 0 : SkScan::FillRect(tmp, clip, blitter);
352 :
353 0 : tmp.set(outer.fLeft, outer.fTop + dy, outer.fLeft + dx, outer.fBottom - dy);
354 0 : SkScan::FillRect(tmp, clip, blitter);
355 0 : tmp.fLeft = outer.fRight - dx;
356 0 : tmp.fRight = outer.fRight;
357 0 : SkScan::FillRect(tmp, clip, blitter);
358 : }
359 :
360 0 : void SkScan::HairLine(const SkPoint& p0, const SkPoint& p1,
361 : const SkRasterClip& clip, SkBlitter* blitter) {
362 0 : if (clip.isBW()) {
363 0 : HairLineRgn(p0, p1, &clip.bwRgn(), blitter);
364 : } else {
365 0 : const SkRegion* clipRgn = NULL;
366 : SkRect r;
367 : SkIRect ir;
368 0 : r.set(p0.fX, p0.fY, p1.fX, p1.fY);
369 0 : r.sort();
370 0 : r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
371 0 : r.roundOut(&ir);
372 :
373 0 : SkAAClipBlitterWrapper wrap;
374 0 : if (!clip.quickContains(ir)) {
375 0 : wrap.init(clip, blitter);
376 0 : blitter = wrap.getBlitter();
377 0 : clipRgn = &wrap.getRgn();
378 : }
379 0 : HairLineRgn(p0, p1, clipRgn, blitter);
380 : }
381 0 : }
382 :
383 0 : void SkScan::AntiHairLine(const SkPoint& p0, const SkPoint& p1,
384 : const SkRasterClip& clip, SkBlitter* blitter) {
385 0 : if (clip.isBW()) {
386 0 : AntiHairLineRgn(p0, p1, &clip.bwRgn(), blitter);
387 : } else {
388 0 : const SkRegion* clipRgn = NULL;
389 : SkRect r;
390 : SkIRect ir;
391 0 : r.set(p0.fX, p0.fY, p1.fX, p1.fY);
392 0 : r.sort();
393 0 : r.roundOut(&ir);
394 0 : ir.inset(-1, -1);
395 :
396 0 : SkAAClipBlitterWrapper wrap;
397 0 : if (!clip.quickContains(ir)) {
398 0 : wrap.init(clip, blitter);
399 0 : blitter = wrap.getBlitter();
400 0 : clipRgn = &wrap.getRgn();
401 : }
402 0 : AntiHairLineRgn(p0, p1, clipRgn, blitter);
403 : }
404 0 : }
|