1 /*
2 * Copyright 2011 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8
9 #include "SkScan.h"
10 #include "SkBlitter.h"
11 #include "SkColorPriv.h"
12 #include "SkLineClipper.h"
13 #include "SkRasterClip.h"
14 #include "SkFDot6.h"
15
16 /* Our attempt to compute the worst case "bounds" for the horizontal and
17 vertical cases has some numerical bug in it, and we sometimes undervalue
18 our extends. The bug is that when this happens, we will set the clip to
19 nullptr (for speed), and thus draw outside of the clip by a pixel, which might
20 only look bad, but it might also access memory outside of the valid range
21 allcoated for the device bitmap.
22
23 This define enables our fix to outset our "bounds" by 1, thus avoiding the
24 chance of the bug, but at the cost of sometimes taking the rectblitter
25 case (i.e. not setting the clip to nullptr) when we might not actually need
26 to. If we can improve/fix the actual calculations, then we can remove this
27 step.
28 */
29 #define OUTSET_BEFORE_CLIP_TEST true
30
31 #define HLINE_STACK_BUFFER 100
32
SmallDot6Scale(int value,int dot6)33 static inline int SmallDot6Scale(int value, int dot6) {
34 SkASSERT((int16_t)value == value);
35 SkASSERT((unsigned)dot6 <= 64);
36 return (value * dot6) >> 6;
37 }
38
39 //#define TEST_GAMMA
40
41 #ifdef TEST_GAMMA
42 static uint8_t gGammaTable[256];
43 #define ApplyGamma(table, alpha) (table)[alpha]
44
build_gamma_table()45 static void build_gamma_table() {
46 static bool gInit = false;
47
48 if (gInit == false) {
49 for (int i = 0; i < 256; i++) {
50 SkFixed n = i * 257;
51 n += n >> 15;
52 SkASSERT(n >= 0 && n <= SK_Fixed1);
53 n = SkFixedSqrt(n);
54 n = n * 255 >> 16;
55 // SkDebugf("morph %d -> %d\n", i, n);
56 gGammaTable[i] = SkToU8(n);
57 }
58 gInit = true;
59 }
60 }
61 #else
62 #define ApplyGamma(table, alpha) SkToU8(alpha)
63 #endif
64
65 ///////////////////////////////////////////////////////////////////////////////
66
call_hline_blitter(SkBlitter * blitter,int x,int y,int count,U8CPU alpha)67 static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
68 U8CPU alpha) {
69 SkASSERT(count > 0);
70
71 int16_t runs[HLINE_STACK_BUFFER + 1];
72 uint8_t aa[HLINE_STACK_BUFFER];
73
74 aa[0] = ApplyGamma(gGammaTable, alpha);
75 do {
76 int n = count;
77 if (n > HLINE_STACK_BUFFER) {
78 n = HLINE_STACK_BUFFER;
79 }
80 runs[0] = SkToS16(n);
81 runs[n] = 0;
82 blitter->blitAntiH(x, y, aa, runs);
83 x += n;
84 count -= n;
85 } while (count > 0);
86 }
87
88 class SkAntiHairBlitter {
89 public:
SkAntiHairBlitter()90 SkAntiHairBlitter() : fBlitter(nullptr) {}
~SkAntiHairBlitter()91 virtual ~SkAntiHairBlitter() {}
92
getBlitter() const93 SkBlitter* getBlitter() const { return fBlitter; }
94
setup(SkBlitter * blitter)95 void setup(SkBlitter* blitter) {
96 fBlitter = blitter;
97 }
98
99 virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) = 0;
100 virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed slope) = 0;
101
102 private:
103 SkBlitter* fBlitter;
104 };
105
106 class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
107 public:
drawCap(int x,SkFixed fy,SkFixed slope,int mod64)108 SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) override {
109 fy += SK_Fixed1/2;
110
111 int y = fy >> 16;
112 uint8_t a = (uint8_t)(fy >> 8);
113
114 // lower line
115 unsigned ma = SmallDot6Scale(a, mod64);
116 if (ma) {
117 call_hline_blitter(this->getBlitter(), x, y, 1, ma);
118 }
119
120 // upper line
121 ma = SmallDot6Scale(255 - a, mod64);
122 if (ma) {
123 call_hline_blitter(this->getBlitter(), x, y - 1, 1, ma);
124 }
125
126 return fy - SK_Fixed1/2;
127 }
128
drawLine(int x,int stopx,SkFixed fy,SkFixed slope)129 virtual SkFixed drawLine(int x, int stopx, SkFixed fy,
130 SkFixed slope) override {
131 SkASSERT(x < stopx);
132 int count = stopx - x;
133 fy += SK_Fixed1/2;
134
135 int y = fy >> 16;
136 uint8_t a = (uint8_t)(fy >> 8);
137
138 // lower line
139 if (a) {
140 call_hline_blitter(this->getBlitter(), x, y, count, a);
141 }
142
143 // upper line
144 a = 255 - a;
145 if (a) {
146 call_hline_blitter(this->getBlitter(), x, y - 1, count, a);
147 }
148
149 return fy - SK_Fixed1/2;
150 }
151 };
152
153 class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
154 public:
drawCap(int x,SkFixed fy,SkFixed dy,int mod64)155 SkFixed drawCap(int x, SkFixed fy, SkFixed dy, int mod64) override {
156 fy += SK_Fixed1/2;
157
158 int lower_y = fy >> 16;
159 uint8_t a = (uint8_t)(fy >> 8);
160 unsigned a0 = SmallDot6Scale(255 - a, mod64);
161 unsigned a1 = SmallDot6Scale(a, mod64);
162 this->getBlitter()->blitAntiV2(x, lower_y - 1, a0, a1);
163
164 return fy + dy - SK_Fixed1/2;
165 }
166
drawLine(int x,int stopx,SkFixed fy,SkFixed dy)167 SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) override {
168 SkASSERT(x < stopx);
169
170 fy += SK_Fixed1/2;
171 SkBlitter* blitter = this->getBlitter();
172 do {
173 int lower_y = fy >> 16;
174 uint8_t a = (uint8_t)(fy >> 8);
175 blitter->blitAntiV2(x, lower_y - 1, 255 - a, a);
176 fy += dy;
177 } while (++x < stopx);
178
179 return fy - SK_Fixed1/2;
180 }
181 };
182
183 class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
184 public:
drawCap(int y,SkFixed fx,SkFixed dx,int mod64)185 SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override {
186 SkASSERT(0 == dx);
187 fx += SK_Fixed1/2;
188
189 int x = fx >> 16;
190 int a = (uint8_t)(fx >> 8);
191
192 unsigned ma = SmallDot6Scale(a, mod64);
193 if (ma) {
194 this->getBlitter()->blitV(x, y, 1, ma);
195 }
196 ma = SmallDot6Scale(255 - a, mod64);
197 if (ma) {
198 this->getBlitter()->blitV(x - 1, y, 1, ma);
199 }
200
201 return fx - SK_Fixed1/2;
202 }
203
drawLine(int y,int stopy,SkFixed fx,SkFixed dx)204 SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
205 SkASSERT(y < stopy);
206 SkASSERT(0 == dx);
207 fx += SK_Fixed1/2;
208
209 int x = fx >> 16;
210 int a = (uint8_t)(fx >> 8);
211
212 if (a) {
213 this->getBlitter()->blitV(x, y, stopy - y, a);
214 }
215 a = 255 - a;
216 if (a) {
217 this->getBlitter()->blitV(x - 1, y, stopy - y, a);
218 }
219
220 return fx - SK_Fixed1/2;
221 }
222 };
223
224 class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
225 public:
drawCap(int y,SkFixed fx,SkFixed dx,int mod64)226 SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override {
227 fx += SK_Fixed1/2;
228
229 int x = fx >> 16;
230 uint8_t a = (uint8_t)(fx >> 8);
231 this->getBlitter()->blitAntiH2(x - 1, y,
232 SmallDot6Scale(255 - a, mod64), SmallDot6Scale(a, mod64));
233
234 return fx + dx - SK_Fixed1/2;
235 }
236
drawLine(int y,int stopy,SkFixed fx,SkFixed dx)237 SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
238 SkASSERT(y < stopy);
239 fx += SK_Fixed1/2;
240 do {
241 int x = fx >> 16;
242 uint8_t a = (uint8_t)(fx >> 8);
243 this->getBlitter()->blitAntiH2(x - 1, y, 255 - a, a);
244 fx += dx;
245 } while (++y < stopy);
246
247 return fx - SK_Fixed1/2;
248 }
249 };
250
fastfixdiv(SkFDot6 a,SkFDot6 b)251 static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
252 SkASSERT((SkLeftShift(a, 16) >> 16) == a);
253 SkASSERT(b != 0);
254 return SkLeftShift(a, 16) / b;
255 }
256
257 #define SkBITCOUNT(x) (sizeof(x) << 3)
258
259 #if 1
260 // returns high-bit set iff x==0x8000...
bad_int(int x)261 static inline int bad_int(int x) {
262 return x & -x;
263 }
264
any_bad_ints(int a,int b,int c,int d)265 static int any_bad_ints(int a, int b, int c, int d) {
266 return (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> (SkBITCOUNT(int) - 1);
267 }
268 #else
good_int(int x)269 static inline int good_int(int x) {
270 return x ^ (1 << (SkBITCOUNT(x) - 1));
271 }
272
any_bad_ints(int a,int b,int c,int d)273 static int any_bad_ints(int a, int b, int c, int d) {
274 return !(good_int(a) & good_int(b) & good_int(c) & good_int(d));
275 }
276 #endif
277
278 #ifdef SK_DEBUG
canConvertFDot6ToFixed(SkFDot6 x)279 static bool canConvertFDot6ToFixed(SkFDot6 x) {
280 const int maxDot6 = SK_MaxS32 >> (16 - 6);
281 return SkAbs32(x) <= maxDot6;
282 }
283 #endif
284
285 /*
286 * We want the fractional part of ordinate, but we want multiples of 64 to
287 * return 64, not 0, so we can't just say (ordinate & 63).
288 * We basically want to compute those bits, and if they're 0, return 64.
289 * We can do that w/o a branch with an extra sub and add.
290 */
contribution_64(SkFDot6 ordinate)291 static int contribution_64(SkFDot6 ordinate) {
292 #if 0
293 int result = ordinate & 63;
294 if (0 == result) {
295 result = 64;
296 }
297 #else
298 int result = ((ordinate - 1) & 63) + 1;
299 #endif
300 SkASSERT(result > 0 && result <= 64);
301 return result;
302 }
303
do_anti_hairline(SkFDot6 x0,SkFDot6 y0,SkFDot6 x1,SkFDot6 y1,const SkIRect * clip,SkBlitter * blitter)304 static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
305 const SkIRect* clip, SkBlitter* blitter) {
306 // check for integer NaN (0x80000000) which we can't handle (can't negate it)
307 // It appears typically from a huge float (inf or nan) being converted to int.
308 // If we see it, just don't draw.
309 if (any_bad_ints(x0, y0, x1, y1)) {
310 return;
311 }
312
313 // The caller must clip the line to [-32767.0 ... 32767.0] ahead of time
314 // (in dot6 format)
315 SkASSERT(canConvertFDot6ToFixed(x0));
316 SkASSERT(canConvertFDot6ToFixed(y0));
317 SkASSERT(canConvertFDot6ToFixed(x1));
318 SkASSERT(canConvertFDot6ToFixed(y1));
319
320 if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) {
321 /* instead of (x0 + x1) >> 1, we shift each separately. This is less
322 precise, but avoids overflowing the intermediate result if the
323 values are huge. A better fix might be to clip the original pts
324 directly (i.e. do the divide), so we don't spend time subdividing
325 huge lines at all.
326 */
327 int hx = (x0 >> 1) + (x1 >> 1);
328 int hy = (y0 >> 1) + (y1 >> 1);
329 do_anti_hairline(x0, y0, hx, hy, clip, blitter);
330 do_anti_hairline(hx, hy, x1, y1, clip, blitter);
331 return;
332 }
333
334 int scaleStart, scaleStop;
335 int istart, istop;
336 SkFixed fstart, slope;
337
338 HLine_SkAntiHairBlitter hline_blitter;
339 Horish_SkAntiHairBlitter horish_blitter;
340 VLine_SkAntiHairBlitter vline_blitter;
341 Vertish_SkAntiHairBlitter vertish_blitter;
342 SkAntiHairBlitter* hairBlitter = nullptr;
343
344 if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) { // mostly horizontal
345 if (x0 > x1) { // we want to go left-to-right
346 SkTSwap<SkFDot6>(x0, x1);
347 SkTSwap<SkFDot6>(y0, y1);
348 }
349
350 istart = SkFDot6Floor(x0);
351 istop = SkFDot6Ceil(x1);
352 fstart = SkFDot6ToFixed(y0);
353 if (y0 == y1) { // completely horizontal, take fast case
354 slope = 0;
355 hairBlitter = &hline_blitter;
356 } else {
357 slope = fastfixdiv(y1 - y0, x1 - x0);
358 SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
359 fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
360 hairBlitter = &horish_blitter;
361 }
362
363 SkASSERT(istop > istart);
364 if (istop - istart == 1) {
365 // we are within a single pixel
366 scaleStart = x1 - x0;
367 SkASSERT(scaleStart >= 0 && scaleStart <= 64);
368 scaleStop = 0;
369 } else {
370 scaleStart = 64 - (x0 & 63);
371 scaleStop = x1 & 63;
372 }
373
374 if (clip){
375 if (istart >= clip->fRight || istop <= clip->fLeft) {
376 return;
377 }
378 if (istart < clip->fLeft) {
379 fstart += slope * (clip->fLeft - istart);
380 istart = clip->fLeft;
381 scaleStart = 64;
382 if (istop - istart == 1) {
383 // we are within a single pixel
384 scaleStart = contribution_64(x1);
385 scaleStop = 0;
386 }
387 }
388 if (istop > clip->fRight) {
389 istop = clip->fRight;
390 scaleStop = 0; // so we don't draw this last column
391 }
392
393 SkASSERT(istart <= istop);
394 if (istart == istop) {
395 return;
396 }
397 // now test if our Y values are completely inside the clip
398 int top, bottom;
399 if (slope >= 0) { // T2B
400 top = SkFixedFloorToInt(fstart - SK_FixedHalf);
401 bottom = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
402 } else { // B2T
403 bottom = SkFixedCeilToInt(fstart + SK_FixedHalf);
404 top = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
405 }
406 #ifdef OUTSET_BEFORE_CLIP_TEST
407 top -= 1;
408 bottom += 1;
409 #endif
410 if (top >= clip->fBottom || bottom <= clip->fTop) {
411 return;
412 }
413 if (clip->fTop <= top && clip->fBottom >= bottom) {
414 clip = nullptr;
415 }
416 }
417 } else { // mostly vertical
418 if (y0 > y1) { // we want to go top-to-bottom
419 SkTSwap<SkFDot6>(x0, x1);
420 SkTSwap<SkFDot6>(y0, y1);
421 }
422
423 istart = SkFDot6Floor(y0);
424 istop = SkFDot6Ceil(y1);
425 fstart = SkFDot6ToFixed(x0);
426 if (x0 == x1) {
427 if (y0 == y1) { // are we zero length?
428 return; // nothing to do
429 }
430 slope = 0;
431 hairBlitter = &vline_blitter;
432 } else {
433 slope = fastfixdiv(x1 - x0, y1 - y0);
434 SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
435 fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
436 hairBlitter = &vertish_blitter;
437 }
438
439 SkASSERT(istop > istart);
440 if (istop - istart == 1) {
441 // we are within a single pixel
442 scaleStart = y1 - y0;
443 SkASSERT(scaleStart >= 0 && scaleStart <= 64);
444 scaleStop = 0;
445 } else {
446 scaleStart = 64 - (y0 & 63);
447 scaleStop = y1 & 63;
448 }
449
450 if (clip) {
451 if (istart >= clip->fBottom || istop <= clip->fTop) {
452 return;
453 }
454 if (istart < clip->fTop) {
455 fstart += slope * (clip->fTop - istart);
456 istart = clip->fTop;
457 scaleStart = 64;
458 if (istop - istart == 1) {
459 // we are within a single pixel
460 scaleStart = contribution_64(y1);
461 scaleStop = 0;
462 }
463 }
464 if (istop > clip->fBottom) {
465 istop = clip->fBottom;
466 scaleStop = 0; // so we don't draw this last row
467 }
468
469 SkASSERT(istart <= istop);
470 if (istart == istop)
471 return;
472
473 // now test if our X values are completely inside the clip
474 int left, right;
475 if (slope >= 0) { // L2R
476 left = SkFixedFloorToInt(fstart - SK_FixedHalf);
477 right = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
478 } else { // R2L
479 right = SkFixedCeilToInt(fstart + SK_FixedHalf);
480 left = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
481 }
482 #ifdef OUTSET_BEFORE_CLIP_TEST
483 left -= 1;
484 right += 1;
485 #endif
486 if (left >= clip->fRight || right <= clip->fLeft) {
487 return;
488 }
489 if (clip->fLeft <= left && clip->fRight >= right) {
490 clip = nullptr;
491 }
492 }
493 }
494
495 SkRectClipBlitter rectClipper;
496 if (clip) {
497 rectClipper.init(blitter, *clip);
498 blitter = &rectClipper;
499 }
500
501 SkASSERT(hairBlitter);
502 hairBlitter->setup(blitter);
503
504 #ifdef SK_DEBUG
505 if (scaleStart > 0 && scaleStop > 0) {
506 // be sure we don't draw twice in the same pixel
507 SkASSERT(istart < istop - 1);
508 }
509 #endif
510
511 fstart = hairBlitter->drawCap(istart, fstart, slope, scaleStart);
512 istart += 1;
513 int fullSpans = istop - istart - (scaleStop > 0);
514 if (fullSpans > 0) {
515 fstart = hairBlitter->drawLine(istart, istart + fullSpans, fstart, slope);
516 }
517 if (scaleStop > 0) {
518 hairBlitter->drawCap(istop - 1, fstart, slope, scaleStop);
519 }
520 }
521
AntiHairLineRgn(const SkPoint array[],int arrayCount,const SkRegion * clip,SkBlitter * blitter)522 void SkScan::AntiHairLineRgn(const SkPoint array[], int arrayCount, const SkRegion* clip,
523 SkBlitter* blitter) {
524 if (clip && clip->isEmpty()) {
525 return;
526 }
527
528 SkASSERT(clip == nullptr || !clip->getBounds().isEmpty());
529
530 #ifdef TEST_GAMMA
531 build_gamma_table();
532 #endif
533
534 const SkScalar max = SkIntToScalar(32767);
535 const SkRect fixedBounds = SkRect::MakeLTRB(-max, -max, max, max);
536
537 SkRect clipBounds;
538 if (clip) {
539 clipBounds.set(clip->getBounds());
540 /* We perform integral clipping later on, but we do a scalar clip first
541 to ensure that our coordinates are expressible in fixed/integers.
542
543 antialiased hairlines can draw up to 1/2 of a pixel outside of
544 their bounds, so we need to outset the clip before calling the
545 clipper. To make the numerics safer, we outset by a whole pixel,
546 since the 1/2 pixel boundary is important to the antihair blitter,
547 we don't want to risk numerical fate by chopping on that edge.
548 */
549 clipBounds.outset(SK_Scalar1, SK_Scalar1);
550 }
551
552 for (int i = 0; i < arrayCount - 1; ++i) {
553 SkPoint pts[2];
554
555 // We have to pre-clip the line to fit in a SkFixed, so we just chop
556 // the line. TODO find a way to actually draw beyond that range.
557 if (!SkLineClipper::IntersectLine(&array[i], fixedBounds, pts)) {
558 continue;
559 }
560
561 if (clip && !SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
562 continue;
563 }
564
565 SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
566 SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
567 SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
568 SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
569
570 if (clip) {
571 SkFDot6 left = SkMin32(x0, x1);
572 SkFDot6 top = SkMin32(y0, y1);
573 SkFDot6 right = SkMax32(x0, x1);
574 SkFDot6 bottom = SkMax32(y0, y1);
575 SkIRect ir;
576
577 ir.set( SkFDot6Floor(left) - 1,
578 SkFDot6Floor(top) - 1,
579 SkFDot6Ceil(right) + 1,
580 SkFDot6Ceil(bottom) + 1);
581
582 if (clip->quickReject(ir)) {
583 continue;
584 }
585 if (!clip->quickContains(ir)) {
586 SkRegion::Cliperator iter(*clip, ir);
587 const SkIRect* r = &iter.rect();
588
589 while (!iter.done()) {
590 do_anti_hairline(x0, y0, x1, y1, r, blitter);
591 iter.next();
592 }
593 continue;
594 }
595 // fall through to no-clip case
596 }
597 do_anti_hairline(x0, y0, x1, y1, nullptr, blitter);
598 }
599 }
600
AntiHairRect(const SkRect & rect,const SkRasterClip & clip,SkBlitter * blitter)601 void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
602 SkBlitter* blitter) {
603 SkPoint pts[5];
604
605 pts[0].set(rect.fLeft, rect.fTop);
606 pts[1].set(rect.fRight, rect.fTop);
607 pts[2].set(rect.fRight, rect.fBottom);
608 pts[3].set(rect.fLeft, rect.fBottom);
609 pts[4] = pts[0];
610 SkScan::AntiHairLine(pts, 5, clip, blitter);
611 }
612
613 ///////////////////////////////////////////////////////////////////////////////
614
615 typedef int FDot8; // 24.8 integer fixed point
616
SkFixedToFDot8(SkFixed x)617 static inline FDot8 SkFixedToFDot8(SkFixed x) {
618 return (x + 0x80) >> 8;
619 }
620
do_scanline(FDot8 L,int top,FDot8 R,U8CPU alpha,SkBlitter * blitter)621 static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
622 SkBlitter* blitter) {
623 SkASSERT(L < R);
624
625 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
626 blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
627 return;
628 }
629
630 int left = L >> 8;
631
632 if (L & 0xFF) {
633 blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
634 left += 1;
635 }
636
637 int rite = R >> 8;
638 int width = rite - left;
639 if (width > 0) {
640 call_hline_blitter(blitter, left, top, width, alpha);
641 }
642 if (R & 0xFF) {
643 blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
644 }
645 }
646
antifilldot8(FDot8 L,FDot8 T,FDot8 R,FDot8 B,SkBlitter * blitter,bool fillInner)647 static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
648 bool fillInner) {
649 // check for empty now that we're in our reduced precision space
650 if (L >= R || T >= B) {
651 return;
652 }
653 int top = T >> 8;
654 if (top == ((B - 1) >> 8)) { // just one scanline high
655 do_scanline(L, top, R, B - T - 1, blitter);
656 return;
657 }
658
659 if (T & 0xFF) {
660 do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
661 top += 1;
662 }
663
664 int bot = B >> 8;
665 int height = bot - top;
666 if (height > 0) {
667 int left = L >> 8;
668 if (left == ((R - 1) >> 8)) { // just 1-pixel wide
669 blitter->blitV(left, top, height, R - L - 1);
670 } else {
671 if (L & 0xFF) {
672 blitter->blitV(left, top, height, 256 - (L & 0xFF));
673 left += 1;
674 }
675 int rite = R >> 8;
676 int width = rite - left;
677 if (width > 0 && fillInner) {
678 blitter->blitRect(left, top, width, height);
679 }
680 if (R & 0xFF) {
681 blitter->blitV(rite, top, height, R & 0xFF);
682 }
683 }
684 }
685
686 if (B & 0xFF) {
687 do_scanline(L, bot, R, B & 0xFF, blitter);
688 }
689 }
690
antifillrect(const SkXRect & xr,SkBlitter * blitter)691 static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
692 antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
693 SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
694 blitter, true);
695 }
696
697 ///////////////////////////////////////////////////////////////////////////////
698
AntiFillXRect(const SkXRect & xr,const SkRegion * clip,SkBlitter * blitter)699 void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
700 SkBlitter* blitter) {
701 if (nullptr == clip) {
702 antifillrect(xr, blitter);
703 } else {
704 SkIRect outerBounds;
705 XRect_roundOut(xr, &outerBounds);
706
707 if (clip->isRect()) {
708 const SkIRect& clipBounds = clip->getBounds();
709
710 if (clipBounds.contains(outerBounds)) {
711 antifillrect(xr, blitter);
712 } else {
713 SkXRect tmpR;
714 // this keeps our original edges fractional
715 XRect_set(&tmpR, clipBounds);
716 if (tmpR.intersect(xr)) {
717 antifillrect(tmpR, blitter);
718 }
719 }
720 } else {
721 SkRegion::Cliperator clipper(*clip, outerBounds);
722 const SkIRect& rr = clipper.rect();
723
724 while (!clipper.done()) {
725 SkXRect tmpR;
726
727 // this keeps our original edges fractional
728 XRect_set(&tmpR, rr);
729 if (tmpR.intersect(xr)) {
730 antifillrect(tmpR, blitter);
731 }
732 clipper.next();
733 }
734 }
735 }
736 }
737
AntiFillXRect(const SkXRect & xr,const SkRasterClip & clip,SkBlitter * blitter)738 void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
739 SkBlitter* blitter) {
740 if (clip.isBW()) {
741 AntiFillXRect(xr, &clip.bwRgn(), blitter);
742 } else {
743 SkIRect outerBounds;
744 XRect_roundOut(xr, &outerBounds);
745
746 if (clip.quickContains(outerBounds)) {
747 AntiFillXRect(xr, nullptr, blitter);
748 } else {
749 SkAAClipBlitterWrapper wrapper(clip, blitter);
750 AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
751 }
752 }
753 }
754
755 /* This guy takes a float-rect, but with the key improvement that it has
756 already been clipped, so we know that it is safe to convert it into a
757 XRect (fixedpoint), as it won't overflow.
758 */
antifillrect(const SkRect & r,SkBlitter * blitter)759 static void antifillrect(const SkRect& r, SkBlitter* blitter) {
760 SkXRect xr;
761
762 XRect_set(&xr, r);
763 antifillrect(xr, blitter);
764 }
765
766 /* We repeat the clipping logic of AntiFillXRect because the float rect might
767 overflow if we blindly converted it to an XRect. This sucks that we have to
768 repeat the clipping logic, but I don't see how to share the code/logic.
769
770 We clip r (as needed) into one or more (smaller) float rects, and then pass
771 those to our version of antifillrect, which converts it into an XRect and
772 then calls the blit.
773 */
AntiFillRect(const SkRect & origR,const SkRegion * clip,SkBlitter * blitter)774 void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
775 SkBlitter* blitter) {
776 if (clip) {
777 SkRect newR;
778 newR.set(clip->getBounds());
779 if (!newR.intersect(origR)) {
780 return;
781 }
782
783 const SkIRect outerBounds = newR.roundOut();
784
785 if (clip->isRect()) {
786 antifillrect(newR, blitter);
787 } else {
788 SkRegion::Cliperator clipper(*clip, outerBounds);
789 while (!clipper.done()) {
790 newR.set(clipper.rect());
791 if (newR.intersect(origR)) {
792 antifillrect(newR, blitter);
793 }
794 clipper.next();
795 }
796 }
797 } else {
798 antifillrect(origR, blitter);
799 }
800 }
801
AntiFillRect(const SkRect & r,const SkRasterClip & clip,SkBlitter * blitter)802 void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
803 SkBlitter* blitter) {
804 if (clip.isBW()) {
805 AntiFillRect(r, &clip.bwRgn(), blitter);
806 } else {
807 SkAAClipBlitterWrapper wrap(clip, blitter);
808 AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
809 }
810 }
811
812 ///////////////////////////////////////////////////////////////////////////////
813
814 #define SkAlphaMulRound(a, b) SkMulDiv255Round(a, b)
815
816 // calls blitRect() if the rectangle is non-empty
fillcheckrect(int L,int T,int R,int B,SkBlitter * blitter)817 static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
818 if (L < R && T < B) {
819 blitter->blitRect(L, T, R - L, B - T);
820 }
821 }
822
SkScalarToFDot8(SkScalar x)823 static inline FDot8 SkScalarToFDot8(SkScalar x) {
824 return (int)(x * 256);
825 }
826
FDot8Floor(FDot8 x)827 static inline int FDot8Floor(FDot8 x) {
828 return x >> 8;
829 }
830
FDot8Ceil(FDot8 x)831 static inline int FDot8Ceil(FDot8 x) {
832 return (x + 0xFF) >> 8;
833 }
834
835 // 1 - (1 - a)*(1 - b)
InvAlphaMul(U8CPU a,U8CPU b)836 static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
837 // need precise rounding (not just SkAlphaMul) so that values like
838 // a=228, b=252 don't overflow the result
839 return SkToU8(a + b - SkAlphaMulRound(a, b));
840 }
841
inner_scanline(FDot8 L,int top,FDot8 R,U8CPU alpha,SkBlitter * blitter)842 static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
843 SkBlitter* blitter) {
844 SkASSERT(L < R);
845
846 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
847 FDot8 widClamp = R - L;
848 // border case clamp 256 to 255 instead of going through call_hline_blitter
849 // see skbug/4406
850 widClamp = widClamp - (widClamp >> 8);
851 blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, widClamp));
852 return;
853 }
854
855 int left = L >> 8;
856 if (L & 0xFF) {
857 blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
858 left += 1;
859 }
860
861 int rite = R >> 8;
862 int width = rite - left;
863 if (width > 0) {
864 call_hline_blitter(blitter, left, top, width, alpha);
865 }
866
867 if (R & 0xFF) {
868 blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
869 }
870 }
871
innerstrokedot8(FDot8 L,FDot8 T,FDot8 R,FDot8 B,SkBlitter * blitter)872 static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
873 SkBlitter* blitter) {
874 SkASSERT(L < R && T < B);
875
876 int top = T >> 8;
877 if (top == ((B - 1) >> 8)) { // just one scanline high
878 // We want the inverse of B-T, since we're the inner-stroke
879 int alpha = 256 - (B - T);
880 if (alpha) {
881 inner_scanline(L, top, R, alpha, blitter);
882 }
883 return;
884 }
885
886 if (T & 0xFF) {
887 inner_scanline(L, top, R, T & 0xFF, blitter);
888 top += 1;
889 }
890
891 int bot = B >> 8;
892 int height = bot - top;
893 if (height > 0) {
894 if (L & 0xFF) {
895 blitter->blitV(L >> 8, top, height, L & 0xFF);
896 }
897 if (R & 0xFF) {
898 blitter->blitV(R >> 8, top, height, ~R & 0xFF);
899 }
900 }
901
902 if (B & 0xFF) {
903 inner_scanline(L, bot, R, ~B & 0xFF, blitter);
904 }
905 }
906
align_thin_stroke(FDot8 & edge1,FDot8 & edge2)907 static inline void align_thin_stroke(FDot8& edge1, FDot8& edge2) {
908 SkASSERT(edge1 <= edge2);
909
910 if (FDot8Floor(edge1) == FDot8Floor(edge2)) {
911 edge2 -= (edge1 & 0xFF);
912 edge1 &= ~0xFF;
913 }
914 }
915
AntiFrameRect(const SkRect & r,const SkPoint & strokeSize,const SkRegion * clip,SkBlitter * blitter)916 void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
917 const SkRegion* clip, SkBlitter* blitter) {
918 SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
919
920 SkScalar rx = SkScalarHalf(strokeSize.fX);
921 SkScalar ry = SkScalarHalf(strokeSize.fY);
922
923 // outset by the radius
924 FDot8 outerL = SkScalarToFDot8(r.fLeft - rx);
925 FDot8 outerT = SkScalarToFDot8(r.fTop - ry);
926 FDot8 outerR = SkScalarToFDot8(r.fRight + rx);
927 FDot8 outerB = SkScalarToFDot8(r.fBottom + ry);
928
929 SkIRect outer;
930 // set outer to the outer rect of the outer section
931 outer.set(FDot8Floor(outerL), FDot8Floor(outerT), FDot8Ceil(outerR), FDot8Ceil(outerB));
932
933 SkBlitterClipper clipper;
934 if (clip) {
935 if (clip->quickReject(outer)) {
936 return;
937 }
938 if (!clip->contains(outer)) {
939 blitter = clipper.apply(blitter, clip, &outer);
940 }
941 // now we can ignore clip for the rest of the function
942 }
943
944 // in case we lost a bit with diameter/2
945 rx = strokeSize.fX - rx;
946 ry = strokeSize.fY - ry;
947
948 // inset by the radius
949 FDot8 innerL = SkScalarToFDot8(r.fLeft + rx);
950 FDot8 innerT = SkScalarToFDot8(r.fTop + ry);
951 FDot8 innerR = SkScalarToFDot8(r.fRight - rx);
952 FDot8 innerB = SkScalarToFDot8(r.fBottom - ry);
953
954 // For sub-unit strokes, tweak the hulls such that one of the edges coincides with the pixel
955 // edge. This ensures that the general rect stroking logic below
956 // a) doesn't blit the same scanline twice
957 // b) computes the correct coverage when both edges fall within the same pixel
958 if (strokeSize.fX < 1 || strokeSize.fY < 1) {
959 align_thin_stroke(outerL, innerL);
960 align_thin_stroke(outerT, innerT);
961 align_thin_stroke(innerR, outerR);
962 align_thin_stroke(innerB, outerB);
963 }
964
965 // stroke the outer hull
966 antifilldot8(outerL, outerT, outerR, outerB, blitter, false);
967
968 // set outer to the outer rect of the middle section
969 outer.set(FDot8Ceil(outerL), FDot8Ceil(outerT), FDot8Floor(outerR), FDot8Floor(outerB));
970
971 if (innerL >= innerR || innerT >= innerB) {
972 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
973 blitter);
974 } else {
975 SkIRect inner;
976 // set inner to the inner rect of the middle section
977 inner.set(FDot8Floor(innerL), FDot8Floor(innerT), FDot8Ceil(innerR), FDot8Ceil(innerB));
978
979 // draw the frame in 4 pieces
980 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
981 blitter);
982 fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
983 blitter);
984 fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
985 blitter);
986 fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
987 blitter);
988
989 // now stroke the inner rect, which is similar to antifilldot8() except that
990 // it treats the fractional coordinates with the inverse bias (since its
991 // inner).
992 innerstrokedot8(innerL, innerT, innerR, innerB, blitter);
993 }
994 }
995
AntiFrameRect(const SkRect & r,const SkPoint & strokeSize,const SkRasterClip & clip,SkBlitter * blitter)996 void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
997 const SkRasterClip& clip, SkBlitter* blitter) {
998 if (clip.isBW()) {
999 AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
1000 } else {
1001 SkAAClipBlitterWrapper wrap(clip, blitter);
1002 AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
1003 }
1004 }
1005