| Index: src/core/SkAnalyticEdge.cpp
|
| diff --git a/src/core/SkAnalyticEdge.cpp b/src/core/SkAnalyticEdge.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..fde37e09db76014789c67cc3e9b7279847ee67f3
|
| --- /dev/null
|
| +++ b/src/core/SkAnalyticEdge.cpp
|
| @@ -0,0 +1,245 @@
|
| +/*
|
| + * Copyright 2006 The Android Open Source Project
|
| + *
|
| + * Use of this source code is governed by a BSD-style license that can be
|
| + * found in the LICENSE file.
|
| + */
|
| +
|
| +
|
| +#include "SkAnalyticEdge.h"
|
| +#include "SkFDot6.h"
|
| +#include "SkMathPriv.h"
|
| +#include "SkAAAConstants.h"
|
| +
|
| +class QuickFDot6Inverse {
|
| +private:
|
| + static constexpr const SkFDot6* table = gFDot6INVERSE + kInverseTableSize;
|
| +public:
|
| + inline static SkFixed Lookup(SkFDot6 x) {
|
| + SkASSERT(SkAbs32(x) < kInverseTableSize);
|
| + return table[x];
|
| + }
|
| +};
|
| +
|
| +static inline SkFixed quickSkFDot6Div(SkFDot6 a, SkFDot6 b) {
|
| + if (SkAbs32(b) < kInverseTableSize) {
|
| + SkASSERT((int64_t)a * QuickFDot6Inverse::Lookup(b) <= SK_MaxS32);
|
| + SkFixed ourAnswer = (a * QuickFDot6Inverse::Lookup(b)) >> 6;
|
| + #ifdef SK_DEBUG
|
| + SkFixed directAnswer = SkFDot6Div(a, b);
|
| + SkASSERT(
|
| + (directAnswer == 0 && ourAnswer == 0) ||
|
| + SkFixedDiv(SkAbs32(directAnswer - ourAnswer), SkAbs32(directAnswer)) <= 1 << 10
|
| + );
|
| + #endif
|
| + return ourAnswer;
|
| + } else {
|
| + return SkFDot6Div(a, b);
|
| + }
|
| +}
|
| +
|
| +// This will become a bottleneck for small ovals rendering if we call SkFixedDiv twice here.
|
| +// Therefore, we'll let the outter function compute the slope once and send in the value.
|
| +// Moreover, we'll compute fDY by quickly lookup the inverse table (if possible).
|
| +bool SkAnalyticEdge::updateLine(SkFixed x0, SkFixed y0, SkFixed x1, SkFixed y1, SkFixed slope) {
|
| + // Since we send in the slope, we can no longer snap y inside this function.
|
| + // If we don't send in the slope, or we do some more sophisticated snapping, this function
|
| + // could be a performance bottleneck.
|
| + SkASSERT(fWinding == 1 || fWinding == -1);
|
| + SkASSERT(fCurveCount != 0);
|
| +
|
| + SkASSERT(y0 <= y1);
|
| +
|
| + SkFDot6 dx = SkFixedToFDot6(x1 - x0);
|
| + SkFDot6 dy = SkFixedToFDot6(y1 - y0);
|
| +
|
| + // are we a zero-height line?
|
| + if (dy == 0) {
|
| + return false;
|
| + }
|
| +
|
| + SkASSERT(slope < SK_MaxS32);
|
| +
|
| + SkFDot6 absSlope = SkAbs32(SkFixedToFDot6(slope));
|
| + fX = x0;
|
| + fDX = slope;
|
| + fUpperX = x0;
|
| + fY = y0;
|
| + fUpperY = y0;
|
| + fLowerY = y1;
|
| + fDY = (absSlope | dx) == 0
|
| + ? SK_MaxS32
|
| + : absSlope < kInverseTableSize
|
| + ? QuickFDot6Inverse::Lookup(absSlope)
|
| + : SkAbs32(quickSkFDot6Div(dy, dx));
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void SkAnalyticEdge::chopLineWithClip(const SkIRect& clip) {
|
| + int top = SkFixedFloorToInt(fUpperY);
|
| +
|
| + SkASSERT(top < clip.fBottom);
|
| +
|
| + // clip the line to the clip top
|
| + if (top < clip.fTop) {
|
| + SkASSERT(SkFixedCeilToInt(fLowerY) > clip.fTop);
|
| + SkFixed newY = SkIntToFixed(clip.fTop);
|
| + this->goY(newY);
|
| + fUpperY = newY;
|
| + }
|
| +}
|
| +
|
| +bool SkAnalyticQuadraticEdge::setQuadratic(const SkPoint pts[3]) {
|
| + if (!fQEdge.setQuadraticWithoutUpdate(pts, 2)) {
|
| + return false;
|
| + }
|
| + fQEdge.fQx >>= 2;
|
| + fQEdge.fQy >>= 2;
|
| + fQEdge.fQDx >>= 2;
|
| + fQEdge.fQDy >>= 2;
|
| + fQEdge.fQDDx >>= 2;
|
| + fQEdge.fQDDy >>= 2;
|
| + fQEdge.fQLastX >>= 2;
|
| + fQEdge.fQLastY >>= 2;
|
| + fQEdge.fQy = snapY(fQEdge.fQy);
|
| + fQEdge.fQLastY = snapY(fQEdge.fQLastY);
|
| +
|
| + fWinding = fQEdge.fWinding;
|
| + fCurveCount = fQEdge.fCurveCount;
|
| + fCurveShift = fQEdge.fCurveShift;
|
| +
|
| + fSnappedX = fQEdge.fQx;
|
| + fSnappedY = fQEdge.fQy;
|
| +
|
| + return this->updateQuadratic();
|
| +}
|
| +
|
| +bool SkAnalyticQuadraticEdge::updateQuadratic() {
|
| + int success = 0; // initialize to fail!
|
| + int count = fCurveCount;
|
| + SkFixed oldx = fQEdge.fQx;
|
| + SkFixed oldy = fQEdge.fQy;
|
| + SkFixed dx = fQEdge.fQDx;
|
| + SkFixed dy = fQEdge.fQDy;
|
| + SkFixed newx, newy, newSnappedX, newSnappedY;
|
| + int shift = fCurveShift;
|
| +
|
| + SkASSERT(count > 0);
|
| +
|
| + do {
|
| + SkFixed slope;
|
| + if (--count > 0)
|
| + {
|
| + newx = oldx + (dx >> shift);
|
| + newy = snapY(oldy + (dy >> shift));
|
| + slope = dy >> 10 > 0 ? quickSkFDot6Div(dx >> 10, dy >> 10) : SK_MaxS32;
|
| + if (SkAbs32(dy) >= SK_Fixed1 * 2) { // only snap when dy is large enough
|
| + newSnappedY = SkTMin<SkFixed>(fQEdge.fQLastY, SkFixedRoundToFixed(newy));
|
| + newSnappedX = newx + SkFixedMul_lowprec(slope, newSnappedY - newy);
|
| + } else {
|
| + newSnappedY = newy;
|
| + newSnappedX = newx;
|
| + }
|
| + dx += fQEdge.fQDDx;
|
| + dy += fQEdge.fQDDy;
|
| + }
|
| + else // last segment
|
| + {
|
| + newx = fQEdge.fQLastX;
|
| + newy = fQEdge.fQLastY;
|
| + newSnappedY = newy;
|
| + newSnappedX = newx;
|
| + slope = (newSnappedY - fSnappedY) >> 10
|
| + ? quickSkFDot6Div((newx - fSnappedX) >> 10, (newy - fSnappedY) >> 10)
|
| + : SK_MaxS32;
|
| + }
|
| + if (slope < SK_MaxS32) {
|
| + success = this->updateLine(fSnappedX, fSnappedY, newSnappedX, newSnappedY, slope);
|
| + }
|
| + oldx = newx;
|
| + oldy = newy;
|
| + } while (count > 0 && !success);
|
| +
|
| + SkASSERT(newSnappedY <= fQEdge.fQLastY);
|
| +
|
| + fQEdge.fQx = newx;
|
| + fQEdge.fQy = newy;
|
| + fQEdge.fQDx = dx;
|
| + fQEdge.fQDy = dy;
|
| + fSnappedX = newSnappedX;
|
| + fSnappedY = newSnappedY;
|
| + fCurveCount = SkToS8(count);
|
| + return success;
|
| +}
|
| +
|
| +bool SkAnalyticCubicEdge::setCubic(const SkPoint pts[4]) {
|
| + if (!fCEdge.setCubicWithoutUpdate(pts, 2)) {
|
| + return false;
|
| + }
|
| +
|
| + fCEdge.fCx >>= 2;
|
| + fCEdge.fCy >>= 2;
|
| + fCEdge.fCDx >>= 2;
|
| + fCEdge.fCDy >>= 2;
|
| + fCEdge.fCDDx >>= 2;
|
| + fCEdge.fCDDy >>= 2;
|
| + fCEdge.fCDDDx >>= 2;
|
| + fCEdge.fCDDDy >>= 2;
|
| + fCEdge.fCLastX >>= 2;
|
| + fCEdge.fCLastY >>= 2;
|
| + fCEdge.fCy = snapY(fCEdge.fCy);
|
| + fCEdge.fCLastY = snapY(fCEdge.fCLastY);
|
| +
|
| + fWinding = fCEdge.fWinding;
|
| + fCurveCount = fCEdge.fCurveCount;
|
| + fCurveShift = fCEdge.fCurveShift;
|
| + fCubicDShift = fCEdge.fCubicDShift;
|
| +
|
| + return this->updateCubic();
|
| +}
|
| +
|
| +bool SkAnalyticCubicEdge::updateCubic() {
|
| + int success;
|
| + int count = fCurveCount;
|
| + SkFixed oldx = fCEdge.fCx;
|
| + SkFixed oldy = fCEdge.fCy;
|
| + SkFixed newx, newy;
|
| + const int ddshift = fCurveShift;
|
| + const int dshift = fCubicDShift;
|
| +
|
| + SkASSERT(count < 0);
|
| +
|
| + do {
|
| + if (++count < 0) {
|
| + newx = oldx + (fCEdge.fCDx >> dshift);
|
| + fCEdge.fCDx += fCEdge.fCDDx >> ddshift;
|
| + fCEdge.fCDDx += fCEdge.fCDDDx;
|
| +
|
| + newy = oldy + (fCEdge.fCDy >> dshift);
|
| + fCEdge.fCDy += fCEdge.fCDDy >> ddshift;
|
| + fCEdge.fCDDy += fCEdge.fCDDDy;
|
| + }
|
| + else { // last segment
|
| + newx = fCEdge.fCLastX;
|
| + newy = fCEdge.fCLastY;
|
| + }
|
| +
|
| + // we want to say SkASSERT(oldy <= newy), but our finite fixedpoint
|
| + // doesn't always achieve that, so we have to explicitly pin it here.
|
| + if (newy < oldy) {
|
| + newy = oldy;
|
| + }
|
| +
|
| + success = this->updateLine(oldx, oldy, newx, newy,
|
| + SkFixedToFDot6(newy - oldy) == 0 ? SK_MaxS32 :
|
| + SkFDot6Div(SkFixedToFDot6(newx - oldx), SkFixedToFDot6(newy - oldy)));
|
| + oldx = newx;
|
| + oldy = newy;
|
| + } while (count < 0 && !success);
|
| +
|
| + fCEdge.fCx = newx;
|
| + fCEdge.fCy = newy;
|
| + fCurveCount = SkToS8(count);
|
| + return success;
|
| +}
|
|
|