Line data Source code
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 :
33 0 : static inline int SmallDot6Scale(int value, int dot6) {
34 0 : SkASSERT((int16_t)value == value);
35 0 : SkASSERT((unsigned)dot6 <= 64);
36 0 : 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 :
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 :
67 10 : static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
68 : U8CPU alpha) {
69 10 : SkASSERT(count > 0);
70 :
71 : int16_t runs[HLINE_STACK_BUFFER + 1];
72 : uint8_t aa[HLINE_STACK_BUFFER];
73 :
74 10 : aa[0] = ApplyGamma(gGammaTable, alpha);
75 2 : do {
76 12 : int n = count;
77 12 : if (n > HLINE_STACK_BUFFER) {
78 2 : n = HLINE_STACK_BUFFER;
79 : }
80 12 : runs[0] = SkToS16(n);
81 12 : runs[n] = 0;
82 12 : blitter->blitAntiH(x, y, aa, runs);
83 12 : x += n;
84 12 : count -= n;
85 12 : } while (count > 0);
86 10 : }
87 :
88 : class SkAntiHairBlitter {
89 : public:
90 0 : SkAntiHairBlitter() : fBlitter(nullptr) {}
91 0 : virtual ~SkAntiHairBlitter() {}
92 :
93 0 : SkBlitter* getBlitter() const { return fBlitter; }
94 :
95 0 : void setup(SkBlitter* blitter) {
96 0 : fBlitter = blitter;
97 0 : }
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 0 : class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
107 : public:
108 0 : SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) override {
109 0 : fy += SK_Fixed1/2;
110 :
111 0 : int y = fy >> 16;
112 0 : uint8_t a = (uint8_t)(fy >> 8);
113 :
114 : // lower line
115 0 : unsigned ma = SmallDot6Scale(a, mod64);
116 0 : if (ma) {
117 0 : call_hline_blitter(this->getBlitter(), x, y, 1, ma);
118 : }
119 :
120 : // upper line
121 0 : ma = SmallDot6Scale(255 - a, mod64);
122 0 : if (ma) {
123 0 : call_hline_blitter(this->getBlitter(), x, y - 1, 1, ma);
124 : }
125 :
126 0 : return fy - SK_Fixed1/2;
127 : }
128 :
129 0 : virtual SkFixed drawLine(int x, int stopx, SkFixed fy,
130 : SkFixed slope) override {
131 0 : SkASSERT(x < stopx);
132 0 : int count = stopx - x;
133 0 : fy += SK_Fixed1/2;
134 :
135 0 : int y = fy >> 16;
136 0 : uint8_t a = (uint8_t)(fy >> 8);
137 :
138 : // lower line
139 0 : if (a) {
140 0 : call_hline_blitter(this->getBlitter(), x, y, count, a);
141 : }
142 :
143 : // upper line
144 0 : a = 255 - a;
145 0 : if (a) {
146 0 : call_hline_blitter(this->getBlitter(), x, y - 1, count, a);
147 : }
148 :
149 0 : return fy - SK_Fixed1/2;
150 : }
151 : };
152 :
153 0 : class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
154 : public:
155 0 : SkFixed drawCap(int x, SkFixed fy, SkFixed dy, int mod64) override {
156 0 : fy += SK_Fixed1/2;
157 :
158 0 : int lower_y = fy >> 16;
159 0 : uint8_t a = (uint8_t)(fy >> 8);
160 0 : unsigned a0 = SmallDot6Scale(255 - a, mod64);
161 0 : unsigned a1 = SmallDot6Scale(a, mod64);
162 0 : this->getBlitter()->blitAntiV2(x, lower_y - 1, a0, a1);
163 :
164 0 : return fy + dy - SK_Fixed1/2;
165 : }
166 :
167 0 : SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) override {
168 0 : SkASSERT(x < stopx);
169 :
170 0 : fy += SK_Fixed1/2;
171 0 : SkBlitter* blitter = this->getBlitter();
172 0 : do {
173 0 : int lower_y = fy >> 16;
174 0 : uint8_t a = (uint8_t)(fy >> 8);
175 0 : blitter->blitAntiV2(x, lower_y - 1, 255 - a, a);
176 0 : fy += dy;
177 : } while (++x < stopx);
178 :
179 0 : return fy - SK_Fixed1/2;
180 : }
181 : };
182 :
183 0 : class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
184 : public:
185 0 : SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override {
186 0 : SkASSERT(0 == dx);
187 0 : fx += SK_Fixed1/2;
188 :
189 0 : int x = fx >> 16;
190 0 : int a = (uint8_t)(fx >> 8);
191 :
192 0 : unsigned ma = SmallDot6Scale(a, mod64);
193 0 : if (ma) {
194 0 : this->getBlitter()->blitV(x, y, 1, ma);
195 : }
196 0 : ma = SmallDot6Scale(255 - a, mod64);
197 0 : if (ma) {
198 0 : this->getBlitter()->blitV(x - 1, y, 1, ma);
199 : }
200 :
201 0 : return fx - SK_Fixed1/2;
202 : }
203 :
204 0 : SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
205 0 : SkASSERT(y < stopy);
206 0 : SkASSERT(0 == dx);
207 0 : fx += SK_Fixed1/2;
208 :
209 0 : int x = fx >> 16;
210 0 : int a = (uint8_t)(fx >> 8);
211 :
212 0 : if (a) {
213 0 : this->getBlitter()->blitV(x, y, stopy - y, a);
214 : }
215 0 : a = 255 - a;
216 0 : if (a) {
217 0 : this->getBlitter()->blitV(x - 1, y, stopy - y, a);
218 : }
219 :
220 0 : return fx - SK_Fixed1/2;
221 : }
222 : };
223 :
224 0 : class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
225 : public:
226 0 : SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override {
227 0 : fx += SK_Fixed1/2;
228 :
229 0 : int x = fx >> 16;
230 0 : uint8_t a = (uint8_t)(fx >> 8);
231 0 : this->getBlitter()->blitAntiH2(x - 1, y,
232 0 : SmallDot6Scale(255 - a, mod64), SmallDot6Scale(a, mod64));
233 :
234 0 : return fx + dx - SK_Fixed1/2;
235 : }
236 :
237 0 : SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
238 0 : SkASSERT(y < stopy);
239 0 : fx += SK_Fixed1/2;
240 0 : do {
241 0 : int x = fx >> 16;
242 0 : uint8_t a = (uint8_t)(fx >> 8);
243 0 : this->getBlitter()->blitAntiH2(x - 1, y, 255 - a, a);
244 0 : fx += dx;
245 : } while (++y < stopy);
246 :
247 0 : return fx - SK_Fixed1/2;
248 : }
249 : };
250 :
251 0 : static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
252 0 : SkASSERT((SkLeftShift(a, 16) >> 16) == a);
253 0 : SkASSERT(b != 0);
254 0 : 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...
261 0 : static inline int bad_int(int x) {
262 0 : return x & -x;
263 : }
264 :
265 0 : static int any_bad_ints(int a, int b, int c, int d) {
266 0 : return (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> (SkBITCOUNT(int) - 1);
267 : }
268 : #else
269 : static inline int good_int(int x) {
270 : return x ^ (1 << (SkBITCOUNT(x) - 1));
271 : }
272 :
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
279 0 : static bool canConvertFDot6ToFixed(SkFDot6 x) {
280 0 : const int maxDot6 = SK_MaxS32 >> (16 - 6);
281 0 : 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 : */
291 0 : 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 0 : int result = ((ordinate - 1) & 63) + 1;
299 : #endif
300 0 : SkASSERT(result > 0 && result <= 64);
301 0 : return result;
302 : }
303 :
304 0 : 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 0 : if (any_bad_ints(x0, y0, x1, y1)) {
310 0 : return;
311 : }
312 :
313 : // The caller must clip the line to [-32767.0 ... 32767.0] ahead of time
314 : // (in dot6 format)
315 0 : SkASSERT(canConvertFDot6ToFixed(x0));
316 0 : SkASSERT(canConvertFDot6ToFixed(y0));
317 0 : SkASSERT(canConvertFDot6ToFixed(x1));
318 0 : SkASSERT(canConvertFDot6ToFixed(y1));
319 :
320 0 : 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 0 : int hx = (x0 >> 1) + (x1 >> 1);
328 0 : int hy = (y0 >> 1) + (y1 >> 1);
329 0 : do_anti_hairline(x0, y0, hx, hy, clip, blitter);
330 0 : do_anti_hairline(hx, hy, x1, y1, clip, blitter);
331 0 : return;
332 : }
333 :
334 : int scaleStart, scaleStop;
335 : int istart, istop;
336 : SkFixed fstart, slope;
337 :
338 0 : HLine_SkAntiHairBlitter hline_blitter;
339 0 : Horish_SkAntiHairBlitter horish_blitter;
340 0 : VLine_SkAntiHairBlitter vline_blitter;
341 0 : Vertish_SkAntiHairBlitter vertish_blitter;
342 0 : SkAntiHairBlitter* hairBlitter = nullptr;
343 :
344 0 : if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) { // mostly horizontal
345 0 : if (x0 > x1) { // we want to go left-to-right
346 0 : SkTSwap<SkFDot6>(x0, x1);
347 0 : SkTSwap<SkFDot6>(y0, y1);
348 : }
349 :
350 0 : istart = SkFDot6Floor(x0);
351 0 : istop = SkFDot6Ceil(x1);
352 0 : fstart = SkFDot6ToFixed(y0);
353 0 : if (y0 == y1) { // completely horizontal, take fast case
354 0 : slope = 0;
355 0 : hairBlitter = &hline_blitter;
356 : } else {
357 0 : slope = fastfixdiv(y1 - y0, x1 - x0);
358 0 : SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
359 0 : fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
360 0 : hairBlitter = &horish_blitter;
361 : }
362 :
363 0 : SkASSERT(istop > istart);
364 0 : if (istop - istart == 1) {
365 : // we are within a single pixel
366 0 : scaleStart = x1 - x0;
367 0 : SkASSERT(scaleStart >= 0 && scaleStart <= 64);
368 0 : scaleStop = 0;
369 : } else {
370 0 : scaleStart = 64 - (x0 & 63);
371 0 : scaleStop = x1 & 63;
372 : }
373 :
374 0 : if (clip){
375 0 : if (istart >= clip->fRight || istop <= clip->fLeft) {
376 0 : return;
377 : }
378 0 : if (istart < clip->fLeft) {
379 0 : fstart += slope * (clip->fLeft - istart);
380 0 : istart = clip->fLeft;
381 0 : scaleStart = 64;
382 0 : if (istop - istart == 1) {
383 : // we are within a single pixel
384 0 : scaleStart = contribution_64(x1);
385 0 : scaleStop = 0;
386 : }
387 : }
388 0 : if (istop > clip->fRight) {
389 0 : istop = clip->fRight;
390 0 : scaleStop = 0; // so we don't draw this last column
391 : }
392 :
393 0 : SkASSERT(istart <= istop);
394 0 : if (istart == istop) {
395 0 : return;
396 : }
397 : // now test if our Y values are completely inside the clip
398 : int top, bottom;
399 0 : if (slope >= 0) { // T2B
400 0 : top = SkFixedFloorToInt(fstart - SK_FixedHalf);
401 0 : bottom = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
402 : } else { // B2T
403 0 : bottom = SkFixedCeilToInt(fstart + SK_FixedHalf);
404 0 : top = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
405 : }
406 : #ifdef OUTSET_BEFORE_CLIP_TEST
407 0 : top -= 1;
408 0 : bottom += 1;
409 : #endif
410 0 : if (top >= clip->fBottom || bottom <= clip->fTop) {
411 0 : return;
412 : }
413 0 : if (clip->fTop <= top && clip->fBottom >= bottom) {
414 0 : clip = nullptr;
415 : }
416 : }
417 : } else { // mostly vertical
418 0 : if (y0 > y1) { // we want to go top-to-bottom
419 0 : SkTSwap<SkFDot6>(x0, x1);
420 0 : SkTSwap<SkFDot6>(y0, y1);
421 : }
422 :
423 0 : istart = SkFDot6Floor(y0);
424 0 : istop = SkFDot6Ceil(y1);
425 0 : fstart = SkFDot6ToFixed(x0);
426 0 : if (x0 == x1) {
427 0 : if (y0 == y1) { // are we zero length?
428 0 : return; // nothing to do
429 : }
430 0 : slope = 0;
431 0 : hairBlitter = &vline_blitter;
432 : } else {
433 0 : slope = fastfixdiv(x1 - x0, y1 - y0);
434 0 : SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
435 0 : fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
436 0 : hairBlitter = &vertish_blitter;
437 : }
438 :
439 0 : SkASSERT(istop > istart);
440 0 : if (istop - istart == 1) {
441 : // we are within a single pixel
442 0 : scaleStart = y1 - y0;
443 0 : SkASSERT(scaleStart >= 0 && scaleStart <= 64);
444 0 : scaleStop = 0;
445 : } else {
446 0 : scaleStart = 64 - (y0 & 63);
447 0 : scaleStop = y1 & 63;
448 : }
449 :
450 0 : if (clip) {
451 0 : if (istart >= clip->fBottom || istop <= clip->fTop) {
452 0 : return;
453 : }
454 0 : if (istart < clip->fTop) {
455 0 : fstart += slope * (clip->fTop - istart);
456 0 : istart = clip->fTop;
457 0 : scaleStart = 64;
458 0 : if (istop - istart == 1) {
459 : // we are within a single pixel
460 0 : scaleStart = contribution_64(y1);
461 0 : scaleStop = 0;
462 : }
463 : }
464 0 : if (istop > clip->fBottom) {
465 0 : istop = clip->fBottom;
466 0 : scaleStop = 0; // so we don't draw this last row
467 : }
468 :
469 0 : SkASSERT(istart <= istop);
470 0 : if (istart == istop)
471 0 : return;
472 :
473 : // now test if our X values are completely inside the clip
474 : int left, right;
475 0 : if (slope >= 0) { // L2R
476 0 : left = SkFixedFloorToInt(fstart - SK_FixedHalf);
477 0 : right = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
478 : } else { // R2L
479 0 : right = SkFixedCeilToInt(fstart + SK_FixedHalf);
480 0 : left = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
481 : }
482 : #ifdef OUTSET_BEFORE_CLIP_TEST
483 0 : left -= 1;
484 0 : right += 1;
485 : #endif
486 0 : if (left >= clip->fRight || right <= clip->fLeft) {
487 0 : return;
488 : }
489 0 : if (clip->fLeft <= left && clip->fRight >= right) {
490 0 : clip = nullptr;
491 : }
492 : }
493 : }
494 :
495 0 : SkRectClipBlitter rectClipper;
496 0 : if (clip) {
497 0 : rectClipper.init(blitter, *clip);
498 0 : blitter = &rectClipper;
499 : }
500 :
501 0 : SkASSERT(hairBlitter);
502 0 : hairBlitter->setup(blitter);
503 :
504 : #ifdef SK_DEBUG
505 0 : if (scaleStart > 0 && scaleStop > 0) {
506 : // be sure we don't draw twice in the same pixel
507 0 : SkASSERT(istart < istop - 1);
508 : }
509 : #endif
510 :
511 0 : fstart = hairBlitter->drawCap(istart, fstart, slope, scaleStart);
512 0 : istart += 1;
513 0 : int fullSpans = istop - istart - (scaleStop > 0);
514 0 : if (fullSpans > 0) {
515 0 : fstart = hairBlitter->drawLine(istart, istart + fullSpans, fstart, slope);
516 : }
517 0 : if (scaleStop > 0) {
518 0 : hairBlitter->drawCap(istop - 1, fstart, slope, scaleStop);
519 : }
520 : }
521 :
522 0 : void SkScan::AntiHairLineRgn(const SkPoint array[], int arrayCount, const SkRegion* clip,
523 : SkBlitter* blitter) {
524 0 : if (clip && clip->isEmpty()) {
525 0 : return;
526 : }
527 :
528 0 : SkASSERT(clip == nullptr || !clip->getBounds().isEmpty());
529 :
530 : #ifdef TEST_GAMMA
531 : build_gamma_table();
532 : #endif
533 :
534 0 : const SkScalar max = SkIntToScalar(32767);
535 0 : const SkRect fixedBounds = SkRect::MakeLTRB(-max, -max, max, max);
536 :
537 : SkRect clipBounds;
538 0 : if (clip) {
539 0 : 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 0 : clipBounds.outset(SK_Scalar1, SK_Scalar1);
550 : }
551 :
552 0 : 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 0 : if (!SkLineClipper::IntersectLine(&array[i], fixedBounds, pts)) {
558 0 : continue;
559 : }
560 :
561 0 : if (clip && !SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
562 0 : continue;
563 : }
564 :
565 0 : SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
566 0 : SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
567 0 : SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
568 0 : SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
569 :
570 0 : if (clip) {
571 0 : SkFDot6 left = SkMin32(x0, x1);
572 0 : SkFDot6 top = SkMin32(y0, y1);
573 0 : SkFDot6 right = SkMax32(x0, x1);
574 0 : SkFDot6 bottom = SkMax32(y0, y1);
575 : SkIRect ir;
576 :
577 0 : ir.set( SkFDot6Floor(left) - 1,
578 0 : SkFDot6Floor(top) - 1,
579 0 : SkFDot6Ceil(right) + 1,
580 0 : SkFDot6Ceil(bottom) + 1);
581 :
582 0 : if (clip->quickReject(ir)) {
583 0 : continue;
584 : }
585 0 : if (!clip->quickContains(ir)) {
586 0 : SkRegion::Cliperator iter(*clip, ir);
587 0 : const SkIRect* r = &iter.rect();
588 :
589 0 : while (!iter.done()) {
590 0 : do_anti_hairline(x0, y0, x1, y1, r, blitter);
591 0 : iter.next();
592 : }
593 0 : continue;
594 : }
595 : // fall through to no-clip case
596 : }
597 0 : do_anti_hairline(x0, y0, x1, y1, nullptr, blitter);
598 : }
599 : }
600 :
601 0 : void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
602 : SkBlitter* blitter) {
603 : SkPoint pts[5];
604 :
605 0 : pts[0].set(rect.fLeft, rect.fTop);
606 0 : pts[1].set(rect.fRight, rect.fTop);
607 0 : pts[2].set(rect.fRight, rect.fBottom);
608 0 : pts[3].set(rect.fLeft, rect.fBottom);
609 0 : pts[4] = pts[0];
610 0 : SkScan::AntiHairLine(pts, 5, clip, blitter);
611 0 : }
612 :
613 : ///////////////////////////////////////////////////////////////////////////////
614 :
615 : typedef int FDot8; // 24.8 integer fixed point
616 :
617 944 : static inline FDot8 SkFixedToFDot8(SkFixed x) {
618 944 : return (x + 0x80) >> 8;
619 : }
620 :
621 10 : static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
622 : SkBlitter* blitter) {
623 10 : SkASSERT(L < R);
624 :
625 10 : if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
626 0 : blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
627 0 : return;
628 : }
629 :
630 10 : int left = L >> 8;
631 :
632 10 : if (L & 0xFF) {
633 0 : blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
634 0 : left += 1;
635 : }
636 :
637 10 : int rite = R >> 8;
638 10 : int width = rite - left;
639 10 : if (width > 0) {
640 10 : call_hline_blitter(blitter, left, top, width, alpha);
641 : }
642 10 : if (R & 0xFF) {
643 0 : blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
644 : }
645 : }
646 :
647 236 : 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 236 : if (L >= R || T >= B) {
651 0 : return;
652 : }
653 236 : int top = T >> 8;
654 236 : if (top == ((B - 1) >> 8)) { // just one scanline high
655 10 : do_scanline(L, top, R, B - T - 1, blitter);
656 10 : return;
657 : }
658 :
659 226 : if (T & 0xFF) {
660 0 : do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
661 0 : top += 1;
662 : }
663 :
664 226 : int bot = B >> 8;
665 226 : int height = bot - top;
666 226 : if (height > 0) {
667 226 : int left = L >> 8;
668 226 : if (left == ((R - 1) >> 8)) { // just 1-pixel wide
669 0 : blitter->blitV(left, top, height, R - L - 1);
670 : } else {
671 226 : if (L & 0xFF) {
672 0 : blitter->blitV(left, top, height, 256 - (L & 0xFF));
673 0 : left += 1;
674 : }
675 226 : int rite = R >> 8;
676 226 : int width = rite - left;
677 226 : if (width > 0 && fillInner) {
678 226 : blitter->blitRect(left, top, width, height);
679 : }
680 226 : if (R & 0xFF) {
681 0 : blitter->blitV(rite, top, height, R & 0xFF);
682 : }
683 : }
684 : }
685 :
686 226 : if (B & 0xFF) {
687 0 : do_scanline(L, bot, R, B & 0xFF, blitter);
688 : }
689 : }
690 :
691 236 : static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
692 472 : antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
693 472 : SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
694 236 : blitter, true);
695 236 : }
696 :
697 : ///////////////////////////////////////////////////////////////////////////////
698 :
699 0 : void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
700 : SkBlitter* blitter) {
701 0 : if (nullptr == clip) {
702 0 : antifillrect(xr, blitter);
703 : } else {
704 : SkIRect outerBounds;
705 0 : XRect_roundOut(xr, &outerBounds);
706 :
707 0 : if (clip->isRect()) {
708 0 : const SkIRect& clipBounds = clip->getBounds();
709 :
710 0 : if (clipBounds.contains(outerBounds)) {
711 0 : antifillrect(xr, blitter);
712 : } else {
713 : SkXRect tmpR;
714 : // this keeps our original edges fractional
715 0 : XRect_set(&tmpR, clipBounds);
716 0 : if (tmpR.intersect(xr)) {
717 0 : antifillrect(tmpR, blitter);
718 : }
719 : }
720 : } else {
721 0 : SkRegion::Cliperator clipper(*clip, outerBounds);
722 0 : const SkIRect& rr = clipper.rect();
723 :
724 0 : while (!clipper.done()) {
725 : SkXRect tmpR;
726 :
727 : // this keeps our original edges fractional
728 0 : XRect_set(&tmpR, rr);
729 0 : if (tmpR.intersect(xr)) {
730 0 : antifillrect(tmpR, blitter);
731 : }
732 0 : clipper.next();
733 : }
734 : }
735 : }
736 0 : }
737 :
738 0 : void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
739 : SkBlitter* blitter) {
740 0 : if (clip.isBW()) {
741 0 : AntiFillXRect(xr, &clip.bwRgn(), blitter);
742 : } else {
743 : SkIRect outerBounds;
744 0 : XRect_roundOut(xr, &outerBounds);
745 :
746 0 : if (clip.quickContains(outerBounds)) {
747 0 : AntiFillXRect(xr, nullptr, blitter);
748 : } else {
749 0 : SkAAClipBlitterWrapper wrapper(clip, blitter);
750 0 : AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
751 : }
752 : }
753 0 : }
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 : */
759 236 : static void antifillrect(const SkRect& r, SkBlitter* blitter) {
760 : SkXRect xr;
761 :
762 236 : XRect_set(&xr, r);
763 236 : antifillrect(xr, blitter);
764 236 : }
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 : */
774 205 : void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
775 : SkBlitter* blitter) {
776 205 : if (clip) {
777 : SkRect newR;
778 205 : newR.set(clip->getBounds());
779 205 : if (!newR.intersect(origR)) {
780 0 : return;
781 : }
782 :
783 205 : const SkIRect outerBounds = newR.roundOut();
784 :
785 205 : if (clip->isRect()) {
786 193 : antifillrect(newR, blitter);
787 : } else {
788 12 : SkRegion::Cliperator clipper(*clip, outerBounds);
789 98 : while (!clipper.done()) {
790 43 : newR.set(clipper.rect());
791 43 : if (newR.intersect(origR)) {
792 43 : antifillrect(newR, blitter);
793 : }
794 43 : clipper.next();
795 : }
796 : }
797 : } else {
798 0 : antifillrect(origR, blitter);
799 : }
800 : }
801 :
802 205 : void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
803 : SkBlitter* blitter) {
804 205 : if (clip.isBW()) {
805 78 : AntiFillRect(r, &clip.bwRgn(), blitter);
806 : } else {
807 254 : SkAAClipBlitterWrapper wrap(clip, blitter);
808 127 : AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
809 : }
810 205 : }
811 :
812 : ///////////////////////////////////////////////////////////////////////////////
813 :
814 : #define SkAlphaMulRound(a, b) SkMulDiv255Round(a, b)
815 :
816 : // calls blitRect() if the rectangle is non-empty
817 0 : static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
818 0 : if (L < R && T < B) {
819 0 : blitter->blitRect(L, T, R - L, B - T);
820 : }
821 0 : }
822 :
823 0 : static inline FDot8 SkScalarToFDot8(SkScalar x) {
824 0 : return (int)(x * 256);
825 : }
826 :
827 0 : static inline int FDot8Floor(FDot8 x) {
828 0 : return x >> 8;
829 : }
830 :
831 0 : static inline int FDot8Ceil(FDot8 x) {
832 0 : return (x + 0xFF) >> 8;
833 : }
834 :
835 : // 1 - (1 - a)*(1 - b)
836 0 : 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 0 : return SkToU8(a + b - SkAlphaMulRound(a, b));
840 : }
841 :
842 0 : static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
843 : SkBlitter* blitter) {
844 0 : SkASSERT(L < R);
845 :
846 0 : if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
847 0 : FDot8 widClamp = R - L;
848 : // border case clamp 256 to 255 instead of going through call_hline_blitter
849 : // see skbug/4406
850 0 : widClamp = widClamp - (widClamp >> 8);
851 0 : blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, widClamp));
852 0 : return;
853 : }
854 :
855 0 : int left = L >> 8;
856 0 : if (L & 0xFF) {
857 0 : blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
858 0 : left += 1;
859 : }
860 :
861 0 : int rite = R >> 8;
862 0 : int width = rite - left;
863 0 : if (width > 0) {
864 0 : call_hline_blitter(blitter, left, top, width, alpha);
865 : }
866 :
867 0 : if (R & 0xFF) {
868 0 : blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
869 : }
870 : }
871 :
872 0 : static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
873 : SkBlitter* blitter) {
874 0 : SkASSERT(L < R && T < B);
875 :
876 0 : int top = T >> 8;
877 0 : if (top == ((B - 1) >> 8)) { // just one scanline high
878 : // We want the inverse of B-T, since we're the inner-stroke
879 0 : int alpha = 256 - (B - T);
880 0 : if (alpha) {
881 0 : inner_scanline(L, top, R, alpha, blitter);
882 : }
883 0 : return;
884 : }
885 :
886 0 : if (T & 0xFF) {
887 0 : inner_scanline(L, top, R, T & 0xFF, blitter);
888 0 : top += 1;
889 : }
890 :
891 0 : int bot = B >> 8;
892 0 : int height = bot - top;
893 0 : if (height > 0) {
894 0 : if (L & 0xFF) {
895 0 : blitter->blitV(L >> 8, top, height, L & 0xFF);
896 : }
897 0 : if (R & 0xFF) {
898 0 : blitter->blitV(R >> 8, top, height, ~R & 0xFF);
899 : }
900 : }
901 :
902 0 : if (B & 0xFF) {
903 0 : inner_scanline(L, bot, R, ~B & 0xFF, blitter);
904 : }
905 : }
906 :
907 0 : static inline void align_thin_stroke(FDot8& edge1, FDot8& edge2) {
908 0 : SkASSERT(edge1 <= edge2);
909 :
910 0 : if (FDot8Floor(edge1) == FDot8Floor(edge2)) {
911 0 : edge2 -= (edge1 & 0xFF);
912 0 : edge1 &= ~0xFF;
913 : }
914 0 : }
915 :
916 0 : void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
917 : const SkRegion* clip, SkBlitter* blitter) {
918 0 : SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
919 :
920 0 : SkScalar rx = SkScalarHalf(strokeSize.fX);
921 0 : SkScalar ry = SkScalarHalf(strokeSize.fY);
922 :
923 : // outset by the radius
924 0 : FDot8 outerL = SkScalarToFDot8(r.fLeft - rx);
925 0 : FDot8 outerT = SkScalarToFDot8(r.fTop - ry);
926 0 : FDot8 outerR = SkScalarToFDot8(r.fRight + rx);
927 0 : FDot8 outerB = SkScalarToFDot8(r.fBottom + ry);
928 :
929 : SkIRect outer;
930 : // set outer to the outer rect of the outer section
931 0 : outer.set(FDot8Floor(outerL), FDot8Floor(outerT), FDot8Ceil(outerR), FDot8Ceil(outerB));
932 :
933 0 : SkBlitterClipper clipper;
934 0 : if (clip) {
935 0 : if (clip->quickReject(outer)) {
936 0 : return;
937 : }
938 0 : if (!clip->contains(outer)) {
939 0 : 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 0 : rx = strokeSize.fX - rx;
946 0 : ry = strokeSize.fY - ry;
947 :
948 : // inset by the radius
949 0 : FDot8 innerL = SkScalarToFDot8(r.fLeft + rx);
950 0 : FDot8 innerT = SkScalarToFDot8(r.fTop + ry);
951 0 : FDot8 innerR = SkScalarToFDot8(r.fRight - rx);
952 0 : 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 0 : if (strokeSize.fX < 1 || strokeSize.fY < 1) {
959 0 : align_thin_stroke(outerL, innerL);
960 0 : align_thin_stroke(outerT, innerT);
961 0 : align_thin_stroke(innerR, outerR);
962 0 : align_thin_stroke(innerB, outerB);
963 : }
964 :
965 : // stroke the outer hull
966 0 : antifilldot8(outerL, outerT, outerR, outerB, blitter, false);
967 :
968 : // set outer to the outer rect of the middle section
969 0 : outer.set(FDot8Ceil(outerL), FDot8Ceil(outerT), FDot8Floor(outerR), FDot8Floor(outerB));
970 :
971 0 : if (innerL >= innerR || innerT >= innerB) {
972 0 : fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
973 0 : blitter);
974 : } else {
975 : SkIRect inner;
976 : // set inner to the inner rect of the middle section
977 0 : inner.set(FDot8Floor(innerL), FDot8Floor(innerT), FDot8Ceil(innerR), FDot8Ceil(innerB));
978 :
979 : // draw the frame in 4 pieces
980 0 : fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
981 0 : blitter);
982 0 : fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
983 0 : blitter);
984 0 : fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
985 0 : blitter);
986 0 : fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
987 0 : 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 0 : innerstrokedot8(innerL, innerT, innerR, innerB, blitter);
993 : }
994 : }
995 :
996 0 : void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
997 : const SkRasterClip& clip, SkBlitter* blitter) {
998 0 : if (clip.isBW()) {
999 0 : AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
1000 : } else {
1001 0 : SkAAClipBlitterWrapper wrap(clip, blitter);
1002 0 : AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
1003 : }
1004 0 : }
|