1
2 /*
3 * Copyright 2011 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 "SkColorPriv.h"
13 #include "SkLineClipper.h"
14 #include "SkRasterClip.h"
15 #include "SkFDot6.h"
16
17 /* Our attempt to compute the worst case "bounds" for the horizontal and
18 vertical cases has some numerical bug in it, and we sometimes undervalue
19 our extends. The bug is that when this happens, we will set the clip to
20 NULL (for speed), and thus draw outside of the clip by a pixel, which might
21 only look bad, but it might also access memory outside of the valid range
22 allcoated for the device bitmap.
23
24 This define enables our fix to outset our "bounds" by 1, thus avoiding the
25 chance of the bug, but at the cost of sometimes taking the rectblitter
26 case (i.e. not setting the clip to NULL) when we might not actually need
27 to. If we can improve/fix the actual calculations, then we can remove this
28 step.
29 */
30 #define OUTSET_BEFORE_CLIP_TEST true
31
32 #define HLINE_STACK_BUFFER 100
33
SmallDot6Scale(int value,int dot6)34 static inline int SmallDot6Scale(int value, int dot6) {
35 SkASSERT((int16_t)value == value);
36 SkASSERT((unsigned)dot6 <= 64);
37 return SkMulS16(value, dot6) >> 6;
38 }
39
40 //#define TEST_GAMMA
41
42 #ifdef TEST_GAMMA
43 static uint8_t gGammaTable[256];
44 #define ApplyGamma(table, alpha) (table)[alpha]
45
build_gamma_table()46 static void build_gamma_table() {
47 static bool gInit = false;
48
49 if (gInit == false) {
50 for (int i = 0; i < 256; i++) {
51 SkFixed n = i * 257;
52 n += n >> 15;
53 SkASSERT(n >= 0 && n <= SK_Fixed1);
54 n = SkFixedSqrt(n);
55 n = n * 255 >> 16;
56 // SkDebugf("morph %d -> %d\n", i, n);
57 gGammaTable[i] = SkToU8(n);
58 }
59 gInit = true;
60 }
61 }
62 #else
63 #define ApplyGamma(table, alpha) SkToU8(alpha)
64 #endif
65
66 ///////////////////////////////////////////////////////////////////////////////
67
call_hline_blitter(SkBlitter * blitter,int x,int y,int count,U8CPU alpha)68 static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
69 U8CPU alpha) {
70 SkASSERT(count > 0);
71
72 int16_t runs[HLINE_STACK_BUFFER + 1];
73 uint8_t aa[HLINE_STACK_BUFFER];
74
75 aa[0] = ApplyGamma(gGammaTable, alpha);
76 do {
77 int n = count;
78 if (n > HLINE_STACK_BUFFER) {
79 n = HLINE_STACK_BUFFER;
80 }
81 runs[0] = SkToS16(n);
82 runs[n] = 0;
83 blitter->blitAntiH(x, y, aa, runs);
84 x += n;
85 count -= n;
86 } while (count > 0);
87 }
88
hline(int x,int stopx,SkFixed fy,SkFixed,SkBlitter * blitter,int mod64)89 static SkFixed hline(int x, int stopx, SkFixed fy, SkFixed /*slope*/,
90 SkBlitter* blitter, int mod64) {
91 SkASSERT(x < stopx);
92 int count = stopx - x;
93 fy += SK_Fixed1/2;
94
95 int y = fy >> 16;
96 uint8_t a = (uint8_t)(fy >> 8);
97
98 // lower line
99 unsigned ma = SmallDot6Scale(a, mod64);
100 if (ma) {
101 call_hline_blitter(blitter, x, y, count, ma);
102 }
103
104 // upper line
105 ma = SmallDot6Scale(255 - a, mod64);
106 if (ma) {
107 call_hline_blitter(blitter, x, y - 1, count, ma);
108 }
109
110 return fy - SK_Fixed1/2;
111 }
112
horish(int x,int stopx,SkFixed fy,SkFixed dy,SkBlitter * blitter,int mod64)113 static SkFixed horish(int x, int stopx, SkFixed fy, SkFixed dy,
114 SkBlitter* blitter, int mod64) {
115 SkASSERT(x < stopx);
116
117 #ifdef TEST_GAMMA
118 const uint8_t* gamma = gGammaTable;
119 #endif
120 int16_t runs[2];
121 uint8_t aa[1];
122
123 runs[0] = 1;
124 runs[1] = 0;
125
126 fy += SK_Fixed1/2;
127 do {
128 int lower_y = fy >> 16;
129 uint8_t a = (uint8_t)(fy >> 8);
130 unsigned ma = SmallDot6Scale(a, mod64);
131 if (ma) {
132 aa[0] = ApplyGamma(gamma, ma);
133 blitter->blitAntiH(x, lower_y, aa, runs);
134 // the clipping blitters might edit runs, but should not affect us
135 SkASSERT(runs[0] == 1);
136 SkASSERT(runs[1] == 0);
137 }
138 ma = SmallDot6Scale(255 - a, mod64);
139 if (ma) {
140 aa[0] = ApplyGamma(gamma, ma);
141 blitter->blitAntiH(x, lower_y - 1, aa, runs);
142 // the clipping blitters might edit runs, but should not affect us
143 SkASSERT(runs[0] == 1);
144 SkASSERT(runs[1] == 0);
145 }
146 fy += dy;
147 } while (++x < stopx);
148
149 return fy - SK_Fixed1/2;
150 }
151
vline(int y,int stopy,SkFixed fx,SkFixed,SkBlitter * blitter,int mod64)152 static SkFixed vline(int y, int stopy, SkFixed fx, SkFixed /*slope*/,
153 SkBlitter* blitter, int mod64) {
154 SkASSERT(y < stopy);
155 fx += SK_Fixed1/2;
156
157 int x = fx >> 16;
158 int a = (uint8_t)(fx >> 8);
159
160 unsigned ma = SmallDot6Scale(a, mod64);
161 if (ma) {
162 blitter->blitV(x, y, stopy - y, ApplyGamma(gGammaTable, ma));
163 }
164 ma = SmallDot6Scale(255 - a, mod64);
165 if (ma) {
166 blitter->blitV(x - 1, y, stopy - y, ApplyGamma(gGammaTable, ma));
167 }
168
169 return fx - SK_Fixed1/2;
170 }
171
vertish(int y,int stopy,SkFixed fx,SkFixed dx,SkBlitter * blitter,int mod64)172 static SkFixed vertish(int y, int stopy, SkFixed fx, SkFixed dx,
173 SkBlitter* blitter, int mod64) {
174 SkASSERT(y < stopy);
175 #ifdef TEST_GAMMA
176 const uint8_t* gamma = gGammaTable;
177 #endif
178 int16_t runs[3];
179 uint8_t aa[2];
180
181 runs[0] = 1;
182 runs[2] = 0;
183
184 fx += SK_Fixed1/2;
185 do {
186 int x = fx >> 16;
187 uint8_t a = (uint8_t)(fx >> 8);
188
189 aa[0] = ApplyGamma(gamma, SmallDot6Scale(255 - a, mod64));
190 aa[1] = ApplyGamma(gamma, SmallDot6Scale(a, mod64));
191 // the clippng blitters might overwrite this guy, so we have to reset it each time
192 runs[1] = 1;
193 blitter->blitAntiH(x - 1, y, aa, runs);
194 // the clipping blitters might edit runs, but should not affect us
195 SkASSERT(runs[0] == 1);
196 SkASSERT(runs[2] == 0);
197 fx += dx;
198 } while (++y < stopy);
199
200 return fx - SK_Fixed1/2;
201 }
202
203 typedef SkFixed (*LineProc)(int istart, int istop, SkFixed fstart,
204 SkFixed slope, SkBlitter*, int);
205
fastfixdiv(SkFDot6 a,SkFDot6 b)206 static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
207 SkASSERT((a << 16 >> 16) == a);
208 SkASSERT(b != 0);
209 return (a << 16) / b;
210 }
211
do_anti_hairline(SkFDot6 x0,SkFDot6 y0,SkFDot6 x1,SkFDot6 y1,const SkIRect * clip,SkBlitter * blitter)212 static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
213 const SkIRect* clip, SkBlitter* blitter) {
214 // check that we're no larger than 511 pixels (so we can do a faster div).
215 // if we are, subdivide and call again
216
217 if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) {
218 /* instead of (x0 + x1) >> 1, we shift each separately. This is less
219 precise, but avoids overflowing the intermediate result if the
220 values are huge. A better fix might be to clip the original pts
221 directly (i.e. do the divide), so we don't spend time subdividing
222 huge lines at all.
223 */
224 int hx = (x0 >> 1) + (x1 >> 1);
225 int hy = (y0 >> 1) + (y1 >> 1);
226 do_anti_hairline(x0, y0, hx, hy, clip, blitter);
227 do_anti_hairline(hx, hy, x1, y1, clip, blitter);
228 return;
229 }
230
231 int scaleStart, scaleStop;
232 int istart, istop;
233 SkFixed fstart, slope;
234 LineProc proc;
235
236 if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) { // mostly horizontal
237 if (x0 > x1) { // we want to go left-to-right
238 SkTSwap<SkFDot6>(x0, x1);
239 SkTSwap<SkFDot6>(y0, y1);
240 }
241
242 istart = SkFDot6Floor(x0);
243 istop = SkFDot6Ceil(x1);
244 fstart = SkFDot6ToFixed(y0);
245 if (y0 == y1) { // completely horizontal, take fast case
246 slope = 0;
247 proc = hline;
248 } else {
249 slope = fastfixdiv(y1 - y0, x1 - x0);
250 SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
251 fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
252 proc = horish;
253 }
254
255 SkASSERT(istop > istart);
256 if (istop - istart == 1) {
257 scaleStart = x1 - x0;
258 SkASSERT(scaleStart >= 0 && scaleStart <= 64);
259 scaleStop = 0;
260 } else {
261 scaleStart = 64 - (x0 & 63);
262 scaleStop = x1 & 63;
263 }
264
265 if (clip){
266 if (istart >= clip->fRight || istop <= clip->fLeft) {
267 return;
268 }
269 if (istart < clip->fLeft) {
270 fstart += slope * (clip->fLeft - istart);
271 istart = clip->fLeft;
272 scaleStart = 64;
273 }
274 if (istop > clip->fRight) {
275 istop = clip->fRight;
276 scaleStop = 64;
277 }
278 SkASSERT(istart <= istop);
279 if (istart == istop) {
280 return;
281 }
282 // now test if our Y values are completely inside the clip
283 int top, bottom;
284 if (slope >= 0) { // T2B
285 top = SkFixedFloor(fstart - SK_FixedHalf);
286 bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
287 } else { // B2T
288 bottom = SkFixedCeil(fstart + SK_FixedHalf);
289 top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
290 }
291 #ifdef OUTSET_BEFORE_CLIP_TEST
292 top -= 1;
293 bottom += 1;
294 #endif
295 if (top >= clip->fBottom || bottom <= clip->fTop) {
296 return;
297 }
298 if (clip->fTop <= top && clip->fBottom >= bottom) {
299 clip = NULL;
300 }
301 }
302 } else { // mostly vertical
303 if (y0 > y1) { // we want to go top-to-bottom
304 SkTSwap<SkFDot6>(x0, x1);
305 SkTSwap<SkFDot6>(y0, y1);
306 }
307
308 istart = SkFDot6Floor(y0);
309 istop = SkFDot6Ceil(y1);
310 fstart = SkFDot6ToFixed(x0);
311 if (x0 == x1) {
312 if (y0 == y1) { // are we zero length?
313 return; // nothing to do
314 }
315 slope = 0;
316 proc = vline;
317 } else {
318 slope = fastfixdiv(x1 - x0, y1 - y0);
319 SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
320 fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
321 proc = vertish;
322 }
323
324 SkASSERT(istop > istart);
325 if (istop - istart == 1) {
326 scaleStart = y1 - y0;
327 SkASSERT(scaleStart >= 0 && scaleStart <= 64);
328 scaleStop = 0;
329 } else {
330 scaleStart = 64 - (y0 & 63);
331 scaleStop = y1 & 63;
332 }
333
334 if (clip) {
335 if (istart >= clip->fBottom || istop <= clip->fTop) {
336 return;
337 }
338 if (istart < clip->fTop) {
339 fstart += slope * (clip->fTop - istart);
340 istart = clip->fTop;
341 scaleStart = 64;
342 }
343 if (istop > clip->fBottom) {
344 istop = clip->fBottom;
345 scaleStop = 64;
346 }
347 SkASSERT(istart <= istop);
348 if (istart == istop)
349 return;
350
351 // now test if our X values are completely inside the clip
352 int left, right;
353 if (slope >= 0) { // L2R
354 left = SkFixedFloor(fstart - SK_FixedHalf);
355 right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
356 } else { // R2L
357 right = SkFixedCeil(fstart + SK_FixedHalf);
358 left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
359 }
360 #ifdef OUTSET_BEFORE_CLIP_TEST
361 left -= 1;
362 right += 1;
363 #endif
364 if (left >= clip->fRight || right <= clip->fLeft) {
365 return;
366 }
367 if (clip->fLeft <= left && clip->fRight >= right) {
368 clip = NULL;
369 }
370 }
371 }
372
373 SkRectClipBlitter rectClipper;
374 if (clip) {
375 rectClipper.init(blitter, *clip);
376 blitter = &rectClipper;
377 }
378
379 fstart = proc(istart, istart + 1, fstart, slope, blitter, scaleStart);
380 istart += 1;
381 int fullSpans = istop - istart - (scaleStop > 0);
382 if (fullSpans > 0) {
383 fstart = proc(istart, istart + fullSpans, fstart, slope, blitter, 64);
384 }
385 if (scaleStop > 0) {
386 proc(istop - 1, istop, fstart, slope, blitter, scaleStop);
387 }
388 }
389
AntiHairLineRgn(const SkPoint & pt0,const SkPoint & pt1,const SkRegion * clip,SkBlitter * blitter)390 void SkScan::AntiHairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
391 const SkRegion* clip, SkBlitter* blitter) {
392 if (clip && clip->isEmpty()) {
393 return;
394 }
395
396 SkASSERT(clip == NULL || !clip->getBounds().isEmpty());
397
398 #ifdef TEST_GAMMA
399 build_gamma_table();
400 #endif
401
402 SkPoint pts[2] = { pt0, pt1 };
403
404 if (clip) {
405 SkRect clipBounds;
406 clipBounds.set(clip->getBounds());
407 /* We perform integral clipping later on, but we do a scalar clip first
408 to ensure that our coordinates are expressible in fixed/integers.
409
410 antialiased hairlines can draw up to 1/2 of a pixel outside of
411 their bounds, so we need to outset the clip before calling the
412 clipper. To make the numerics safer, we outset by a whole pixel,
413 since the 1/2 pixel boundary is important to the antihair blitter,
414 we don't want to risk numerical fate by chopping on that edge.
415 */
416 clipBounds.inset(-SK_Scalar1, -SK_Scalar1);
417
418 if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
419 return;
420 }
421 }
422
423 SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
424 SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
425 SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
426 SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
427
428 if (clip) {
429 SkFDot6 left = SkMin32(x0, x1);
430 SkFDot6 top = SkMin32(y0, y1);
431 SkFDot6 right = SkMax32(x0, x1);
432 SkFDot6 bottom = SkMax32(y0, y1);
433 SkIRect ir;
434
435 ir.set( SkFDot6Floor(left) - 1,
436 SkFDot6Floor(top) - 1,
437 SkFDot6Ceil(right) + 1,
438 SkFDot6Ceil(bottom) + 1);
439
440 if (clip->quickReject(ir)) {
441 return;
442 }
443 if (!clip->quickContains(ir)) {
444 SkRegion::Cliperator iter(*clip, ir);
445 const SkIRect* r = &iter.rect();
446
447 while (!iter.done()) {
448 do_anti_hairline(x0, y0, x1, y1, r, blitter);
449 iter.next();
450 }
451 return;
452 }
453 // fall through to no-clip case
454 }
455 do_anti_hairline(x0, y0, x1, y1, NULL, blitter);
456 }
457
AntiHairRect(const SkRect & rect,const SkRasterClip & clip,SkBlitter * blitter)458 void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
459 SkBlitter* blitter) {
460 SkPoint p0, p1;
461
462 p0.set(rect.fLeft, rect.fTop);
463 p1.set(rect.fRight, rect.fTop);
464 SkScan::AntiHairLine(p0, p1, clip, blitter);
465 p0.set(rect.fRight, rect.fBottom);
466 SkScan::AntiHairLine(p0, p1, clip, blitter);
467 p1.set(rect.fLeft, rect.fBottom);
468 SkScan::AntiHairLine(p0, p1, clip, blitter);
469 p0.set(rect.fLeft, rect.fTop);
470 SkScan::AntiHairLine(p0, p1, clip, blitter);
471 }
472
473 ///////////////////////////////////////////////////////////////////////////////
474
475 typedef int FDot8; // 24.8 integer fixed point
476
SkFixedToFDot8(SkFixed x)477 static inline FDot8 SkFixedToFDot8(SkFixed x) {
478 return (x + 0x80) >> 8;
479 }
480
do_scanline(FDot8 L,int top,FDot8 R,U8CPU alpha,SkBlitter * blitter)481 static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
482 SkBlitter* blitter) {
483 SkASSERT(L < R);
484
485 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
486 blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
487 return;
488 }
489
490 int left = L >> 8;
491
492 if (L & 0xFF) {
493 blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
494 left += 1;
495 }
496
497 int rite = R >> 8;
498 int width = rite - left;
499 if (width > 0) {
500 call_hline_blitter(blitter, left, top, width, alpha);
501 }
502 if (R & 0xFF) {
503 blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
504 }
505 }
506
antifilldot8(FDot8 L,FDot8 T,FDot8 R,FDot8 B,SkBlitter * blitter,bool fillInner)507 static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
508 bool fillInner) {
509 // check for empty now that we're in our reduced precision space
510 if (L >= R || T >= B) {
511 return;
512 }
513 int top = T >> 8;
514 if (top == ((B - 1) >> 8)) { // just one scanline high
515 do_scanline(L, top, R, B - T - 1, blitter);
516 return;
517 }
518
519 if (T & 0xFF) {
520 do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
521 top += 1;
522 }
523
524 int bot = B >> 8;
525 int height = bot - top;
526 if (height > 0) {
527 int left = L >> 8;
528 if (left == ((R - 1) >> 8)) { // just 1-pixel wide
529 blitter->blitV(left, top, height, R - L - 1);
530 } else {
531 if (L & 0xFF) {
532 blitter->blitV(left, top, height, 256 - (L & 0xFF));
533 left += 1;
534 }
535 int rite = R >> 8;
536 int width = rite - left;
537 if (width > 0 && fillInner) {
538 blitter->blitRect(left, top, width, height);
539 }
540 if (R & 0xFF) {
541 blitter->blitV(rite, top, height, R & 0xFF);
542 }
543 }
544 }
545
546 if (B & 0xFF) {
547 do_scanline(L, bot, R, B & 0xFF, blitter);
548 }
549 }
550
antifillrect(const SkXRect & xr,SkBlitter * blitter)551 static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
552 antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
553 SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
554 blitter, true);
555 }
556
557 ///////////////////////////////////////////////////////////////////////////////
558
AntiFillXRect(const SkXRect & xr,const SkRegion * clip,SkBlitter * blitter)559 void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
560 SkBlitter* blitter) {
561 if (NULL == clip) {
562 antifillrect(xr, blitter);
563 } else {
564 SkIRect outerBounds;
565 XRect_roundOut(xr, &outerBounds);
566
567 if (clip->isRect()) {
568 const SkIRect& clipBounds = clip->getBounds();
569
570 if (clipBounds.contains(outerBounds)) {
571 antifillrect(xr, blitter);
572 } else {
573 SkXRect tmpR;
574 // this keeps our original edges fractional
575 XRect_set(&tmpR, clipBounds);
576 if (tmpR.intersect(xr)) {
577 antifillrect(tmpR, blitter);
578 }
579 }
580 } else {
581 SkRegion::Cliperator clipper(*clip, outerBounds);
582 const SkIRect& rr = clipper.rect();
583
584 while (!clipper.done()) {
585 SkXRect tmpR;
586
587 // this keeps our original edges fractional
588 XRect_set(&tmpR, rr);
589 if (tmpR.intersect(xr)) {
590 antifillrect(tmpR, blitter);
591 }
592 clipper.next();
593 }
594 }
595 }
596 }
597
AntiFillXRect(const SkXRect & xr,const SkRasterClip & clip,SkBlitter * blitter)598 void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
599 SkBlitter* blitter) {
600 if (clip.isBW()) {
601 AntiFillXRect(xr, &clip.bwRgn(), blitter);
602 } else {
603 SkIRect outerBounds;
604 XRect_roundOut(xr, &outerBounds);
605
606 if (clip.quickContains(outerBounds)) {
607 AntiFillXRect(xr, NULL, blitter);
608 } else {
609 SkAAClipBlitterWrapper wrapper(clip, blitter);
610 blitter = wrapper.getBlitter();
611
612 AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
613 }
614 }
615 }
616
617 #ifdef SK_SCALAR_IS_FLOAT
618
619 /* This guy takes a float-rect, but with the key improvement that it has
620 already been clipped, so we know that it is safe to convert it into a
621 XRect (fixedpoint), as it won't overflow.
622 */
antifillrect(const SkRect & r,SkBlitter * blitter)623 static void antifillrect(const SkRect& r, SkBlitter* blitter) {
624 SkXRect xr;
625
626 XRect_set(&xr, r);
627 antifillrect(xr, blitter);
628 }
629
630 /* We repeat the clipping logic of AntiFillXRect because the float rect might
631 overflow if we blindly converted it to an XRect. This sucks that we have to
632 repeat the clipping logic, but I don't see how to share the code/logic.
633
634 We clip r (as needed) into one or more (smaller) float rects, and then pass
635 those to our version of antifillrect, which converts it into an XRect and
636 then calls the blit.
637 */
AntiFillRect(const SkRect & origR,const SkRegion * clip,SkBlitter * blitter)638 void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
639 SkBlitter* blitter) {
640 if (clip) {
641 SkRect newR;
642 newR.set(clip->getBounds());
643 if (!newR.intersect(origR)) {
644 return;
645 }
646
647 SkIRect outerBounds;
648 newR.roundOut(&outerBounds);
649
650 if (clip->isRect()) {
651 antifillrect(newR, blitter);
652 } else {
653 SkRegion::Cliperator clipper(*clip, outerBounds);
654 while (!clipper.done()) {
655 newR.set(clipper.rect());
656 if (newR.intersect(origR)) {
657 antifillrect(newR, blitter);
658 }
659 clipper.next();
660 }
661 }
662 } else {
663 antifillrect(origR, blitter);
664 }
665 }
666
AntiFillRect(const SkRect & r,const SkRasterClip & clip,SkBlitter * blitter)667 void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
668 SkBlitter* blitter) {
669 if (clip.isBW()) {
670 AntiFillRect(r, &clip.bwRgn(), blitter);
671 } else {
672 SkAAClipBlitterWrapper wrap(clip, blitter);
673 AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
674 }
675 }
676
677 #endif // SK_SCALAR_IS_FLOAT
678
679 ///////////////////////////////////////////////////////////////////////////////
680
681 #define SkAlphaMulRound(a, b) SkMulDiv255Round(a, b)
682
683 // calls blitRect() if the rectangle is non-empty
fillcheckrect(int L,int T,int R,int B,SkBlitter * blitter)684 static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
685 if (L < R && T < B) {
686 blitter->blitRect(L, T, R - L, B - T);
687 }
688 }
689
SkScalarToFDot8(SkScalar x)690 static inline FDot8 SkScalarToFDot8(SkScalar x) {
691 #ifdef SK_SCALAR_IS_FLOAT
692 return (int)(x * 256);
693 #else
694 return x >> 8;
695 #endif
696 }
697
FDot8Floor(FDot8 x)698 static inline int FDot8Floor(FDot8 x) {
699 return x >> 8;
700 }
701
FDot8Ceil(FDot8 x)702 static inline int FDot8Ceil(FDot8 x) {
703 return (x + 0xFF) >> 8;
704 }
705
706 // 1 - (1 - a)*(1 - b)
InvAlphaMul(U8CPU a,U8CPU b)707 static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
708 // need precise rounding (not just SkAlphaMul) so that values like
709 // a=228, b=252 don't overflow the result
710 return SkToU8(a + b - SkAlphaMulRound(a, b));
711 }
712
inner_scanline(FDot8 L,int top,FDot8 R,U8CPU alpha,SkBlitter * blitter)713 static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
714 SkBlitter* blitter) {
715 SkASSERT(L < R);
716
717 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
718 blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L));
719 return;
720 }
721
722 int left = L >> 8;
723 if (L & 0xFF) {
724 blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
725 left += 1;
726 }
727
728 int rite = R >> 8;
729 int width = rite - left;
730 if (width > 0) {
731 call_hline_blitter(blitter, left, top, width, alpha);
732 }
733
734 if (R & 0xFF) {
735 blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
736 }
737 }
738
innerstrokedot8(FDot8 L,FDot8 T,FDot8 R,FDot8 B,SkBlitter * blitter)739 static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
740 SkBlitter* blitter) {
741 SkASSERT(L < R && T < B);
742
743 int top = T >> 8;
744 if (top == ((B - 1) >> 8)) { // just one scanline high
745 inner_scanline(L, top, R, B - T, blitter);
746 return;
747 }
748
749 if (T & 0xFF) {
750 inner_scanline(L, top, R, T & 0xFF, blitter);
751 top += 1;
752 }
753
754 int bot = B >> 8;
755 int height = bot - top;
756 if (height > 0) {
757 if (L & 0xFF) {
758 blitter->blitV(L >> 8, top, height, L & 0xFF);
759 }
760 if (R & 0xFF) {
761 blitter->blitV(R >> 8, top, height, ~R & 0xFF);
762 }
763 }
764
765 if (B & 0xFF) {
766 inner_scanline(L, bot, R, ~B & 0xFF, blitter);
767 }
768 }
769
AntiFrameRect(const SkRect & r,const SkPoint & strokeSize,const SkRegion * clip,SkBlitter * blitter)770 void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
771 const SkRegion* clip, SkBlitter* blitter) {
772 SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
773
774 SkScalar rx = SkScalarHalf(strokeSize.fX);
775 SkScalar ry = SkScalarHalf(strokeSize.fY);
776
777 // outset by the radius
778 FDot8 L = SkScalarToFDot8(r.fLeft - rx);
779 FDot8 T = SkScalarToFDot8(r.fTop - ry);
780 FDot8 R = SkScalarToFDot8(r.fRight + rx);
781 FDot8 B = SkScalarToFDot8(r.fBottom + ry);
782
783 SkIRect outer;
784 // set outer to the outer rect of the outer section
785 outer.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
786
787 SkBlitterClipper clipper;
788 if (clip) {
789 if (clip->quickReject(outer)) {
790 return;
791 }
792 if (!clip->contains(outer)) {
793 blitter = clipper.apply(blitter, clip, &outer);
794 }
795 // now we can ignore clip for the rest of the function
796 }
797
798 // stroke the outer hull
799 antifilldot8(L, T, R, B, blitter, false);
800
801 // set outer to the outer rect of the middle section
802 outer.set(FDot8Ceil(L), FDot8Ceil(T), FDot8Floor(R), FDot8Floor(B));
803
804 // in case we lost a bit with diameter/2
805 rx = strokeSize.fX - rx;
806 ry = strokeSize.fY - ry;
807 // inset by the radius
808 L = SkScalarToFDot8(r.fLeft + rx);
809 T = SkScalarToFDot8(r.fTop + ry);
810 R = SkScalarToFDot8(r.fRight - rx);
811 B = SkScalarToFDot8(r.fBottom - ry);
812
813 if (L >= R || T >= B) {
814 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
815 blitter);
816 } else {
817 SkIRect inner;
818 // set inner to the inner rect of the middle section
819 inner.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
820
821 // draw the frame in 4 pieces
822 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
823 blitter);
824 fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
825 blitter);
826 fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
827 blitter);
828 fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
829 blitter);
830
831 // now stroke the inner rect, which is similar to antifilldot8() except that
832 // it treats the fractional coordinates with the inverse bias (since its
833 // inner).
834 innerstrokedot8(L, T, R, B, blitter);
835 }
836 }
837
AntiFrameRect(const SkRect & r,const SkPoint & strokeSize,const SkRasterClip & clip,SkBlitter * blitter)838 void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
839 const SkRasterClip& clip, SkBlitter* blitter) {
840 if (clip.isBW()) {
841 AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
842 } else {
843 SkAAClipBlitterWrapper wrap(clip, blitter);
844 AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
845 }
846 }
847
848