| OLD | NEW |
| (Empty) |
| 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 | |
| 23 static void horiline(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitte
r) | |
| 24 { | |
| 25 SkASSERT(x < stopx); | |
| 26 | |
| 27 do { | |
| 28 blitter->blitH(x, fy >> 16, 1); | |
| 29 fy += dy; | |
| 30 } while (++x < stopx); | |
| 31 } | |
| 32 | |
| 33 static void vertline(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitte
r) | |
| 34 { | |
| 35 SkASSERT(y < stopy); | |
| 36 | |
| 37 do { | |
| 38 blitter->blitH(fx >> 16, y, 1); | |
| 39 fx += dx; | |
| 40 } while (++y < stopy); | |
| 41 } | |
| 42 | |
| 43 void SkScan::HairLine(const SkPoint& pt0, const SkPoint& pt1, const SkRegion* cl
ip, 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. | |
| 123 void SkScan::HairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitt
er) | |
| 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 | |
| 163 static bool quad_too_curvy(const SkPoint pts[3]) | |
| 164 { | |
| 165 return true; | |
| 166 } | |
| 167 | |
| 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 | |
| 187 static void hairquad(const SkPoint pts[3], const SkRegion* clip, SkBlitter* blit
ter, int level, | |
| 188 void (*lineproc)(const SkPoint&, const SkPoint&, const SkRe
gion* 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 | |
| 218 static bool cubic_too_curvy(const SkPoint pts[4]) | |
| 219 { | |
| 220 return true; | |
| 221 } | |
| 222 | |
| 223 static void haircubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* bli
tter, int level, | |
| 224 void (*lineproc)(const SkPoint&, const SkPoint&, const SkR
egion*, 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 | |
| 241 static void hair_path(const SkPath& path, const SkRegion* clip, SkBlitter* blitt
er, | |
| 242 void (*lineproc)(const SkPoint&, const SkPoint&, const SkR
egion*, SkBlitter*)) | |
| 243 { | |
| 244 if (path.isEmpty()) | |
| 245 return; | |
| 246 | |
| 247 const SkIRect* clipR = NULL; | |
| 248 | |
| 249 if (clip) | |
| 250 { | |
| 251 SkRect bounds; | |
| 252 SkIRect ibounds; | |
| 253 | |
| 254 path.computeBounds(&bounds, SkPath::kFast_BoundsType); | |
| 255 bounds.roundOut(&ibounds); | |
| 256 ibounds.inset(-1, -1); | |
| 257 | |
| 258 if (clip->quickReject(ibounds)) | |
| 259 return; | |
| 260 | |
| 261 if (clip->quickContains(ibounds)) | |
| 262 clip = NULL; | |
| 263 else | |
| 264 clipR = &clip->getBounds(); | |
| 265 } | |
| 266 | |
| 267 SkPath::Iter iter(path, false); | |
| 268 SkPoint pts[4]; | |
| 269 SkPath::Verb verb; | |
| 270 | |
| 271 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) | |
| 272 { | |
| 273 switch (verb) { | |
| 274 case SkPath::kLine_Verb: | |
| 275 lineproc(pts[0], pts[1], clip, blitter); | |
| 276 break; | |
| 277 case SkPath::kQuad_Verb: { | |
| 278 int d = compute_int_quad_dist(pts); | |
| 279 /* quadratics approach the line connecting their start and end poin
ts | |
| 280 4x closer with each subdivision, so we compute the number of | |
| 281 subdivisions to be the minimum need to get that distance to be less | |
| 282 than a pixel. | |
| 283 */ | |
| 284 int level = (33 - SkCLZ(d)) >> 1; | |
| 285 // SkDebugf("----- distance %d computedLevel %d\n", d, computedLevel); | |
| 286 // sanity check on level (from the previous version) | |
| 287 if (level > kMaxQuadSubdivideLevel) { | |
| 288 level = kMaxQuadSubdivideLevel; | |
| 289 } | |
| 290 hairquad(pts, clip, blitter, level, lineproc); | |
| 291 break; | |
| 292 } | |
| 293 case SkPath::kCubic_Verb: | |
| 294 haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc); | |
| 295 break; | |
| 296 default: | |
| 297 break; | |
| 298 } | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 void SkScan::HairPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitt
er) | |
| 303 { | |
| 304 hair_path(path, clip, blitter, SkScan::HairLine); | |
| 305 } | |
| 306 | |
| 307 void SkScan::AntiHairPath(const SkPath& path, const SkRegion* clip, SkBlitter* b
litter) | |
| 308 { | |
| 309 hair_path(path, clip, blitter, SkScan::AntiHairLine); | |
| 310 } | |
| 311 | |
| 312 //////////////////////////////////////////////////////////////////////////////// | |
| 313 | |
| 314 void SkScan::FrameRect(const SkRect& r, SkScalar diameter, const SkRegion* clip,
SkBlitter* blitter) | |
| 315 { | |
| 316 SkASSERT(diameter > 0); | |
| 317 | |
| 318 if (r.isEmpty()) | |
| 319 return; | |
| 320 | |
| 321 SkScalar radius = diameter / 2; | |
| 322 SkRect outer, tmp; | |
| 323 | |
| 324 outer.set( r.fLeft - radius, r.fTop - radius, | |
| 325 r.fRight + radius, r.fBottom + radius); | |
| 326 | |
| 327 if (r.width() <= diameter || r.height() <= diameter) | |
| 328 { | |
| 329 SkScan::FillRect(outer, clip, blitter); | |
| 330 return; | |
| 331 } | |
| 332 | |
| 333 tmp.set(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + diameter); | |
| 334 SkScan::FillRect(tmp, clip, blitter); | |
| 335 tmp.fTop = outer.fBottom - diameter; | |
| 336 tmp.fBottom = outer.fBottom; | |
| 337 SkScan::FillRect(tmp, clip, blitter); | |
| 338 | |
| 339 tmp.set(outer.fLeft, outer.fTop + diameter, outer.fLeft + diameter, outer.fB
ottom - diameter); | |
| 340 SkScan::FillRect(tmp, clip, blitter); | |
| 341 tmp.fLeft = outer.fRight - diameter; | |
| 342 tmp.fRight = outer.fRight; | |
| 343 SkScan::FillRect(tmp, clip, blitter); | |
| 344 } | |
| 345 | |
| OLD | NEW |