aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkScan_Antihair.cpp
diff options
context:
space:
mode:
authorGravatar reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2008-12-17 15:59:43 +0000
committerGravatar reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2008-12-17 15:59:43 +0000
commit8a1c16ff38322f0210116fa7293eb8817c7e477e (patch)
treefe40e07f6c8983318a2f79032b9a706ede1090c1 /src/core/SkScan_Antihair.cpp
parent2559c629078f738ac37095d896d580b681ac6a30 (diff)
grab from latest android
git-svn-id: http://skia.googlecode.com/svn/trunk@27 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/core/SkScan_Antihair.cpp')
-rw-r--r--src/core/SkScan_Antihair.cpp645
1 files changed, 645 insertions, 0 deletions
diff --git a/src/core/SkScan_Antihair.cpp b/src/core/SkScan_Antihair.cpp
new file mode 100644
index 0000000000..04e4690e63
--- /dev/null
+++ b/src/core/SkScan_Antihair.cpp
@@ -0,0 +1,645 @@
+/* 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"
+
+#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 (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 (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
+
+