Index: skia/sgl/SkScan_Antihair.cpp |
=================================================================== |
--- skia/sgl/SkScan_Antihair.cpp (revision 16859) |
+++ skia/sgl/SkScan_Antihair.cpp (working copy) |
@@ -1,669 +0,0 @@ |
-/* libs/graphics/sgl/SkScan_Antihair.cpp |
-** |
-** Copyright 2006, The Android Open Source Project |
-** |
-** Licensed under the Apache License, Version 2.0 (the "License"); |
-** you may not use this file except in compliance with the License. |
-** You may obtain a copy of the License at |
-** |
-** http://www.apache.org/licenses/LICENSE-2.0 |
-** |
-** Unless required by applicable law or agreed to in writing, software |
-** distributed under the License is distributed on an "AS IS" BASIS, |
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
-** See the License for the specific language governing permissions and |
-** limitations under the License. |
-*/ |
- |
-#include "SkScan.h" |
-#include "SkBlitter.h" |
-#include "SkColorPriv.h" |
-#include "SkRegion.h" |
-#include "SkFDot6.h" |
- |
-/* Our attempt to compute the worst case "bounds" for the horizontal and |
- vertical cases has some numerical bug in it, and we sometimes undervalue |
- our extends. The bug is that when this happens, we will set the clip to |
- NULL (for speed), and thus draw outside of the clip by a pixel, which might |
- only look bad, but it might also access memory outside of the valid range |
- allcoated for the device bitmap. |
- |
- This define enables our fix to outset our "bounds" by 1, thus avoiding the |
- chance of the bug, but at the cost of sometimes taking the rectblitter |
- case (i.e. not setting the clip to NULL) when we might not actually need |
- to. If we can improve/fix the actual calculations, then we can remove this |
- step. |
-*/ |
-#define OUTSET_BEFORE_CLIP_TEST true |
- |
- |
-#define HLINE_STACK_BUFFER 100 |
- |
-static inline int SmallDot6Scale(int value, int dot6) { |
- SkASSERT((int16_t)value == value); |
- SkASSERT((unsigned)dot6 <= 64); |
- return SkMulS16(value, dot6) >> 6; |
-} |
- |
-//#define TEST_GAMMA |
- |
-#ifdef TEST_GAMMA |
- static uint8_t gGammaTable[256]; |
- #define ApplyGamma(table, alpha) (table)[alpha] |
- |
- static void build_gamma_table() |
- { |
- static bool gInit = false; |
- |
- if (gInit == false) |
- { |
- for (int i = 0; i < 256; i++) |
- { |
- SkFixed n = i * 257; |
- n += n >> 15; |
- SkASSERT(n >= 0 && n <= SK_Fixed1); |
- n = SkFixedSqrt(n); |
- n = n * 255 >> 16; |
- // SkDebugf("morph %d -> %d\n", i, n); |
- gGammaTable[i] = SkToU8(n); |
- } |
- gInit = true; |
- } |
- } |
-#else |
- #define ApplyGamma(table, alpha) SkToU8(alpha) |
-#endif |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count, U8CPU alpha) |
-{ |
- SkASSERT(count > 0); |
- |
- int16_t runs[HLINE_STACK_BUFFER + 1]; |
- uint8_t aa[HLINE_STACK_BUFFER]; |
- |
- aa[0] = ApplyGamma(gGammaTable, alpha); |
- do { |
- int n = count; |
- if (n > HLINE_STACK_BUFFER) |
- n = HLINE_STACK_BUFFER; |
- |
- runs[0] = SkToS16(n); |
- runs[n] = 0; |
- blitter->blitAntiH(x, y, aa, runs); |
- x += n; |
- count -= n; |
- } while (count > 0); |
-} |
- |
-static SkFixed hline(int x, int stopx, SkFixed fy, SkFixed /*slope*/, SkBlitter* blitter, int mod64) |
-{ |
- SkASSERT(x < stopx); |
- int count = stopx - x; |
- fy += SK_Fixed1/2; |
- |
- int y = fy >> 16; |
- uint8_t a = (uint8_t)(fy >> 8); |
- |
- // lower line |
- unsigned ma = SmallDot6Scale(a, mod64); |
- if (ma) { |
- call_hline_blitter(blitter, x, y, count, ma); |
- } |
- |
- // upper line |
- ma = SmallDot6Scale(255 - a, mod64); |
- if (ma) { |
- call_hline_blitter(blitter, x, y - 1, count, ma); |
- } |
- |
- return fy - SK_Fixed1/2; |
-} |
- |
-static SkFixed horish(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitter, int mod64) |
-{ |
- SkASSERT(x < stopx); |
- |
-#ifdef TEST_GAMMA |
- const uint8_t* gamma = gGammaTable; |
-#endif |
- int16_t runs[2]; |
- uint8_t aa[1]; |
- |
- runs[0] = 1; |
- runs[1] = 0; |
- |
- fy += SK_Fixed1/2; |
- do { |
- int lower_y = fy >> 16; |
- uint8_t a = (uint8_t)(fy >> 8); |
- unsigned ma = SmallDot6Scale(a, mod64); |
- if (ma) |
- { |
- aa[0] = ApplyGamma(gamma, ma); |
- blitter->blitAntiH(x, lower_y, aa, runs); |
- // the clipping blitters might edit runs, but should not affect us |
- SkASSERT(runs[0] == 1); |
- SkASSERT(runs[1] == 0); |
- } |
- ma = SmallDot6Scale(255 - a, mod64); |
- if (ma) |
- { |
- aa[0] = ApplyGamma(gamma, ma); |
- blitter->blitAntiH(x, lower_y - 1, aa, runs); |
- // the clipping blitters might edit runs, but should not affect us |
- SkASSERT(runs[0] == 1); |
- SkASSERT(runs[1] == 0); |
- } |
- fy += dy; |
- } while (++x < stopx); |
- |
- return fy - SK_Fixed1/2; |
-} |
- |
-static SkFixed vline(int y, int stopy, SkFixed fx, SkFixed /*slope*/, SkBlitter* blitter, int mod64) |
-{ |
- SkASSERT(y < stopy); |
- fx += SK_Fixed1/2; |
- |
- int x = fx >> 16; |
- int a = (uint8_t)(fx >> 8); |
- |
- unsigned ma = SmallDot6Scale(a, mod64); |
- if (ma) |
- blitter->blitV(x, y, stopy - y, ApplyGamma(gGammaTable, ma)); |
- ma = SmallDot6Scale(255 - a, mod64); |
- if (ma) |
- blitter->blitV(x - 1, y, stopy - y, ApplyGamma(gGammaTable, ma)); |
- |
- return fx - SK_Fixed1/2; |
-} |
- |
-static SkFixed vertish(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitter, int mod64) |
-{ |
- SkASSERT(y < stopy); |
-#ifdef TEST_GAMMA |
- const uint8_t* gamma = gGammaTable; |
-#endif |
- int16_t runs[3]; |
- uint8_t aa[2]; |
- |
- runs[0] = 1; |
- runs[2] = 0; |
- |
- fx += SK_Fixed1/2; |
- do { |
- int x = fx >> 16; |
- uint8_t a = (uint8_t)(fx >> 8); |
- |
- aa[0] = ApplyGamma(gamma, SmallDot6Scale(255 - a, mod64)); |
- aa[1] = ApplyGamma(gamma, SmallDot6Scale(a, mod64)); |
- // the clippng blitters might overwrite this guy, so we have to reset it each time |
- runs[1] = 1; |
- blitter->blitAntiH(x - 1, y, aa, runs); |
- // the clipping blitters might edit runs, but should not affect us |
- SkASSERT(runs[0] == 1); |
- SkASSERT(runs[2] == 0); |
- fx += dx; |
- } while (++y < stopy); |
- |
- return fx - SK_Fixed1/2; |
-} |
- |
-typedef SkFixed (*LineProc)(int istart, int istop, SkFixed fstart, SkFixed slope, SkBlitter*, int); |
- |
-static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) |
-{ |
- SkASSERT((a << 16 >> 16) == a); |
- SkASSERT(b != 0); |
- return (a << 16) / b; |
-} |
- |
-static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1, |
- const SkIRect* clip, SkBlitter* blitter) |
-{ |
- // check that we're no larger than 511 pixels (so we can do a faster div). |
- // if we are, subdivide and call again |
- |
- if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) |
- { |
- int hx = (x0 + x1) >> 1; |
- int hy = (y0 + y1) >> 1; |
- do_anti_hairline(x0, y0, hx, hy, clip, blitter); |
- do_anti_hairline(hx, hy, x1, y1, clip, blitter); |
- return; |
- } |
- |
- int scaleStart, scaleStop; |
- int istart, istop; |
- SkFixed fstart, slope; |
- LineProc proc; |
- |
- if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) // mostly horizontal |
- { |
- if (x0 > x1) { // we want to go left-to-right |
- SkTSwap<SkFDot6>(x0, x1); |
- SkTSwap<SkFDot6>(y0, y1); |
- } |
- |
- istart = SkFDot6Floor(x0); |
- istop = SkFDot6Ceil(x1); |
- fstart = SkFDot6ToFixed(y0); |
- if (y0 == y1) { // completely horizontal, take fast case |
- slope = 0; |
- proc = hline; |
- } else { |
- slope = fastfixdiv(y1 - y0, x1 - x0); |
- SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1); |
- fstart += (slope * (32 - (x0 & 63)) + 32) >> 6; |
- proc = horish; |
- } |
- |
- SkASSERT(istop > istart); |
- if (istop - istart == 1) { |
- scaleStart = x1 - x0; |
- SkASSERT(scaleStart >= 0 && scaleStart <= 64); |
- scaleStop = 0; |
- } else { |
- scaleStart = 64 - (x0 & 63); |
- scaleStop = x1 & 63; |
- } |
- |
- if (clip) |
- { |
- if (istart >= clip->fRight || istop <= clip->fLeft) |
- return; |
- if (istart < clip->fLeft) |
- { |
- fstart += slope * (clip->fLeft - istart); |
- istart = clip->fLeft; |
- scaleStart = 64; |
- } |
- if (istop > clip->fRight) { |
- istop = clip->fRight; |
- scaleStop = 64; |
- } |
- SkASSERT(istart <= istop); |
- if (istart == istop) |
- return; |
- |
- // now test if our Y values are completely inside the clip |
- int top, bottom; |
- if (slope >= 0) // T2B |
- { |
- top = SkFixedFloor(fstart - SK_FixedHalf); |
- bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf); |
- } |
- else // B2T |
- { |
- bottom = SkFixedCeil(fstart + SK_FixedHalf); |
- top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf); |
- } |
- if (OUTSET_BEFORE_CLIP_TEST) { |
- top -= 1; |
- bottom += 1; |
- } |
- if (top >= clip->fBottom || bottom <= clip->fTop) |
- return; |
- if (clip->fTop <= top && clip->fBottom >= bottom) |
- clip = NULL; |
- } |
- } |
- else // mostly vertical |
- { |
- if (y0 > y1) // we want to go top-to-bottom |
- { |
- SkTSwap<SkFDot6>(x0, x1); |
- SkTSwap<SkFDot6>(y0, y1); |
- } |
- |
- istart = SkFDot6Floor(y0); |
- istop = SkFDot6Ceil(y1); |
- fstart = SkFDot6ToFixed(x0); |
- if (x0 == x1) |
- { |
- if (y0 == y1) { // are we zero length? |
- return; // nothing to do |
- } |
- slope = 0; |
- proc = vline; |
- } |
- else |
- { |
- slope = fastfixdiv(x1 - x0, y1 - y0); |
- SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1); |
- fstart += (slope * (32 - (y0 & 63)) + 32) >> 6; |
- proc = vertish; |
- } |
- |
- SkASSERT(istop > istart); |
- if (istop - istart == 1) { |
- scaleStart = y1 - y0; |
- SkASSERT(scaleStart >= 0 && scaleStart <= 64); |
- scaleStop = 0; |
- } else { |
- scaleStart = 64 - (y0 & 63); |
- scaleStop = y1 & 63; |
- } |
- |
- if (clip) |
- { |
- if (istart >= clip->fBottom || istop <= clip->fTop) |
- return; |
- if (istart < clip->fTop) |
- { |
- fstart += slope * (clip->fTop - istart); |
- istart = clip->fTop; |
- scaleStart = 64; |
- } |
- if (istop > clip->fBottom) { |
- istop = clip->fBottom; |
- scaleStop = 64; |
- } |
- SkASSERT(istart <= istop); |
- if (istart == istop) |
- return; |
- |
- // now test if our X values are completely inside the clip |
- int left, right; |
- if (slope >= 0) // L2R |
- { |
- left = SkFixedFloor(fstart - SK_FixedHalf); |
- right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf); |
- } |
- else // R2L |
- { |
- right = SkFixedCeil(fstart + SK_FixedHalf); |
- left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf); |
- } |
- if (OUTSET_BEFORE_CLIP_TEST) { |
- left -= 1; |
- right += 1; |
- } |
- if (left >= clip->fRight || right <= clip->fLeft) |
- return; |
- if (clip->fLeft <= left && clip->fRight >= right) |
- clip = NULL; |
- } |
- } |
- |
- SkRectClipBlitter rectClipper; |
- if (clip) |
- { |
- rectClipper.init(blitter, *clip); |
- blitter = &rectClipper; |
- } |
- |
- fstart = proc(istart, istart + 1, fstart, slope, blitter, scaleStart); |
- istart += 1; |
- int fullSpans = istop - istart - 1; |
- if (fullSpans > 0) { |
- fstart = proc(istart, istart + fullSpans, fstart, slope, blitter, 64); |
- } |
- if (scaleStop > 0) { |
- proc(istop - 1, istop, fstart, slope, blitter, scaleStop); |
- } |
-} |
- |
-void SkScan::AntiHairLine(const SkPoint& pt0, const SkPoint& pt1, |
- const SkRegion* clip, SkBlitter* blitter) |
-{ |
- if (clip && clip->isEmpty()) |
- return; |
- |
- SkASSERT(clip == NULL || !clip->getBounds().isEmpty()); |
- |
-#ifdef TEST_GAMMA |
- build_gamma_table(); |
-#endif |
- |
- SkFDot6 x0 = SkScalarToFDot6(pt0.fX); |
- SkFDot6 y0 = SkScalarToFDot6(pt0.fY); |
- SkFDot6 x1 = SkScalarToFDot6(pt1.fX); |
- SkFDot6 y1 = SkScalarToFDot6(pt1.fY); |
- |
- if (clip) |
- { |
- SkFDot6 left = SkMin32(x0, x1); |
- SkFDot6 top = SkMin32(y0, y1); |
- SkFDot6 right = SkMax32(x0, x1); |
- SkFDot6 bottom = SkMax32(y0, y1); |
- SkIRect ir; |
- |
- ir.set( SkFDot6Floor(left) - 1, |
- SkFDot6Floor(top) - 1, |
- SkFDot6Ceil(right) + 1, |
- SkFDot6Ceil(bottom) + 1); |
- |
- if (clip->quickReject(ir)) |
- return; |
- if (!clip->quickContains(ir)) |
- { |
- SkRegion::Cliperator iter(*clip, ir); |
- const SkIRect* r = &iter.rect(); |
- |
- while (!iter.done()) |
- { |
- do_anti_hairline(x0, y0, x1, y1, r, blitter); |
- iter.next(); |
- } |
- return; |
- } |
- // fall through to no-clip case |
- } |
- do_anti_hairline(x0, y0, x1, y1, NULL, blitter); |
-} |
- |
-void SkScan::AntiHairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter) |
-{ |
- if (clip) |
- { |
- SkIRect ir; |
- SkRect r = rect; |
- |
- r.inset(-SK_Scalar1/2, -SK_Scalar1/2); |
- r.roundOut(&ir); |
- if (clip->quickReject(ir)) |
- return; |
- if (clip->quickContains(ir)) |
- clip = NULL; |
- } |
- |
- SkPoint p0, p1; |
- |
- p0.set(rect.fLeft, rect.fTop); |
- p1.set(rect.fRight, rect.fTop); |
- SkScan::AntiHairLine(p0, p1, clip, blitter); |
- p0.set(rect.fRight, rect.fBottom); |
- SkScan::AntiHairLine(p0, p1, clip, blitter); |
- p1.set(rect.fLeft, rect.fBottom); |
- SkScan::AntiHairLine(p0, p1, clip, blitter); |
- p0.set(rect.fLeft, rect.fTop); |
- SkScan::AntiHairLine(p0, p1, clip, blitter); |
-} |
- |
-////////////////////////////////////////////////////////////////////////////////////////// |
- |
-typedef int FDot8; // 24.8 integer fixed point |
- |
-static inline FDot8 SkFixedToFDot8(SkFixed x) { |
- return (x + 0x80) >> 8; |
-} |
- |
-static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, SkBlitter* blitter) |
-{ |
- SkASSERT(L < R); |
- |
- if ((L >> 8) == ((R - 1) >> 8)) // 1x1 pixel |
- { |
- blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L)); |
- return; |
- } |
- |
- int left = L >> 8; |
- |
- if (L & 0xFF) |
- { |
- blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF))); |
- left += 1; |
- } |
- |
- int rite = R >> 8; |
- int width = rite - left; |
- if (width > 0) |
- call_hline_blitter(blitter, left, top, width, alpha); |
- |
- if (R & 0xFF) |
- blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF)); |
-} |
- |
-static void antifillrect(const SkXRect& xr, SkBlitter* blitter) |
-{ |
- FDot8 L = SkFixedToFDot8(xr.fLeft); |
- FDot8 T = SkFixedToFDot8(xr.fTop); |
- FDot8 R = SkFixedToFDot8(xr.fRight); |
- FDot8 B = SkFixedToFDot8(xr.fBottom); |
- |
- // check for empty now that we're in our reduced precision space |
- if (L >= R || T >= B) |
- return; |
- |
- int top = T >> 8; |
- if (top == ((B - 1) >> 8)) // just one scanline high |
- { |
- do_scanline(L, top, R, B - T - 1, blitter); |
- return; |
- } |
- |
- if (T & 0xFF) |
- { |
- do_scanline(L, top, R, 256 - (T & 0xFF), blitter); |
- top += 1; |
- } |
- |
- int bot = B >> 8; |
- int height = bot - top; |
- if (height > 0) |
- { |
- int left = L >> 8; |
- if (L & 0xFF) |
- { |
- blitter->blitV(left, top, height, 256 - (L & 0xFF)); |
- left += 1; |
- } |
- int rite = R >> 8; |
- int width = rite - left; |
- if (width > 0) |
- blitter->blitRect(left, top, width, height); |
- if (R & 0xFF) |
- blitter->blitV(rite, top, height, R & 0xFF); |
- } |
- |
- if (B & 0xFF) |
- do_scanline(L, bot, R, B & 0xFF, blitter); |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip, |
- SkBlitter* blitter) { |
- if (clip) { |
- SkIRect outerBounds; |
- XRect_roundOut(xr, &outerBounds); |
- |
- if (clip->isRect()) { |
- const SkIRect& clipBounds = clip->getBounds(); |
- |
- if (clipBounds.contains(outerBounds)) { |
- antifillrect(xr, blitter); |
- } else { |
- SkXRect tmpR; |
- // this keeps our original edges fractional |
- XRect_set(&tmpR, clipBounds); |
- if (tmpR.intersect(xr)) { |
- antifillrect(tmpR, blitter); |
- } |
- } |
- } else { |
- SkRegion::Cliperator clipper(*clip, outerBounds); |
- const SkIRect& rr = clipper.rect(); |
- |
- while (!clipper.done()) { |
- SkXRect tmpR; |
- |
- // this keeps our original edges fractional |
- XRect_set(&tmpR, rr); |
- if (tmpR.intersect(xr)) { |
- antifillrect(tmpR, blitter); |
- } |
- clipper.next(); |
- } |
- } |
- } else { |
- antifillrect(xr, blitter); |
- } |
-} |
- |
-#ifdef SK_SCALAR_IS_FLOAT |
- |
-/* This guy takes a float-rect, but with the key improvement that it has |
- already been clipped, so we know that it is safe to convert it into a |
- XRect (fixedpoint), as it won't overflow. |
-*/ |
-static void antifillrect(const SkRect& r, SkBlitter* blitter) { |
- SkXRect xr; |
- |
- XRect_set(&xr, r); |
- antifillrect(xr, blitter); |
-} |
- |
-/* We repeat the clipping logic of AntiFillXRect because the float rect might |
- overflow if we blindly converted it to an XRect. This sucks that we have to |
- repeat the clipping logic, but I don't see how to share the code/logic. |
- |
- We clip r (as needed) into one or more (smaller) float rects, and then pass |
- those to our version of antifillrect, which converts it into an XRect and |
- then calls the blit. |
-*/ |
-void SkScan::AntiFillRect(const SkRect& r, const SkRegion* clip, |
- SkBlitter* blitter) { |
- if (clip) { |
- SkIRect outerBounds; |
- r.roundOut(&outerBounds); |
- |
- if (clip->isRect()) { |
- const SkIRect& clipBounds = clip->getBounds(); |
- |
- if (clipBounds.contains(outerBounds)) { |
- antifillrect(r, blitter); |
- } else { |
- SkRect tmpR; |
- // this keeps our original edges fractional |
- tmpR.set(clipBounds); |
- if (tmpR.intersect(r)) { |
- antifillrect(tmpR, blitter); |
- } |
- } |
- } else { |
- SkRegion::Cliperator clipper(*clip, outerBounds); |
- const SkIRect& rr = clipper.rect(); |
- |
- while (!clipper.done()) { |
- SkRect tmpR; |
- // this keeps our original edges fractional |
- tmpR.set(rr); |
- if (tmpR.intersect(r)) { |
- antifillrect(tmpR, blitter); |
- } |
- clipper.next(); |
- } |
- } |
- } else { |
- antifillrect(r, blitter); |
- } |
-} |
- |
-#endif |
- |
- |