Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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 */
10 #include "SkScan.h"
11 #include "SkBlitter.h"
12 #include "SkColorPriv.h"
13 #include "SkLineClipper.h"
14 #include "SkRasterClip.h"
15 #include "SkFDot6.h"
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.
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
32 #define HLINE_STACK_BUFFER 100
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 }
40 //#define TEST_GAMMA
42 #ifdef TEST_GAMMA
43 static uint8_t gGammaTable[256];
44 #define ApplyGamma(table, alpha) (table)[alpha]
46 static void build_gamma_table() {
47 static bool gInit = false;
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
66 ///////////////////////////////////////////////////////////////////////////////
68 static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
69 U8CPU alpha) {
70 SkASSERT(count > 0);
72 int16_t runs[HLINE_STACK_BUFFER + 1];
73 uint8_t aa[HLINE_STACK_BUFFER];
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 }
89 class SkAntiHairBlitter {
90 public:
91 SkAntiHairBlitter() : fBlitter(NULL) {}
92 virtual ~SkAntiHairBlitter() {}
94 SkBlitter* getBlitter() const { return fBlitter; }
96 void setup(SkBlitter* blitter) {
97 fBlitter = blitter;
98 }
100 virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) = 0;
101 virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed slope) = 0;
103 private:
104 SkBlitter* fBlitter;
105 };
107 class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
108 public:
109 virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) SK_OVERRIDE {
110 fy += SK_Fixed1/2;
112 int y = fy >> 16;
113 uint8_t a = (uint8_t)(fy >> 8);
115 // lower line
116 unsigned ma = SmallDot6Scale(a, mod64);
117 if (ma) {
118 call_hline_blitter(this->getBlitter(), x, y, 1, ma);
119 }
121 // upper line
122 ma = SmallDot6Scale(255 - a, mod64);
123 if (ma) {
124 call_hline_blitter(this->getBlitter(), x, y - 1, 1, ma);
125 }
127 return fy - SK_Fixed1/2;
128 }
130 virtual SkFixed drawLine(int x, int stopx, SkFixed fy,
131 SkFixed slope) SK_OVERRIDE {
132 SkASSERT(x < stopx);
133 int count = stopx - x;
134 fy += SK_Fixed1/2;
136 int y = fy >> 16;
137 uint8_t a = (uint8_t)(fy >> 8);
139 // lower line
140 if (a) {
141 call_hline_blitter(this->getBlitter(), x, y, count, a);
142 }
144 // upper line
145 a = 255 - a;
146 if (a) {
147 call_hline_blitter(this->getBlitter(), x, y - 1, count, a);
148 }
150 return fy - SK_Fixed1/2;
151 }
152 };
154 class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
155 public:
156 virtual SkFixed drawCap(int x, SkFixed fy, SkFixed dy, int mod64) SK_OVERRIDE {
157 int16_t runs[2];
158 uint8_t aa[1];
160 runs[0] = 1;
161 runs[1] = 0;
163 fy += SK_Fixed1/2;
164 SkBlitter* blitter = this->getBlitter();
166 int lower_y = fy >> 16;
167 uint8_t a = (uint8_t)(fy >> 8);
168 unsigned ma = SmallDot6Scale(a, mod64);
169 if (ma) {
170 aa[0] = ApplyGamma(gamma, ma);
171 blitter->blitAntiH(x, lower_y, aa, runs);
172 // the clipping blitters might edit runs, but should not affect us
173 SkASSERT(runs[0] == 1);
174 SkASSERT(runs[1] == 0);
175 }
176 ma = SmallDot6Scale(255 - a, mod64);
177 if (ma) {
178 aa[0] = ApplyGamma(gamma, ma);
179 blitter->blitAntiH(x, lower_y - 1, aa, runs);
180 // the clipping blitters might edit runs, but should not affect us
181 SkASSERT(runs[0] == 1);
182 SkASSERT(runs[1] == 0);
183 }
184 fy += dy;
186 return fy - SK_Fixed1/2;
187 }
189 virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) SK_OVERRIDE {
190 SkASSERT(x < stopx);
192 int16_t runs[2];
193 uint8_t aa[1];
195 runs[0] = 1;
196 runs[1] = 0;
198 fy += SK_Fixed1/2;
199 SkBlitter* blitter = this->getBlitter();
200 do {
201 int lower_y = fy >> 16;
202 uint8_t a = (uint8_t)(fy >> 8);
203 if (a) {
204 aa[0] = a;
205 blitter->blitAntiH(x, lower_y, aa, runs);
206 // the clipping blitters might edit runs, but should not affect us
207 SkASSERT(runs[0] == 1);
208 SkASSERT(runs[1] == 0);
209 }
210 a = 255 - a;
211 if (a) {
212 aa[0] = a;
213 blitter->blitAntiH(x, lower_y - 1, aa, runs);
214 // the clipping blitters might edit runs, but should not affect us
215 SkASSERT(runs[0] == 1);
216 SkASSERT(runs[1] == 0);
217 }
218 fy += dy;
219 } while (++x < stopx);
221 return fy - SK_Fixed1/2;
222 }
223 };
225 class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
226 public:
227 virtual SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) SK_OVERRIDE {
228 SkASSERT(0 == dx);
229 fx += SK_Fixed1/2;
231 int x = fx >> 16;
232 int a = (uint8_t)(fx >> 8);
234 unsigned ma = SmallDot6Scale(a, mod64);
235 if (ma) {
236 this->getBlitter()->blitV(x, y, 1, ma);
237 }
238 ma = SmallDot6Scale(255 - a, mod64);
239 if (ma) {
240 this->getBlitter()->blitV(x - 1, y, 1, ma);
241 }
243 return fx - SK_Fixed1/2;
244 }
246 virtual SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) SK_OVERRIDE {
247 SkASSERT(y < stopy);
248 SkASSERT(0 == dx);
249 fx += SK_Fixed1/2;
251 int x = fx >> 16;
252 int a = (uint8_t)(fx >> 8);
254 if (a) {
255 this->getBlitter()->blitV(x, y, stopy - y, a);
256 }
257 a = 255 - a;
258 if (a) {
259 this->getBlitter()->blitV(x - 1, y, stopy - y, a);
260 }
262 return fx - SK_Fixed1/2;
263 }
264 };
266 class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
267 public:
268 virtual SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) SK_OVERRIDE {
269 int16_t runs[3];
270 uint8_t aa[2];
272 runs[0] = 1;
273 runs[2] = 0;
275 fx += SK_Fixed1/2;
276 int x = fx >> 16;
277 uint8_t a = (uint8_t)(fx >> 8);
279 aa[0] = SmallDot6Scale(255 - a, mod64);
280 aa[1] = SmallDot6Scale(a, mod64);
281 // the clippng blitters might overwrite this guy, so we have to reset it each time
282 runs[1] = 1;
283 this->getBlitter()->blitAntiH(x - 1, y, aa, runs);
284 // the clipping blitters might edit runs, but should not affect us
285 SkASSERT(runs[0] == 1);
286 SkASSERT(runs[2] == 0);
287 fx += dx;
289 return fx - SK_Fixed1/2;
290 }
292 virtual SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) SK_OVERRIDE {
293 SkASSERT(y < stopy);
294 int16_t runs[3];
295 uint8_t aa[2];
297 runs[0] = 1;
298 runs[2] = 0;
300 fx += SK_Fixed1/2;
301 do {
302 int x = fx >> 16;
303 uint8_t a = (uint8_t)(fx >> 8);
305 aa[0] = 255 - a;
306 aa[1] = a;
307 // the clippng blitters might overwrite this guy, so we have to reset it each time
308 runs[1] = 1;
309 this->getBlitter()->blitAntiH(x - 1, y, aa, runs);
310 // the clipping blitters might edit runs, but should not affect us
311 SkASSERT(runs[0] == 1);
312 SkASSERT(runs[2] == 0);
313 fx += dx;
314 } while (++y < stopy);
316 return fx - SK_Fixed1/2;
317 }
318 };
320 static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
321 SkASSERT((a << 16 >> 16) == a);
322 SkASSERT(b != 0);
323 return (a << 16) / b;
324 }
326 #define SkBITCOUNT(x) (sizeof(x) << 3)
328 #if 1
329 // returns high-bit set iff x==0x8000...
330 static inline int bad_int(int x) {
331 return x & -x;
332 }
334 static int any_bad_ints(int a, int b, int c, int d) {
335 return (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> (SkBITCOUNT(int) - 1);
336 }
337 #else
338 static inline int good_int(int x) {
339 return x ^ (1 << (SkBITCOUNT(x) - 1));
340 }
342 static int any_bad_ints(int a, int b, int c, int d) {
343 return !(good_int(a) & good_int(b) & good_int(c) & good_int(d));
344 }
345 #endif
347 #ifdef SK_DEBUG
348 static bool canConvertFDot6ToFixed(SkFDot6 x) {
349 const int maxDot6 = SK_MaxS32 >> (16 - 6);
350 return SkAbs32(x) <= maxDot6;
351 }
352 #endif
354 /*
355 * We want the fractional part of ordinate, but we want multiples of 64 to
356 * return 64, not 0, so we can't just say (ordinate & 63).
357 * We basically want to compute those bits, and if they're 0, return 64.
358 * We can do that w/o a branch with an extra sub and add.
359 */
360 static int contribution_64(SkFDot6 ordinate) {
361 #if 0
362 int result = ordinate & 63;
363 if (0 == result) {
364 result = 64;
365 }
366 #else
367 int result = ((ordinate - 1) & 63) + 1;
368 #endif
369 SkASSERT(result > 0 && result <= 64);
370 return result;
371 }
373 static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
374 const SkIRect* clip, SkBlitter* blitter) {
375 // check for integer NaN (0x80000000) which we can't handle (can't negate it)
376 // It appears typically from a huge float (inf or nan) being converted to int.
377 // If we see it, just don't draw.
378 if (any_bad_ints(x0, y0, x1, y1)) {
379 return;
380 }
382 // The caller must clip the line to [-32767.0 ... 32767.0] ahead of time
383 // (in dot6 format)
384 SkASSERT(canConvertFDot6ToFixed(x0));
385 SkASSERT(canConvertFDot6ToFixed(y0));
386 SkASSERT(canConvertFDot6ToFixed(x1));
387 SkASSERT(canConvertFDot6ToFixed(y1));
389 if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) {
390 /* instead of (x0 + x1) >> 1, we shift each separately. This is less
391 precise, but avoids overflowing the intermediate result if the
392 values are huge. A better fix might be to clip the original pts
393 directly (i.e. do the divide), so we don't spend time subdividing
394 huge lines at all.
395 */
396 int hx = (x0 >> 1) + (x1 >> 1);
397 int hy = (y0 >> 1) + (y1 >> 1);
398 do_anti_hairline(x0, y0, hx, hy, clip, blitter);
399 do_anti_hairline(hx, hy, x1, y1, clip, blitter);
400 return;
401 }
403 int scaleStart, scaleStop;
404 int istart, istop;
405 SkFixed fstart, slope;
407 HLine_SkAntiHairBlitter hline_blitter;
408 Horish_SkAntiHairBlitter horish_blitter;
409 VLine_SkAntiHairBlitter vline_blitter;
410 Vertish_SkAntiHairBlitter vertish_blitter;
411 SkAntiHairBlitter* hairBlitter = NULL;
413 if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) { // mostly horizontal
414 if (x0 > x1) { // we want to go left-to-right
415 SkTSwap<SkFDot6>(x0, x1);
416 SkTSwap<SkFDot6>(y0, y1);
417 }
419 istart = SkFDot6Floor(x0);
420 istop = SkFDot6Ceil(x1);
421 fstart = SkFDot6ToFixed(y0);
422 if (y0 == y1) { // completely horizontal, take fast case
423 slope = 0;
424 hairBlitter = &hline_blitter;
425 } else {
426 slope = fastfixdiv(y1 - y0, x1 - x0);
427 SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
428 fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
429 hairBlitter = &horish_blitter;
430 }
432 SkASSERT(istop > istart);
433 if (istop - istart == 1) {
434 // we are within a single pixel
435 scaleStart = x1 - x0;
436 SkASSERT(scaleStart >= 0 && scaleStart <= 64);
437 scaleStop = 0;
438 } else {
439 scaleStart = 64 - (x0 & 63);
440 scaleStop = x1 & 63;
441 }
443 if (clip){
444 if (istart >= clip->fRight || istop <= clip->fLeft) {
445 return;
446 }
447 if (istart < clip->fLeft) {
448 fstart += slope * (clip->fLeft - istart);
449 istart = clip->fLeft;
450 scaleStart = 64;
451 if (istop - istart == 1) {
452 // we are within a single pixel
453 scaleStart = contribution_64(x1);
454 scaleStop = 0;
455 }
456 }
457 if (istop > clip->fRight) {
458 istop = clip->fRight;
459 scaleStop = 0; // so we don't draw this last column
460 }
462 SkASSERT(istart <= istop);
463 if (istart == istop) {
464 return;
465 }
466 // now test if our Y values are completely inside the clip
467 int top, bottom;
468 if (slope >= 0) { // T2B
469 top = SkFixedFloorToInt(fstart - SK_FixedHalf);
470 bottom = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
471 } else { // B2T
472 bottom = SkFixedCeilToInt(fstart + SK_FixedHalf);
473 top = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
474 }
475 #ifdef OUTSET_BEFORE_CLIP_TEST
476 top -= 1;
477 bottom += 1;
478 #endif
479 if (top >= clip->fBottom || bottom <= clip->fTop) {
480 return;
481 }
482 if (clip->fTop <= top && clip->fBottom >= bottom) {
483 clip = NULL;
484 }
485 }
486 } else { // mostly vertical
487 if (y0 > y1) { // we want to go top-to-bottom
488 SkTSwap<SkFDot6>(x0, x1);
489 SkTSwap<SkFDot6>(y0, y1);
490 }
492 istart = SkFDot6Floor(y0);
493 istop = SkFDot6Ceil(y1);
494 fstart = SkFDot6ToFixed(x0);
495 if (x0 == x1) {
496 if (y0 == y1) { // are we zero length?
497 return; // nothing to do
498 }
499 slope = 0;
500 hairBlitter = &vline_blitter;
501 } else {
502 slope = fastfixdiv(x1 - x0, y1 - y0);
503 SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
504 fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
505 hairBlitter = &vertish_blitter;
506 }
508 SkASSERT(istop > istart);
509 if (istop - istart == 1) {
510 // we are within a single pixel
511 scaleStart = y1 - y0;
512 SkASSERT(scaleStart >= 0 && scaleStart <= 64);
513 scaleStop = 0;
514 } else {
515 scaleStart = 64 - (y0 & 63);
516 scaleStop = y1 & 63;
517 }
519 if (clip) {
520 if (istart >= clip->fBottom || istop <= clip->fTop) {
521 return;
522 }
523 if (istart < clip->fTop) {
524 fstart += slope * (clip->fTop - istart);
525 istart = clip->fTop;
526 scaleStart = 64;
527 if (istop - istart == 1) {
528 // we are within a single pixel
529 scaleStart = contribution_64(y1);
530 scaleStop = 0;
531 }
532 }
533 if (istop > clip->fBottom) {
534 istop = clip->fBottom;
535 scaleStop = 0; // so we don't draw this last row
536 }
538 SkASSERT(istart <= istop);
539 if (istart == istop)
540 return;
542 // now test if our X values are completely inside the clip
543 int left, right;
544 if (slope >= 0) { // L2R
545 left = SkFixedFloorToInt(fstart - SK_FixedHalf);
546 right = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
547 } else { // R2L
548 right = SkFixedCeilToInt(fstart + SK_FixedHalf);
549 left = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
550 }
551 #ifdef OUTSET_BEFORE_CLIP_TEST
552 left -= 1;
553 right += 1;
554 #endif
555 if (left >= clip->fRight || right <= clip->fLeft) {
556 return;
557 }
558 if (clip->fLeft <= left && clip->fRight >= right) {
559 clip = NULL;
560 }
561 }
562 }
564 SkRectClipBlitter rectClipper;
565 if (clip) {
566 rectClipper.init(blitter, *clip);
567 blitter = &rectClipper;
568 }
570 SkASSERT(hairBlitter);
571 hairBlitter->setup(blitter);
573 #ifdef SK_DEBUG
574 if (scaleStart > 0 && scaleStop > 0) {
575 // be sure we don't draw twice in the same pixel
576 SkASSERT(istart < istop - 1);
577 }
578 #endif
580 fstart = hairBlitter->drawCap(istart, fstart, slope, scaleStart);
581 istart += 1;
582 int fullSpans = istop - istart - (scaleStop > 0);
583 if (fullSpans > 0) {
584 fstart = hairBlitter->drawLine(istart, istart + fullSpans, fstart, slope);
585 }
586 if (scaleStop > 0) {
587 hairBlitter->drawCap(istop - 1, fstart, slope, scaleStop);
588 }
589 }
591 void SkScan::AntiHairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
592 const SkRegion* clip, SkBlitter* blitter) {
593 if (clip && clip->isEmpty()) {
594 return;
595 }
597 SkASSERT(clip == NULL || !clip->getBounds().isEmpty());
599 #ifdef TEST_GAMMA
600 build_gamma_table();
601 #endif
603 SkPoint pts[2] = { pt0, pt1 };
605 // We have to pre-clip the line to fit in a SkFixed, so we just chop
606 // the line. TODO find a way to actually draw beyond that range.
607 {
608 SkRect fixedBounds;
609 const SkScalar max = SkIntToScalar(32767);
610 fixedBounds.set(-max, -max, max, max);
611 if (!SkLineClipper::IntersectLine(pts, fixedBounds, pts)) {
612 return;
613 }
614 }
616 if (clip) {
617 SkRect clipBounds;
618 clipBounds.set(clip->getBounds());
619 /* We perform integral clipping later on, but we do a scalar clip first
620 to ensure that our coordinates are expressible in fixed/integers.
622 antialiased hairlines can draw up to 1/2 of a pixel outside of
623 their bounds, so we need to outset the clip before calling the
624 clipper. To make the numerics safer, we outset by a whole pixel,
625 since the 1/2 pixel boundary is important to the antihair blitter,
626 we don't want to risk numerical fate by chopping on that edge.
627 */
628 clipBounds.inset(-SK_Scalar1, -SK_Scalar1);
630 if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
631 return;
632 }
633 }
635 SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
636 SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
637 SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
638 SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
640 if (clip) {
641 SkFDot6 left = SkMin32(x0, x1);
642 SkFDot6 top = SkMin32(y0, y1);
643 SkFDot6 right = SkMax32(x0, x1);
644 SkFDot6 bottom = SkMax32(y0, y1);
645 SkIRect ir;
647 ir.set( SkFDot6Floor(left) - 1,
648 SkFDot6Floor(top) - 1,
649 SkFDot6Ceil(right) + 1,
650 SkFDot6Ceil(bottom) + 1);
652 if (clip->quickReject(ir)) {
653 return;
654 }
655 if (!clip->quickContains(ir)) {
656 SkRegion::Cliperator iter(*clip, ir);
657 const SkIRect* r = &iter.rect();
659 while (!iter.done()) {
660 do_anti_hairline(x0, y0, x1, y1, r, blitter);
661 iter.next();
662 }
663 return;
664 }
665 // fall through to no-clip case
666 }
667 do_anti_hairline(x0, y0, x1, y1, NULL, blitter);
668 }
670 void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
671 SkBlitter* blitter) {
672 SkPoint p0, p1;
674 p0.set(rect.fLeft, rect.fTop);
675 p1.set(rect.fRight, rect.fTop);
676 SkScan::AntiHairLine(p0, p1, clip, blitter);
677 p0.set(rect.fRight, rect.fBottom);
678 SkScan::AntiHairLine(p0, p1, clip, blitter);
679 p1.set(rect.fLeft, rect.fBottom);
680 SkScan::AntiHairLine(p0, p1, clip, blitter);
681 p0.set(rect.fLeft, rect.fTop);
682 SkScan::AntiHairLine(p0, p1, clip, blitter);
683 }
685 ///////////////////////////////////////////////////////////////////////////////
687 typedef int FDot8; // 24.8 integer fixed point
689 static inline FDot8 SkFixedToFDot8(SkFixed x) {
690 return (x + 0x80) >> 8;
691 }
693 static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
694 SkBlitter* blitter) {
695 SkASSERT(L < R);
697 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
698 blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
699 return;
700 }
702 int left = L >> 8;
704 if (L & 0xFF) {
705 blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
706 left += 1;
707 }
709 int rite = R >> 8;
710 int width = rite - left;
711 if (width > 0) {
712 call_hline_blitter(blitter, left, top, width, alpha);
713 }
714 if (R & 0xFF) {
715 blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
716 }
717 }
719 static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
720 bool fillInner) {
721 // check for empty now that we're in our reduced precision space
722 if (L >= R || T >= B) {
723 return;
724 }
725 int top = T >> 8;
726 if (top == ((B - 1) >> 8)) { // just one scanline high
727 do_scanline(L, top, R, B - T - 1, blitter);
728 return;
729 }
731 if (T & 0xFF) {
732 do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
733 top += 1;
734 }
736 int bot = B >> 8;
737 int height = bot - top;
738 if (height > 0) {
739 int left = L >> 8;
740 if (left == ((R - 1) >> 8)) { // just 1-pixel wide
741 blitter->blitV(left, top, height, R - L - 1);
742 } else {
743 if (L & 0xFF) {
744 blitter->blitV(left, top, height, 256 - (L & 0xFF));
745 left += 1;
746 }
747 int rite = R >> 8;
748 int width = rite - left;
749 if (width > 0 && fillInner) {
750 blitter->blitRect(left, top, width, height);
751 }
752 if (R & 0xFF) {
753 blitter->blitV(rite, top, height, R & 0xFF);
754 }
755 }
756 }
758 if (B & 0xFF) {
759 do_scanline(L, bot, R, B & 0xFF, blitter);
760 }
761 }
763 static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
764 antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
765 SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
766 blitter, true);
767 }
769 ///////////////////////////////////////////////////////////////////////////////
771 void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
772 SkBlitter* blitter) {
773 if (NULL == clip) {
774 antifillrect(xr, blitter);
775 } else {
776 SkIRect outerBounds;
777 XRect_roundOut(xr, &outerBounds);
779 if (clip->isRect()) {
780 const SkIRect& clipBounds = clip->getBounds();
782 if (clipBounds.contains(outerBounds)) {
783 antifillrect(xr, blitter);
784 } else {
785 SkXRect tmpR;
786 // this keeps our original edges fractional
787 XRect_set(&tmpR, clipBounds);
788 if (tmpR.intersect(xr)) {
789 antifillrect(tmpR, blitter);
790 }
791 }
792 } else {
793 SkRegion::Cliperator clipper(*clip, outerBounds);
794 const SkIRect& rr = clipper.rect();
796 while (!clipper.done()) {
797 SkXRect tmpR;
799 // this keeps our original edges fractional
800 XRect_set(&tmpR, rr);
801 if (tmpR.intersect(xr)) {
802 antifillrect(tmpR, blitter);
803 }
804 clipper.next();
805 }
806 }
807 }
808 }
810 void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
811 SkBlitter* blitter) {
812 if (clip.isBW()) {
813 AntiFillXRect(xr, &clip.bwRgn(), blitter);
814 } else {
815 SkIRect outerBounds;
816 XRect_roundOut(xr, &outerBounds);
818 if (clip.quickContains(outerBounds)) {
819 AntiFillXRect(xr, NULL, blitter);
820 } else {
821 SkAAClipBlitterWrapper wrapper(clip, blitter);
822 blitter = wrapper.getBlitter();
824 AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
825 }
826 }
827 }
829 /* This guy takes a float-rect, but with the key improvement that it has
830 already been clipped, so we know that it is safe to convert it into a
831 XRect (fixedpoint), as it won't overflow.
832 */
833 static void antifillrect(const SkRect& r, SkBlitter* blitter) {
834 SkXRect xr;
836 XRect_set(&xr, r);
837 antifillrect(xr, blitter);
838 }
840 /* We repeat the clipping logic of AntiFillXRect because the float rect might
841 overflow if we blindly converted it to an XRect. This sucks that we have to
842 repeat the clipping logic, but I don't see how to share the code/logic.
844 We clip r (as needed) into one or more (smaller) float rects, and then pass
845 those to our version of antifillrect, which converts it into an XRect and
846 then calls the blit.
847 */
848 void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
849 SkBlitter* blitter) {
850 if (clip) {
851 SkRect newR;
852 newR.set(clip->getBounds());
853 if (!newR.intersect(origR)) {
854 return;
855 }
857 SkIRect outerBounds;
858 newR.roundOut(&outerBounds);
860 if (clip->isRect()) {
861 antifillrect(newR, blitter);
862 } else {
863 SkRegion::Cliperator clipper(*clip, outerBounds);
864 while (!clipper.done()) {
865 newR.set(clipper.rect());
866 if (newR.intersect(origR)) {
867 antifillrect(newR, blitter);
868 }
869 clipper.next();
870 }
871 }
872 } else {
873 antifillrect(origR, blitter);
874 }
875 }
877 void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
878 SkBlitter* blitter) {
879 if (clip.isBW()) {
880 AntiFillRect(r, &clip.bwRgn(), blitter);
881 } else {
882 SkAAClipBlitterWrapper wrap(clip, blitter);
883 AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
884 }
885 }
887 ///////////////////////////////////////////////////////////////////////////////
889 #define SkAlphaMulRound(a, b) SkMulDiv255Round(a, b)
891 // calls blitRect() if the rectangle is non-empty
892 static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
893 if (L < R && T < B) {
894 blitter->blitRect(L, T, R - L, B - T);
895 }
896 }
898 static inline FDot8 SkScalarToFDot8(SkScalar x) {
899 return (int)(x * 256);
900 }
902 static inline int FDot8Floor(FDot8 x) {
903 return x >> 8;
904 }
906 static inline int FDot8Ceil(FDot8 x) {
907 return (x + 0xFF) >> 8;
908 }
910 // 1 - (1 - a)*(1 - b)
911 static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
912 // need precise rounding (not just SkAlphaMul) so that values like
913 // a=228, b=252 don't overflow the result
914 return SkToU8(a + b - SkAlphaMulRound(a, b));
915 }
917 static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
918 SkBlitter* blitter) {
919 SkASSERT(L < R);
921 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
922 blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L));
923 return;
924 }
926 int left = L >> 8;
927 if (L & 0xFF) {
928 blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
929 left += 1;
930 }
932 int rite = R >> 8;
933 int width = rite - left;
934 if (width > 0) {
935 call_hline_blitter(blitter, left, top, width, alpha);
936 }
938 if (R & 0xFF) {
939 blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
940 }
941 }
943 static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
944 SkBlitter* blitter) {
945 SkASSERT(L < R && T < B);
947 int top = T >> 8;
948 if (top == ((B - 1) >> 8)) { // just one scanline high
949 // We want the inverse of B-T, since we're the inner-stroke
950 int alpha = 256 - (B - T);
951 if (alpha) {
952 inner_scanline(L, top, R, alpha, blitter);
953 }
954 return;
955 }
957 if (T & 0xFF) {
958 inner_scanline(L, top, R, T & 0xFF, blitter);
959 top += 1;
960 }
962 int bot = B >> 8;
963 int height = bot - top;
964 if (height > 0) {
965 if (L & 0xFF) {
966 blitter->blitV(L >> 8, top, height, L & 0xFF);
967 }
968 if (R & 0xFF) {
969 blitter->blitV(R >> 8, top, height, ~R & 0xFF);
970 }
971 }
973 if (B & 0xFF) {
974 inner_scanline(L, bot, R, ~B & 0xFF, blitter);
975 }
976 }
978 void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
979 const SkRegion* clip, SkBlitter* blitter) {
980 SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
982 SkScalar rx = SkScalarHalf(strokeSize.fX);
983 SkScalar ry = SkScalarHalf(strokeSize.fY);
985 // outset by the radius
986 FDot8 L = SkScalarToFDot8(r.fLeft - rx);
987 FDot8 T = SkScalarToFDot8(r.fTop - ry);
988 FDot8 R = SkScalarToFDot8(r.fRight + rx);
989 FDot8 B = SkScalarToFDot8(r.fBottom + ry);
991 SkIRect outer;
992 // set outer to the outer rect of the outer section
993 outer.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
995 SkBlitterClipper clipper;
996 if (clip) {
997 if (clip->quickReject(outer)) {
998 return;
999 }
1000 if (!clip->contains(outer)) {
1001 blitter = clipper.apply(blitter, clip, &outer);
1002 }
1003 // now we can ignore clip for the rest of the function
1004 }
1006 // stroke the outer hull
1007 antifilldot8(L, T, R, B, blitter, false);
1009 // set outer to the outer rect of the middle section
1010 outer.set(FDot8Ceil(L), FDot8Ceil(T), FDot8Floor(R), FDot8Floor(B));
1012 // in case we lost a bit with diameter/2
1013 rx = strokeSize.fX - rx;
1014 ry = strokeSize.fY - ry;
1015 // inset by the radius
1016 L = SkScalarToFDot8(r.fLeft + rx);
1017 T = SkScalarToFDot8(r.fTop + ry);
1018 R = SkScalarToFDot8(r.fRight - rx);
1019 B = SkScalarToFDot8(r.fBottom - ry);
1021 if (L >= R || T >= B) {
1022 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
1023 blitter);
1024 } else {
1025 SkIRect inner;
1026 // set inner to the inner rect of the middle section
1027 inner.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
1029 // draw the frame in 4 pieces
1030 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
1031 blitter);
1032 fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
1033 blitter);
1034 fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
1035 blitter);
1036 fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
1037 blitter);
1039 // now stroke the inner rect, which is similar to antifilldot8() except that
1040 // it treats the fractional coordinates with the inverse bias (since its
1041 // inner).
1042 innerstrokedot8(L, T, R, B, blitter);
1043 }
1044 }
1046 void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
1047 const SkRasterClip& clip, SkBlitter* blitter) {
1048 if (clip.isBW()) {
1049 AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
1050 } else {
1051 SkAAClipBlitterWrapper wrap(clip, blitter);
1052 AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
1053 }
1054 }