OLD | NEW |
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2006 The Android Open Source Project | 3 * Copyright 2006 The Android Open Source Project |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 | 8 |
9 | 9 |
10 #include "SkScan.h" | 10 #include "SkScan.h" |
(...skipping 22 matching lines...) Expand all Loading... |
33 } while (++y < stopy); | 33 } while (++y < stopy); |
34 } | 34 } |
35 | 35 |
36 #ifdef SK_DEBUG | 36 #ifdef SK_DEBUG |
37 static bool canConvertFDot6ToFixed(SkFDot6 x) { | 37 static bool canConvertFDot6ToFixed(SkFDot6 x) { |
38 const int maxDot6 = SK_MaxS32 >> (16 - 6); | 38 const int maxDot6 = SK_MaxS32 >> (16 - 6); |
39 return SkAbs32(x) <= maxDot6; | 39 return SkAbs32(x) <= maxDot6; |
40 } | 40 } |
41 #endif | 41 #endif |
42 | 42 |
43 void SkScan::HairLineRgn(SkPoint pt0, SkPoint pt1, const SkRegion* clip, SkBlitt
er* blitter) { | 43 void SkScan::HairLineRgn(const SkPoint array[], int arrayCount, const SkRegion*
clip, |
| 44 SkBlitter* origBlitter) { |
44 SkBlitterClipper clipper; | 45 SkBlitterClipper clipper; |
45 SkRect r; | |
46 SkIRect clipR, ptsR; | 46 SkIRect clipR, ptsR; |
47 SkPoint pts[2] = { pt0, pt1 }; | |
48 | 47 |
49 // We have to pre-clip the line to fit in a SkFixed, so we just chop | 48 const SkScalar max = SkIntToScalar(32767); |
50 // the line. TODO find a way to actually draw beyond that range. | 49 const SkRect fixedBounds = SkRect::MakeLTRB(-max, -max, max, max); |
51 { | 50 |
52 SkRect fixedBounds; | 51 SkRect clipBounds; |
53 const SkScalar max = SkIntToScalar(32767); | 52 if (clip) { |
54 fixedBounds.set(-max, -max, max, max); | 53 clipBounds.set(clip->getBounds()); |
55 if (!SkLineClipper::IntersectLine(pts, fixedBounds, pts)) { | |
56 return; | |
57 } | |
58 } | 54 } |
59 | 55 |
60 if (clip) { | 56 for (int i = 0; i < arrayCount - 1; ++i) { |
| 57 SkBlitter* blitter = origBlitter; |
| 58 |
| 59 SkPoint pts[2]; |
| 60 |
| 61 // We have to pre-clip the line to fit in a SkFixed, so we just chop |
| 62 // the line. TODO find a way to actually draw beyond that range. |
| 63 if (!SkLineClipper::IntersectLine(&array[i], fixedBounds, pts)) { |
| 64 continue; |
| 65 } |
| 66 |
61 // Perform a clip in scalar space, so we catch huge values which might | 67 // Perform a clip in scalar space, so we catch huge values which might |
62 // be missed after we convert to SkFDot6 (overflow) | 68 // be missed after we convert to SkFDot6 (overflow) |
63 r.set(clip->getBounds()); | 69 if (clip && !SkLineClipper::IntersectLine(pts, clipBounds, pts)) { |
64 if (!SkLineClipper::IntersectLine(pts, r, pts)) { | 70 continue; |
65 return; | |
66 } | |
67 } | |
68 | |
69 SkFDot6 x0 = SkScalarToFDot6(pts[0].fX); | |
70 SkFDot6 y0 = SkScalarToFDot6(pts[0].fY); | |
71 SkFDot6 x1 = SkScalarToFDot6(pts[1].fX); | |
72 SkFDot6 y1 = SkScalarToFDot6(pts[1].fY); | |
73 | |
74 SkASSERT(canConvertFDot6ToFixed(x0)); | |
75 SkASSERT(canConvertFDot6ToFixed(y0)); | |
76 SkASSERT(canConvertFDot6ToFixed(x1)); | |
77 SkASSERT(canConvertFDot6ToFixed(y1)); | |
78 | |
79 if (clip) { | |
80 // now perform clipping again, as the rounding to dot6 can wiggle us | |
81 // our rects are really dot6 rects, but since we've already used | |
82 // lineclipper, we know they will fit in 32bits (26.6) | |
83 const SkIRect& bounds = clip->getBounds(); | |
84 | |
85 clipR.set(SkIntToFDot6(bounds.fLeft), SkIntToFDot6(bounds.fTop), | |
86 SkIntToFDot6(bounds.fRight), SkIntToFDot6(bounds.fBottom)); | |
87 ptsR.set(x0, y0, x1, y1); | |
88 ptsR.sort(); | |
89 | |
90 // outset the right and bottom, to account for how hairlines are | |
91 // actually drawn, which may hit the pixel to the right or below of | |
92 // the coordinate | |
93 ptsR.fRight += SK_FDot6One; | |
94 ptsR.fBottom += SK_FDot6One; | |
95 | |
96 if (!SkIRect::Intersects(ptsR, clipR)) { | |
97 return; | |
98 } | |
99 if (clip->isRect() && clipR.contains(ptsR)) { | |
100 clip = NULL; | |
101 } else { | |
102 blitter = clipper.apply(blitter, clip); | |
103 } | |
104 } | |
105 | |
106 SkFDot6 dx = x1 - x0; | |
107 SkFDot6 dy = y1 - y0; | |
108 | |
109 if (SkAbs32(dx) > SkAbs32(dy)) { // mostly horizontal | |
110 if (x0 > x1) { // we want to go left-to-right | |
111 SkTSwap<SkFDot6>(x0, x1); | |
112 SkTSwap<SkFDot6>(y0, y1); | |
113 } | |
114 int ix0 = SkFDot6Round(x0); | |
115 int ix1 = SkFDot6Round(x1); | |
116 if (ix0 == ix1) {// too short to draw | |
117 return; | |
118 } | 71 } |
119 | 72 |
120 SkFixed slope = SkFixedDiv(dy, dx); | 73 SkFDot6 x0 = SkScalarToFDot6(pts[0].fX); |
121 SkFixed startY = SkFDot6ToFixed(y0) + (slope * ((32 - x0) & 63) >> 6); | 74 SkFDot6 y0 = SkScalarToFDot6(pts[0].fY); |
| 75 SkFDot6 x1 = SkScalarToFDot6(pts[1].fX); |
| 76 SkFDot6 y1 = SkScalarToFDot6(pts[1].fY); |
122 | 77 |
123 horiline(ix0, ix1, startY, slope, blitter); | 78 SkASSERT(canConvertFDot6ToFixed(x0)); |
124 } else { // mostly vertical | 79 SkASSERT(canConvertFDot6ToFixed(y0)); |
125 if (y0 > y1) { // we want to go top-to-bottom | 80 SkASSERT(canConvertFDot6ToFixed(x1)); |
126 SkTSwap<SkFDot6>(x0, x1); | 81 SkASSERT(canConvertFDot6ToFixed(y1)); |
127 SkTSwap<SkFDot6>(y0, y1); | 82 |
128 } | 83 if (clip) { |
129 int iy0 = SkFDot6Round(y0); | 84 // now perform clipping again, as the rounding to dot6 can wiggle us |
130 int iy1 = SkFDot6Round(y1); | 85 // our rects are really dot6 rects, but since we've already used |
131 if (iy0 == iy1) { // too short to draw | 86 // lineclipper, we know they will fit in 32bits (26.6) |
132 return; | 87 const SkIRect& bounds = clip->getBounds(); |
| 88 |
| 89 clipR.set(SkIntToFDot6(bounds.fLeft), SkIntToFDot6(bounds.fTop), |
| 90 SkIntToFDot6(bounds.fRight), SkIntToFDot6(bounds.fBottom))
; |
| 91 ptsR.set(x0, y0, x1, y1); |
| 92 ptsR.sort(); |
| 93 |
| 94 // outset the right and bottom, to account for how hairlines are |
| 95 // actually drawn, which may hit the pixel to the right or below of |
| 96 // the coordinate |
| 97 ptsR.fRight += SK_FDot6One; |
| 98 ptsR.fBottom += SK_FDot6One; |
| 99 |
| 100 if (!SkIRect::Intersects(ptsR, clipR)) { |
| 101 continue; |
| 102 } |
| 103 if (!clip->isRect() || !clipR.contains(ptsR)) { |
| 104 blitter = clipper.apply(origBlitter, clip); |
| 105 } |
133 } | 106 } |
134 | 107 |
135 SkFixed slope = SkFixedDiv(dx, dy); | 108 SkFDot6 dx = x1 - x0; |
136 SkFixed startX = SkFDot6ToFixed(x0) + (slope * ((32 - y0) & 63) >> 6); | 109 SkFDot6 dy = y1 - y0; |
137 | 110 |
138 vertline(iy0, iy1, startX, slope, blitter); | 111 if (SkAbs32(dx) > SkAbs32(dy)) { // mostly horizontal |
| 112 if (x0 > x1) { // we want to go left-to-right |
| 113 SkTSwap<SkFDot6>(x0, x1); |
| 114 SkTSwap<SkFDot6>(y0, y1); |
| 115 } |
| 116 int ix0 = SkFDot6Round(x0); |
| 117 int ix1 = SkFDot6Round(x1); |
| 118 if (ix0 == ix1) {// too short to draw |
| 119 continue; |
| 120 } |
| 121 |
| 122 SkFixed slope = SkFixedDiv(dy, dx); |
| 123 SkFixed startY = SkFDot6ToFixed(y0) + (slope * ((32 - x0) & 63) >> 6
); |
| 124 |
| 125 horiline(ix0, ix1, startY, slope, blitter); |
| 126 } else { // mostly vertical |
| 127 if (y0 > y1) { // we want to go top-to-bottom |
| 128 SkTSwap<SkFDot6>(x0, x1); |
| 129 SkTSwap<SkFDot6>(y0, y1); |
| 130 } |
| 131 int iy0 = SkFDot6Round(y0); |
| 132 int iy1 = SkFDot6Round(y1); |
| 133 if (iy0 == iy1) { // too short to draw |
| 134 continue; |
| 135 } |
| 136 |
| 137 SkFixed slope = SkFixedDiv(dx, dy); |
| 138 SkFixed startX = SkFDot6ToFixed(x0) + (slope * ((32 - y0) & 63) >> 6
); |
| 139 |
| 140 vertline(iy0, iy1, startX, slope, blitter); |
| 141 } |
139 } | 142 } |
140 } | 143 } |
141 | 144 |
142 // we don't just draw 4 lines, 'cause that can leave a gap in the bottom-right | 145 // we don't just draw 4 lines, 'cause that can leave a gap in the bottom-right |
143 // and double-hit the top-left. | 146 // and double-hit the top-left. |
144 // TODO: handle huge coordinates on rect (before calling SkScalarToFixed) | 147 // TODO: handle huge coordinates on rect (before calling SkScalarToFixed) |
145 void SkScan::HairRect(const SkRect& rect, const SkRasterClip& clip, | 148 void SkScan::HairRect(const SkRect& rect, const SkRasterClip& clip, |
146 SkBlitter* blitter) { | 149 SkBlitter* blitter) { |
147 SkAAClipBlitterWrapper wrapper; | 150 SkAAClipBlitterWrapper wrapper; |
148 SkBlitterClipper clipper; | 151 SkBlitterClipper clipper; |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
202 int idx = SkScalarCeilToInt(dx); | 205 int idx = SkScalarCeilToInt(dx); |
203 int idy = SkScalarCeilToInt(dy); | 206 int idy = SkScalarCeilToInt(dy); |
204 // use the cheap approx for distance | 207 // use the cheap approx for distance |
205 if (idx > idy) { | 208 if (idx > idy) { |
206 return idx + (idy >> 1); | 209 return idx + (idy >> 1); |
207 } else { | 210 } else { |
208 return idy + (idx >> 1); | 211 return idy + (idx >> 1); |
209 } | 212 } |
210 } | 213 } |
211 | 214 |
212 typedef void (*LineProc)(SkPoint, SkPoint, const SkRegion*, SkBlitter*); | |
213 | |
214 static void hairquad(const SkPoint pts[3], const SkRegion* clip, | 215 static void hairquad(const SkPoint pts[3], const SkRegion* clip, |
215 SkBlitter* blitter, int level, LineProc lineproc) { | 216 SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc
) { |
216 if (level > 0) { | 217 if (level > 0) { |
217 SkPoint tmp[5]; | 218 SkPoint tmp[5]; |
218 | 219 |
219 SkChopQuadAtHalf(pts, tmp); | 220 SkChopQuadAtHalf(pts, tmp); |
220 hairquad(tmp, clip, blitter, level - 1, lineproc); | 221 hairquad(tmp, clip, blitter, level - 1, lineproc); |
221 hairquad(&tmp[2], clip, blitter, level - 1, lineproc); | 222 hairquad(&tmp[2], clip, blitter, level - 1, lineproc); |
222 } else { | 223 } else { |
223 lineproc(pts[0], pts[2], clip, blitter); | 224 SkPoint tmp[] = { pts[0], pts[2] }; |
| 225 lineproc(tmp, 2, clip, blitter); |
224 } | 226 } |
225 } | 227 } |
226 | 228 |
227 static void haircubic(const SkPoint pts[4], const SkRegion* clip, | 229 static void haircubic(const SkPoint pts[4], const SkRegion* clip, |
228 SkBlitter* blitter, int level, LineProc lineproc) { | 230 SkBlitter* blitter, int level, SkScan::HairRgnProc linepro
c) { |
229 if (level > 0) { | 231 if (level > 0) { |
230 SkPoint tmp[7]; | 232 SkPoint tmp[7]; |
231 | 233 |
232 SkChopCubicAt(pts, tmp, SK_Scalar1/2); | 234 SkChopCubicAt(pts, tmp, SK_Scalar1/2); |
233 haircubic(tmp, clip, blitter, level - 1, lineproc); | 235 haircubic(tmp, clip, blitter, level - 1, lineproc); |
234 haircubic(&tmp[3], clip, blitter, level - 1, lineproc); | 236 haircubic(&tmp[3], clip, blitter, level - 1, lineproc); |
235 } else { | 237 } else { |
236 lineproc(pts[0], pts[3], clip, blitter); | 238 SkPoint tmp[] = { pts[0], pts[3] }; |
| 239 lineproc(tmp, 2, clip, blitter); |
237 } | 240 } |
238 } | 241 } |
239 | 242 |
240 #define kMaxCubicSubdivideLevel 6 | 243 #define kMaxCubicSubdivideLevel 6 |
241 #define kMaxQuadSubdivideLevel 5 | 244 #define kMaxQuadSubdivideLevel 5 |
242 | 245 |
243 static int compute_quad_level(const SkPoint pts[3]) { | 246 static int compute_quad_level(const SkPoint pts[3]) { |
244 int d = compute_int_quad_dist(pts); | 247 int d = compute_int_quad_dist(pts); |
245 /* quadratics approach the line connecting their start and end points | 248 /* quadratics approach the line connecting their start and end points |
246 4x closer with each subdivision, so we compute the number of | 249 4x closer with each subdivision, so we compute the number of |
247 subdivisions to be the minimum need to get that distance to be less | 250 subdivisions to be the minimum need to get that distance to be less |
248 than a pixel. | 251 than a pixel. |
249 */ | 252 */ |
250 int level = (33 - SkCLZ(d)) >> 1; | 253 int level = (33 - SkCLZ(d)) >> 1; |
251 // sanity check on level (from the previous version) | 254 // sanity check on level (from the previous version) |
252 if (level > kMaxQuadSubdivideLevel) { | 255 if (level > kMaxQuadSubdivideLevel) { |
253 level = kMaxQuadSubdivideLevel; | 256 level = kMaxQuadSubdivideLevel; |
254 } | 257 } |
255 return level; | 258 return level; |
256 } | 259 } |
257 | 260 |
258 static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter*
blitter, | 261 static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter*
blitter, |
259 LineProc lineproc) { | 262 SkScan::HairRgnProc lineproc) { |
260 if (path.isEmpty()) { | 263 if (path.isEmpty()) { |
261 return; | 264 return; |
262 } | 265 } |
263 | 266 |
264 SkAAClipBlitterWrapper wrap; | 267 SkAAClipBlitterWrapper wrap; |
265 const SkRegion* clip = NULL; | 268 const SkRegion* clip = NULL; |
266 | 269 |
267 { | 270 { |
268 const SkIRect ibounds = path.getBounds().roundOut().makeOutset(1, 1); | 271 const SkIRect ibounds = path.getBounds().roundOut().makeOutset(1, 1); |
269 | 272 |
(...skipping 14 matching lines...) Expand all Loading... |
284 SkPath::Iter iter(path, false); | 287 SkPath::Iter iter(path, false); |
285 SkPoint pts[4]; | 288 SkPoint pts[4]; |
286 SkPath::Verb verb; | 289 SkPath::Verb verb; |
287 SkAutoConicToQuads converter; | 290 SkAutoConicToQuads converter; |
288 | 291 |
289 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) { | 292 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) { |
290 switch (verb) { | 293 switch (verb) { |
291 case SkPath::kMove_Verb: | 294 case SkPath::kMove_Verb: |
292 break; | 295 break; |
293 case SkPath::kLine_Verb: | 296 case SkPath::kLine_Verb: |
294 lineproc(pts[0], pts[1], clip, blitter); | 297 lineproc(pts, 2, clip, blitter); |
295 break; | 298 break; |
296 case SkPath::kQuad_Verb: | 299 case SkPath::kQuad_Verb: |
297 hairquad(pts, clip, blitter, compute_quad_level(pts), lineproc); | 300 hairquad(pts, clip, blitter, compute_quad_level(pts), lineproc); |
298 break; | 301 break; |
299 case SkPath::kConic_Verb: { | 302 case SkPath::kConic_Verb: { |
300 // how close should the quads be to the original conic? | 303 // how close should the quads be to the original conic? |
301 const SkScalar tol = SK_Scalar1 / 4; | 304 const SkScalar tol = SK_Scalar1 / 4; |
302 const SkPoint* quadPts = converter.computeQuads(pts, | 305 const SkPoint* quadPts = converter.computeQuads(pts, |
303 iter.conicWeight(), tol); | 306 iter.conicWeight(), tol); |
304 for (int i = 0; i < converter.countQuads(); ++i) { | 307 for (int i = 0; i < converter.countQuads(); ++i) { |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
357 tmp.fBottom = outer.fBottom; | 360 tmp.fBottom = outer.fBottom; |
358 SkScan::FillRect(tmp, clip, blitter); | 361 SkScan::FillRect(tmp, clip, blitter); |
359 | 362 |
360 tmp.set(outer.fLeft, outer.fTop + dy, outer.fLeft + dx, outer.fBottom - dy); | 363 tmp.set(outer.fLeft, outer.fTop + dy, outer.fLeft + dx, outer.fBottom - dy); |
361 SkScan::FillRect(tmp, clip, blitter); | 364 SkScan::FillRect(tmp, clip, blitter); |
362 tmp.fLeft = outer.fRight - dx; | 365 tmp.fLeft = outer.fRight - dx; |
363 tmp.fRight = outer.fRight; | 366 tmp.fRight = outer.fRight; |
364 SkScan::FillRect(tmp, clip, blitter); | 367 SkScan::FillRect(tmp, clip, blitter); |
365 } | 368 } |
366 | 369 |
367 void SkScan::HairLine(SkPoint p0, SkPoint p1, const SkRasterClip& clip, SkBlitte
r* blitter) { | 370 void SkScan::HairLine(const SkPoint pts[], int count, const SkRasterClip& clip, |
| 371 SkBlitter* blitter) { |
368 if (clip.isBW()) { | 372 if (clip.isBW()) { |
369 HairLineRgn(p0, p1, &clip.bwRgn(), blitter); | 373 HairLineRgn(pts, count, &clip.bwRgn(), blitter); |
370 } else { | 374 } else { |
371 const SkRegion* clipRgn = NULL; | 375 const SkRegion* clipRgn = NULL; |
| 376 |
372 SkRect r; | 377 SkRect r; |
373 r.set(p0.fX, p0.fY, p1.fX, p1.fY); | 378 r.set(pts, count); |
374 r.sort(); | |
375 r.outset(SK_ScalarHalf, SK_ScalarHalf); | 379 r.outset(SK_ScalarHalf, SK_ScalarHalf); |
376 | 380 |
377 SkAAClipBlitterWrapper wrap; | 381 SkAAClipBlitterWrapper wrap; |
378 if (!clip.quickContains(r.roundOut())) { | 382 if (!clip.quickContains(r.roundOut())) { |
379 wrap.init(clip, blitter); | 383 wrap.init(clip, blitter); |
380 blitter = wrap.getBlitter(); | 384 blitter = wrap.getBlitter(); |
381 clipRgn = &wrap.getRgn(); | 385 clipRgn = &wrap.getRgn(); |
382 } | 386 } |
383 HairLineRgn(p0, p1, clipRgn, blitter); | 387 HairLineRgn(pts, count, clipRgn, blitter); |
384 } | 388 } |
385 } | 389 } |
386 | 390 |
387 void SkScan::AntiHairLine(SkPoint p0, SkPoint p1, const SkRasterClip& clip, SkBl
itter* blitter) { | 391 void SkScan::AntiHairLine(const SkPoint pts[], int count, const SkRasterClip& cl
ip, |
| 392 SkBlitter* blitter) { |
388 if (clip.isBW()) { | 393 if (clip.isBW()) { |
389 AntiHairLineRgn(p0, p1, &clip.bwRgn(), blitter); | 394 AntiHairLineRgn(pts, count, &clip.bwRgn(), blitter); |
390 } else { | 395 } else { |
391 const SkRegion* clipRgn = NULL; | 396 const SkRegion* clipRgn = NULL; |
| 397 |
392 SkRect r; | 398 SkRect r; |
393 r.set(p0.fX, p0.fY, p1.fX, p1.fY); | 399 r.set(pts, count); |
394 r.sort(); | |
395 | 400 |
396 SkAAClipBlitterWrapper wrap; | 401 SkAAClipBlitterWrapper wrap; |
397 if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) { | 402 if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) { |
398 wrap.init(clip, blitter); | 403 wrap.init(clip, blitter); |
399 blitter = wrap.getBlitter(); | 404 blitter = wrap.getBlitter(); |
400 clipRgn = &wrap.getRgn(); | 405 clipRgn = &wrap.getRgn(); |
401 } | 406 } |
402 AntiHairLineRgn(p0, p1, clipRgn, blitter); | 407 AntiHairLineRgn(pts, count, clipRgn, blitter); |
403 } | 408 } |
404 } | 409 } |
OLD | NEW |