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 :
34 0 : static inline int SmallDot6Scale(int value, int dot6) {
35 0 : SkASSERT((int16_t)value == value);
36 0 : SkASSERT((unsigned)dot6 <= 64);
37 0 : 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 :
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 :
68 0 : static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
69 : U8CPU alpha) {
70 0 : SkASSERT(count > 0);
71 :
72 : int16_t runs[HLINE_STACK_BUFFER + 1];
73 : uint8_t aa[HLINE_STACK_BUFFER];
74 :
75 0 : aa[0] = ApplyGamma(gGammaTable, alpha);
76 0 : do {
77 0 : int n = count;
78 0 : if (n > HLINE_STACK_BUFFER) {
79 0 : n = HLINE_STACK_BUFFER;
80 : }
81 0 : runs[0] = SkToS16(n);
82 0 : runs[n] = 0;
83 0 : blitter->blitAntiH(x, y, aa, runs);
84 0 : x += n;
85 0 : count -= n;
86 : } while (count > 0);
87 0 : }
88 :
89 0 : static SkFixed hline(int x, int stopx, SkFixed fy, SkFixed /*slope*/,
90 : SkBlitter* blitter, int mod64) {
91 0 : SkASSERT(x < stopx);
92 0 : int count = stopx - x;
93 0 : fy += SK_Fixed1/2;
94 :
95 0 : int y = fy >> 16;
96 0 : uint8_t a = (uint8_t)(fy >> 8);
97 :
98 : // lower line
99 0 : unsigned ma = SmallDot6Scale(a, mod64);
100 0 : if (ma) {
101 0 : call_hline_blitter(blitter, x, y, count, ma);
102 : }
103 :
104 : // upper line
105 0 : ma = SmallDot6Scale(255 - a, mod64);
106 0 : if (ma) {
107 0 : call_hline_blitter(blitter, x, y - 1, count, ma);
108 : }
109 :
110 0 : return fy - SK_Fixed1/2;
111 : }
112 :
113 0 : static SkFixed horish(int x, int stopx, SkFixed fy, SkFixed dy,
114 : SkBlitter* blitter, int mod64) {
115 0 : 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 0 : runs[0] = 1;
124 0 : runs[1] = 0;
125 :
126 0 : fy += SK_Fixed1/2;
127 0 : do {
128 0 : int lower_y = fy >> 16;
129 0 : uint8_t a = (uint8_t)(fy >> 8);
130 0 : unsigned ma = SmallDot6Scale(a, mod64);
131 0 : if (ma) {
132 0 : aa[0] = ApplyGamma(gamma, ma);
133 0 : blitter->blitAntiH(x, lower_y, aa, runs);
134 : // the clipping blitters might edit runs, but should not affect us
135 0 : SkASSERT(runs[0] == 1);
136 0 : SkASSERT(runs[1] == 0);
137 : }
138 0 : ma = SmallDot6Scale(255 - a, mod64);
139 0 : if (ma) {
140 0 : aa[0] = ApplyGamma(gamma, ma);
141 0 : blitter->blitAntiH(x, lower_y - 1, aa, runs);
142 : // the clipping blitters might edit runs, but should not affect us
143 0 : SkASSERT(runs[0] == 1);
144 0 : SkASSERT(runs[1] == 0);
145 : }
146 0 : fy += dy;
147 : } while (++x < stopx);
148 :
149 0 : return fy - SK_Fixed1/2;
150 : }
151 :
152 0 : static SkFixed vline(int y, int stopy, SkFixed fx, SkFixed /*slope*/,
153 : SkBlitter* blitter, int mod64) {
154 0 : SkASSERT(y < stopy);
155 0 : fx += SK_Fixed1/2;
156 :
157 0 : int x = fx >> 16;
158 0 : int a = (uint8_t)(fx >> 8);
159 :
160 0 : unsigned ma = SmallDot6Scale(a, mod64);
161 0 : if (ma) {
162 0 : blitter->blitV(x, y, stopy - y, ApplyGamma(gGammaTable, ma));
163 : }
164 0 : ma = SmallDot6Scale(255 - a, mod64);
165 0 : if (ma) {
166 0 : blitter->blitV(x - 1, y, stopy - y, ApplyGamma(gGammaTable, ma));
167 : }
168 :
169 0 : return fx - SK_Fixed1/2;
170 : }
171 :
172 0 : static SkFixed vertish(int y, int stopy, SkFixed fx, SkFixed dx,
173 : SkBlitter* blitter, int mod64) {
174 0 : 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 0 : runs[0] = 1;
182 0 : runs[2] = 0;
183 :
184 0 : fx += SK_Fixed1/2;
185 0 : do {
186 0 : int x = fx >> 16;
187 0 : uint8_t a = (uint8_t)(fx >> 8);
188 :
189 0 : aa[0] = ApplyGamma(gamma, SmallDot6Scale(255 - a, mod64));
190 0 : aa[1] = ApplyGamma(gamma, SmallDot6Scale(a, mod64));
191 : // the clippng blitters might overwrite this guy, so we have to reset it each time
192 0 : runs[1] = 1;
193 0 : blitter->blitAntiH(x - 1, y, aa, runs);
194 : // the clipping blitters might edit runs, but should not affect us
195 0 : SkASSERT(runs[0] == 1);
196 0 : SkASSERT(runs[2] == 0);
197 0 : fx += dx;
198 : } while (++y < stopy);
199 :
200 0 : return fx - SK_Fixed1/2;
201 : }
202 :
203 : typedef SkFixed (*LineProc)(int istart, int istop, SkFixed fstart,
204 : SkFixed slope, SkBlitter*, int);
205 :
206 0 : static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
207 0 : SkASSERT((a << 16 >> 16) == a);
208 0 : SkASSERT(b != 0);
209 0 : return (a << 16) / b;
210 : }
211 :
212 0 : 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 0 : 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 0 : int hx = (x0 >> 1) + (x1 >> 1);
225 0 : int hy = (y0 >> 1) + (y1 >> 1);
226 0 : do_anti_hairline(x0, y0, hx, hy, clip, blitter);
227 0 : do_anti_hairline(hx, hy, x1, y1, clip, blitter);
228 0 : return;
229 : }
230 :
231 : int scaleStart, scaleStop;
232 : int istart, istop;
233 : SkFixed fstart, slope;
234 : LineProc proc;
235 :
236 0 : if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) { // mostly horizontal
237 0 : if (x0 > x1) { // we want to go left-to-right
238 0 : SkTSwap<SkFDot6>(x0, x1);
239 0 : SkTSwap<SkFDot6>(y0, y1);
240 : }
241 :
242 0 : istart = SkFDot6Floor(x0);
243 0 : istop = SkFDot6Ceil(x1);
244 0 : fstart = SkFDot6ToFixed(y0);
245 0 : if (y0 == y1) { // completely horizontal, take fast case
246 0 : slope = 0;
247 0 : proc = hline;
248 : } else {
249 0 : slope = fastfixdiv(y1 - y0, x1 - x0);
250 0 : SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
251 0 : fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
252 0 : proc = horish;
253 : }
254 :
255 0 : SkASSERT(istop > istart);
256 0 : if (istop - istart == 1) {
257 0 : scaleStart = x1 - x0;
258 0 : SkASSERT(scaleStart >= 0 && scaleStart <= 64);
259 0 : scaleStop = 0;
260 : } else {
261 0 : scaleStart = 64 - (x0 & 63);
262 0 : scaleStop = x1 & 63;
263 : }
264 :
265 0 : if (clip){
266 0 : if (istart >= clip->fRight || istop <= clip->fLeft) {
267 0 : return;
268 : }
269 0 : if (istart < clip->fLeft) {
270 0 : fstart += slope * (clip->fLeft - istart);
271 0 : istart = clip->fLeft;
272 0 : scaleStart = 64;
273 : }
274 0 : if (istop > clip->fRight) {
275 0 : istop = clip->fRight;
276 0 : scaleStop = 64;
277 : }
278 0 : SkASSERT(istart <= istop);
279 0 : if (istart == istop) {
280 0 : return;
281 : }
282 : // now test if our Y values are completely inside the clip
283 : int top, bottom;
284 0 : if (slope >= 0) { // T2B
285 0 : top = SkFixedFloor(fstart - SK_FixedHalf);
286 0 : bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
287 : } else { // B2T
288 0 : bottom = SkFixedCeil(fstart + SK_FixedHalf);
289 0 : top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
290 : }
291 : #ifdef OUTSET_BEFORE_CLIP_TEST
292 0 : top -= 1;
293 0 : bottom += 1;
294 : #endif
295 0 : if (top >= clip->fBottom || bottom <= clip->fTop) {
296 0 : return;
297 : }
298 0 : if (clip->fTop <= top && clip->fBottom >= bottom) {
299 0 : clip = NULL;
300 : }
301 : }
302 : } else { // mostly vertical
303 0 : if (y0 > y1) { // we want to go top-to-bottom
304 0 : SkTSwap<SkFDot6>(x0, x1);
305 0 : SkTSwap<SkFDot6>(y0, y1);
306 : }
307 :
308 0 : istart = SkFDot6Floor(y0);
309 0 : istop = SkFDot6Ceil(y1);
310 0 : fstart = SkFDot6ToFixed(x0);
311 0 : if (x0 == x1) {
312 0 : if (y0 == y1) { // are we zero length?
313 0 : return; // nothing to do
314 : }
315 0 : slope = 0;
316 0 : proc = vline;
317 : } else {
318 0 : slope = fastfixdiv(x1 - x0, y1 - y0);
319 0 : SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
320 0 : fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
321 0 : proc = vertish;
322 : }
323 :
324 0 : SkASSERT(istop > istart);
325 0 : if (istop - istart == 1) {
326 0 : scaleStart = y1 - y0;
327 0 : SkASSERT(scaleStart >= 0 && scaleStart <= 64);
328 0 : scaleStop = 0;
329 : } else {
330 0 : scaleStart = 64 - (y0 & 63);
331 0 : scaleStop = y1 & 63;
332 : }
333 :
334 0 : if (clip) {
335 0 : if (istart >= clip->fBottom || istop <= clip->fTop) {
336 0 : return;
337 : }
338 0 : if (istart < clip->fTop) {
339 0 : fstart += slope * (clip->fTop - istart);
340 0 : istart = clip->fTop;
341 0 : scaleStart = 64;
342 : }
343 0 : if (istop > clip->fBottom) {
344 0 : istop = clip->fBottom;
345 0 : scaleStop = 64;
346 : }
347 0 : SkASSERT(istart <= istop);
348 0 : if (istart == istop)
349 0 : return;
350 :
351 : // now test if our X values are completely inside the clip
352 : int left, right;
353 0 : if (slope >= 0) { // L2R
354 0 : left = SkFixedFloor(fstart - SK_FixedHalf);
355 0 : right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
356 : } else { // R2L
357 0 : right = SkFixedCeil(fstart + SK_FixedHalf);
358 0 : left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
359 : }
360 : #ifdef OUTSET_BEFORE_CLIP_TEST
361 0 : left -= 1;
362 0 : right += 1;
363 : #endif
364 0 : if (left >= clip->fRight || right <= clip->fLeft) {
365 0 : return;
366 : }
367 0 : if (clip->fLeft <= left && clip->fRight >= right) {
368 0 : clip = NULL;
369 : }
370 : }
371 : }
372 :
373 0 : SkRectClipBlitter rectClipper;
374 0 : if (clip) {
375 0 : rectClipper.init(blitter, *clip);
376 0 : blitter = &rectClipper;
377 : }
378 :
379 0 : fstart = proc(istart, istart + 1, fstart, slope, blitter, scaleStart);
380 0 : istart += 1;
381 0 : int fullSpans = istop - istart - (scaleStop > 0);
382 0 : if (fullSpans > 0) {
383 0 : fstart = proc(istart, istart + fullSpans, fstart, slope, blitter, 64);
384 : }
385 0 : if (scaleStop > 0) {
386 0 : proc(istop - 1, istop, fstart, slope, blitter, scaleStop);
387 : }
388 : }
389 :
390 0 : void SkScan::AntiHairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
391 : const SkRegion* clip, SkBlitter* blitter) {
392 0 : if (clip && clip->isEmpty()) {
393 0 : return;
394 : }
395 :
396 0 : SkASSERT(clip == NULL || !clip->getBounds().isEmpty());
397 :
398 : #ifdef TEST_GAMMA
399 : build_gamma_table();
400 : #endif
401 :
402 0 : SkPoint pts[2] = { pt0, pt1 };
403 :
404 0 : if (clip) {
405 : SkRect clipBounds;
406 0 : 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 0 : clipBounds.inset(-SK_Scalar1, -SK_Scalar1);
417 :
418 0 : if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
419 0 : return;
420 : }
421 : }
422 :
423 0 : SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
424 0 : SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
425 0 : SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
426 0 : SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
427 :
428 0 : if (clip) {
429 0 : SkFDot6 left = SkMin32(x0, x1);
430 0 : SkFDot6 top = SkMin32(y0, y1);
431 0 : SkFDot6 right = SkMax32(x0, x1);
432 0 : SkFDot6 bottom = SkMax32(y0, y1);
433 : SkIRect ir;
434 :
435 : ir.set( SkFDot6Floor(left) - 1,
436 : SkFDot6Floor(top) - 1,
437 : SkFDot6Ceil(right) + 1,
438 0 : SkFDot6Ceil(bottom) + 1);
439 :
440 0 : if (clip->quickReject(ir)) {
441 0 : return;
442 : }
443 0 : if (!clip->quickContains(ir)) {
444 0 : SkRegion::Cliperator iter(*clip, ir);
445 0 : const SkIRect* r = &iter.rect();
446 :
447 0 : while (!iter.done()) {
448 0 : do_anti_hairline(x0, y0, x1, y1, r, blitter);
449 0 : iter.next();
450 : }
451 0 : return;
452 : }
453 : // fall through to no-clip case
454 : }
455 0 : do_anti_hairline(x0, y0, x1, y1, NULL, blitter);
456 : }
457 :
458 0 : void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
459 : SkBlitter* blitter) {
460 : SkPoint p0, p1;
461 :
462 0 : p0.set(rect.fLeft, rect.fTop);
463 0 : p1.set(rect.fRight, rect.fTop);
464 0 : SkScan::AntiHairLine(p0, p1, clip, blitter);
465 0 : p0.set(rect.fRight, rect.fBottom);
466 0 : SkScan::AntiHairLine(p0, p1, clip, blitter);
467 0 : p1.set(rect.fLeft, rect.fBottom);
468 0 : SkScan::AntiHairLine(p0, p1, clip, blitter);
469 0 : p0.set(rect.fLeft, rect.fTop);
470 0 : SkScan::AntiHairLine(p0, p1, clip, blitter);
471 0 : }
472 :
473 : ///////////////////////////////////////////////////////////////////////////////
474 :
475 : typedef int FDot8; // 24.8 integer fixed point
476 :
477 0 : static inline FDot8 SkFixedToFDot8(SkFixed x) {
478 0 : return (x + 0x80) >> 8;
479 : }
480 :
481 0 : static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
482 : SkBlitter* blitter) {
483 0 : SkASSERT(L < R);
484 :
485 0 : if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
486 0 : blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
487 0 : return;
488 : }
489 :
490 0 : int left = L >> 8;
491 :
492 0 : if (L & 0xFF) {
493 0 : blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
494 0 : left += 1;
495 : }
496 :
497 0 : int rite = R >> 8;
498 0 : int width = rite - left;
499 0 : if (width > 0) {
500 0 : call_hline_blitter(blitter, left, top, width, alpha);
501 : }
502 0 : if (R & 0xFF) {
503 0 : blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
504 : }
505 : }
506 :
507 0 : 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 0 : if (L >= R || T >= B) {
511 0 : return;
512 : }
513 0 : int top = T >> 8;
514 0 : if (top == ((B - 1) >> 8)) { // just one scanline high
515 0 : do_scanline(L, top, R, B - T - 1, blitter);
516 0 : return;
517 : }
518 :
519 0 : if (T & 0xFF) {
520 0 : do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
521 0 : top += 1;
522 : }
523 :
524 0 : int bot = B >> 8;
525 0 : int height = bot - top;
526 0 : if (height > 0) {
527 0 : int left = L >> 8;
528 0 : if (left == ((R - 1) >> 8)) { // just 1-pixel wide
529 0 : blitter->blitV(left, top, height, R - L - 1);
530 : } else {
531 0 : if (L & 0xFF) {
532 0 : blitter->blitV(left, top, height, 256 - (L & 0xFF));
533 0 : left += 1;
534 : }
535 0 : int rite = R >> 8;
536 0 : int width = rite - left;
537 0 : if (width > 0 && fillInner) {
538 0 : blitter->blitRect(left, top, width, height);
539 : }
540 0 : if (R & 0xFF) {
541 0 : blitter->blitV(rite, top, height, R & 0xFF);
542 : }
543 : }
544 : }
545 :
546 0 : if (B & 0xFF) {
547 0 : do_scanline(L, bot, R, B & 0xFF, blitter);
548 : }
549 : }
550 :
551 0 : static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
552 : antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
553 : SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
554 0 : blitter, true);
555 0 : }
556 :
557 : ///////////////////////////////////////////////////////////////////////////////
558 :
559 0 : void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
560 : SkBlitter* blitter) {
561 0 : if (NULL == clip) {
562 0 : antifillrect(xr, blitter);
563 : } else {
564 : SkIRect outerBounds;
565 0 : XRect_roundOut(xr, &outerBounds);
566 :
567 0 : if (clip->isRect()) {
568 0 : const SkIRect& clipBounds = clip->getBounds();
569 :
570 0 : if (clipBounds.contains(outerBounds)) {
571 0 : antifillrect(xr, blitter);
572 : } else {
573 : SkXRect tmpR;
574 : // this keeps our original edges fractional
575 0 : XRect_set(&tmpR, clipBounds);
576 0 : if (tmpR.intersect(xr)) {
577 0 : antifillrect(tmpR, blitter);
578 : }
579 : }
580 : } else {
581 0 : SkRegion::Cliperator clipper(*clip, outerBounds);
582 0 : const SkIRect& rr = clipper.rect();
583 :
584 0 : while (!clipper.done()) {
585 : SkXRect tmpR;
586 :
587 : // this keeps our original edges fractional
588 0 : XRect_set(&tmpR, rr);
589 0 : if (tmpR.intersect(xr)) {
590 0 : antifillrect(tmpR, blitter);
591 : }
592 0 : clipper.next();
593 : }
594 : }
595 : }
596 0 : }
597 :
598 0 : void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
599 : SkBlitter* blitter) {
600 0 : if (clip.isBW()) {
601 0 : AntiFillXRect(xr, &clip.bwRgn(), blitter);
602 : } else {
603 : SkIRect outerBounds;
604 0 : XRect_roundOut(xr, &outerBounds);
605 :
606 0 : if (clip.quickContains(outerBounds)) {
607 0 : AntiFillXRect(xr, NULL, blitter);
608 : } else {
609 0 : SkAAClipBlitterWrapper wrapper(clip, blitter);
610 0 : blitter = wrapper.getBlitter();
611 :
612 0 : AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
613 : }
614 : }
615 0 : }
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 : */
623 0 : static void antifillrect(const SkRect& r, SkBlitter* blitter) {
624 : SkXRect xr;
625 :
626 0 : XRect_set(&xr, r);
627 0 : antifillrect(xr, blitter);
628 0 : }
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 : */
638 0 : void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
639 : SkBlitter* blitter) {
640 0 : if (clip) {
641 : SkRect newR;
642 0 : newR.set(clip->getBounds());
643 0 : if (!newR.intersect(origR)) {
644 0 : return;
645 : }
646 :
647 : SkIRect outerBounds;
648 0 : newR.roundOut(&outerBounds);
649 :
650 0 : if (clip->isRect()) {
651 0 : antifillrect(newR, blitter);
652 : } else {
653 0 : SkRegion::Cliperator clipper(*clip, outerBounds);
654 0 : while (!clipper.done()) {
655 0 : newR.set(clipper.rect());
656 0 : if (newR.intersect(origR)) {
657 0 : antifillrect(newR, blitter);
658 : }
659 0 : clipper.next();
660 : }
661 : }
662 : } else {
663 0 : antifillrect(origR, blitter);
664 : }
665 : }
666 :
667 0 : void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
668 : SkBlitter* blitter) {
669 0 : if (clip.isBW()) {
670 0 : AntiFillRect(r, &clip.bwRgn(), blitter);
671 : } else {
672 0 : SkAAClipBlitterWrapper wrap(clip, blitter);
673 0 : AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
674 : }
675 0 : }
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
684 0 : static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
685 0 : if (L < R && T < B) {
686 0 : blitter->blitRect(L, T, R - L, B - T);
687 : }
688 0 : }
689 :
690 0 : static inline FDot8 SkScalarToFDot8(SkScalar x) {
691 : #ifdef SK_SCALAR_IS_FLOAT
692 0 : return (int)(x * 256);
693 : #else
694 : return x >> 8;
695 : #endif
696 : }
697 :
698 0 : static inline int FDot8Floor(FDot8 x) {
699 0 : return x >> 8;
700 : }
701 :
702 0 : static inline int FDot8Ceil(FDot8 x) {
703 0 : return (x + 0xFF) >> 8;
704 : }
705 :
706 : // 1 - (1 - a)*(1 - b)
707 0 : 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 0 : return SkToU8(a + b - SkAlphaMulRound(a, b));
711 : }
712 :
713 0 : static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
714 : SkBlitter* blitter) {
715 0 : SkASSERT(L < R);
716 :
717 0 : if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
718 0 : blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L));
719 0 : return;
720 : }
721 :
722 0 : int left = L >> 8;
723 0 : if (L & 0xFF) {
724 0 : blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
725 0 : left += 1;
726 : }
727 :
728 0 : int rite = R >> 8;
729 0 : int width = rite - left;
730 0 : if (width > 0) {
731 0 : call_hline_blitter(blitter, left, top, width, alpha);
732 : }
733 :
734 0 : if (R & 0xFF) {
735 0 : blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
736 : }
737 : }
738 :
739 0 : static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
740 : SkBlitter* blitter) {
741 0 : SkASSERT(L < R && T < B);
742 :
743 0 : int top = T >> 8;
744 0 : if (top == ((B - 1) >> 8)) { // just one scanline high
745 0 : inner_scanline(L, top, R, B - T, blitter);
746 0 : return;
747 : }
748 :
749 0 : if (T & 0xFF) {
750 0 : inner_scanline(L, top, R, T & 0xFF, blitter);
751 0 : top += 1;
752 : }
753 :
754 0 : int bot = B >> 8;
755 0 : int height = bot - top;
756 0 : if (height > 0) {
757 0 : if (L & 0xFF) {
758 0 : blitter->blitV(L >> 8, top, height, L & 0xFF);
759 : }
760 0 : if (R & 0xFF) {
761 0 : blitter->blitV(R >> 8, top, height, ~R & 0xFF);
762 : }
763 : }
764 :
765 0 : if (B & 0xFF) {
766 0 : inner_scanline(L, bot, R, ~B & 0xFF, blitter);
767 : }
768 : }
769 :
770 0 : void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
771 : const SkRegion* clip, SkBlitter* blitter) {
772 0 : SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
773 :
774 0 : SkScalar rx = SkScalarHalf(strokeSize.fX);
775 0 : SkScalar ry = SkScalarHalf(strokeSize.fY);
776 :
777 : // outset by the radius
778 0 : FDot8 L = SkScalarToFDot8(r.fLeft - rx);
779 0 : FDot8 T = SkScalarToFDot8(r.fTop - ry);
780 0 : FDot8 R = SkScalarToFDot8(r.fRight + rx);
781 0 : FDot8 B = SkScalarToFDot8(r.fBottom + ry);
782 :
783 : SkIRect outer;
784 : // set outer to the outer rect of the outer section
785 0 : outer.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
786 :
787 0 : SkBlitterClipper clipper;
788 0 : if (clip) {
789 0 : if (clip->quickReject(outer)) {
790 : return;
791 : }
792 0 : if (!clip->contains(outer)) {
793 0 : 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 0 : antifilldot8(L, T, R, B, blitter, false);
800 :
801 : // set outer to the outer rect of the middle section
802 0 : outer.set(FDot8Ceil(L), FDot8Ceil(T), FDot8Floor(R), FDot8Floor(B));
803 :
804 : // in case we lost a bit with diameter/2
805 0 : rx = strokeSize.fX - rx;
806 0 : ry = strokeSize.fY - ry;
807 : // inset by the radius
808 0 : L = SkScalarToFDot8(r.fLeft + rx);
809 0 : T = SkScalarToFDot8(r.fTop + ry);
810 0 : R = SkScalarToFDot8(r.fRight - rx);
811 0 : B = SkScalarToFDot8(r.fBottom - ry);
812 :
813 0 : if (L >= R || T >= B) {
814 : fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
815 0 : blitter);
816 : } else {
817 : SkIRect inner;
818 : // set inner to the inner rect of the middle section
819 0 : 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 0 : blitter);
824 : fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
825 0 : blitter);
826 : fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
827 0 : blitter);
828 : fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
829 0 : 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 0 : innerstrokedot8(L, T, R, B, blitter);
835 : }
836 : }
837 :
838 0 : void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
839 : const SkRasterClip& clip, SkBlitter* blitter) {
840 0 : if (clip.isBW()) {
841 0 : AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
842 : } else {
843 0 : SkAAClipBlitterWrapper wrap(clip, blitter);
844 0 : AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
845 : }
846 0 : }
847 :
|