| /* |
| * Copyright 2012 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| #ifndef SkPathOpsTypes_DEFINED |
| #define SkPathOpsTypes_DEFINED |
| |
| #include "include/core/SkPath.h" |
| #include "include/core/SkScalar.h" |
| #include "include/core/SkTypes.h" |
| #include "src/pathops/SkPathOpsDebug.h" |
| |
| #include <cfloat> |
| #include <cmath> |
| |
| enum SkPathOpsMask { |
| kWinding_PathOpsMask = -1, |
| kNo_PathOpsMask = 0, |
| kEvenOdd_PathOpsMask = 1 |
| }; |
| |
| class SkArenaAlloc; |
| class SkOpCoincidence; |
| class SkOpContour; |
| class SkOpContourHead; |
| |
| enum class SkOpPhase : char { |
| kNoChange, |
| kIntersecting, |
| kWalking, |
| kFixWinding, |
| }; |
| |
| class SkOpGlobalState { |
| public: |
| SkOpGlobalState(SkOpContourHead* head, |
| SkArenaAlloc* allocator SkDEBUGPARAMS(bool debugSkipAssert) |
| SkDEBUGPARAMS(const char* testName)); |
| |
| enum { |
| kMaxWindingTries = 10 |
| }; |
| |
| bool allocatedOpSpan() const { |
| return fAllocatedOpSpan; |
| } |
| |
| SkArenaAlloc* allocator() { |
| return fAllocator; |
| } |
| |
| void bumpNested() { |
| ++fNested; |
| } |
| |
| void clearNested() { |
| fNested = 0; |
| } |
| |
| SkOpCoincidence* coincidence() { |
| return fCoincidence; |
| } |
| |
| SkOpContourHead* contourHead() { |
| return fContourHead; |
| } |
| |
| #ifdef SK_DEBUG |
| const class SkOpAngle* debugAngle(int id) const; |
| const SkOpCoincidence* debugCoincidence() const; |
| SkOpContour* debugContour(int id) const; |
| const class SkOpPtT* debugPtT(int id) const; |
| #endif |
| |
| static bool DebugRunFail(); |
| |
| #ifdef SK_DEBUG |
| const class SkOpSegment* debugSegment(int id) const; |
| bool debugSkipAssert() const { return fDebugSkipAssert; } |
| const class SkOpSpanBase* debugSpan(int id) const; |
| const char* debugTestName() const { return fDebugTestName; } |
| #endif |
| |
| #if DEBUG_T_SECT_LOOP_COUNT |
| void debugAddLoopCount(SkIntersections* , const SkIntersectionHelper& , |
| const SkIntersectionHelper& ); |
| void debugDoYourWorst(SkOpGlobalState* ); |
| void debugLoopReport(); |
| void debugResetLoopCounts(); |
| #endif |
| |
| #if DEBUG_COINCIDENCE |
| void debugSetCheckHealth(bool check) { fDebugCheckHealth = check; } |
| bool debugCheckHealth() const { return fDebugCheckHealth; } |
| #endif |
| |
| #if DEBUG_VALIDATE || DEBUG_COIN |
| void debugSetPhase(const char* funcName DEBUG_COIN_DECLARE_PARAMS()) const; |
| #endif |
| |
| #if DEBUG_COIN |
| void debugAddToCoinChangedDict(); |
| void debugAddToGlobalCoinDicts(); |
| SkPathOpsDebug::CoinDict* debugCoinChangedDict() { return &fCoinChangedDict; } |
| const SkPathOpsDebug::CoinDictEntry& debugCoinDictEntry() const { return fCoinDictEntry; } |
| |
| static void DumpCoinDict(); |
| #endif |
| |
| |
| int nested() const { |
| return fNested; |
| } |
| |
| #ifdef SK_DEBUG |
| int nextAngleID() { |
| return ++fAngleID; |
| } |
| |
| int nextCoinID() { |
| return ++fCoinID; |
| } |
| |
| int nextContourID() { |
| return ++fContourID; |
| } |
| |
| int nextPtTID() { |
| return ++fPtTID; |
| } |
| |
| int nextSegmentID() { |
| return ++fSegmentID; |
| } |
| |
| int nextSpanID() { |
| return ++fSpanID; |
| } |
| #endif |
| |
| SkOpPhase phase() const { |
| return fPhase; |
| } |
| |
| void resetAllocatedOpSpan() { |
| fAllocatedOpSpan = false; |
| } |
| |
| void setAllocatedOpSpan() { |
| fAllocatedOpSpan = true; |
| } |
| |
| void setCoincidence(SkOpCoincidence* coincidence) { |
| fCoincidence = coincidence; |
| } |
| |
| void setContourHead(SkOpContourHead* contourHead) { |
| fContourHead = contourHead; |
| } |
| |
| void setPhase(SkOpPhase phase) { |
| if (SkOpPhase::kNoChange == phase) { |
| return; |
| } |
| SkASSERT(fPhase != phase); |
| fPhase = phase; |
| } |
| |
| // called in very rare cases where angles are sorted incorrectly -- signfies op will fail |
| void setWindingFailed() { |
| fWindingFailed = true; |
| } |
| |
| bool windingFailed() const { |
| return fWindingFailed; |
| } |
| |
| private: |
| SkArenaAlloc* fAllocator; |
| SkOpCoincidence* fCoincidence; |
| SkOpContourHead* fContourHead; |
| int fNested; |
| bool fAllocatedOpSpan; |
| bool fWindingFailed; |
| SkOpPhase fPhase; |
| #ifdef SK_DEBUG |
| const char* fDebugTestName; |
| void* fDebugReporter; |
| int fAngleID; |
| int fCoinID; |
| int fContourID; |
| int fPtTID; |
| int fSegmentID; |
| int fSpanID; |
| bool fDebugSkipAssert; |
| #endif |
| #if DEBUG_T_SECT_LOOP_COUNT |
| int fDebugLoopCount[3]; |
| SkPath::Verb fDebugWorstVerb[6]; |
| SkPoint fDebugWorstPts[24]; |
| float fDebugWorstWeight[6]; |
| #endif |
| #if DEBUG_COIN |
| SkPathOpsDebug::CoinDict fCoinChangedDict; |
| SkPathOpsDebug::CoinDict fCoinVisitedDict; |
| SkPathOpsDebug::CoinDictEntry fCoinDictEntry; |
| const char* fPreviousFuncName; |
| #endif |
| #if DEBUG_COINCIDENCE |
| bool fDebugCheckHealth; |
| #endif |
| }; |
| |
| #ifdef SK_DEBUG |
| #if DEBUG_COINCIDENCE |
| #define SkOPASSERT(cond) SkASSERT((this->globalState() && \ |
| (this->globalState()->debugCheckHealth() || \ |
| this->globalState()->debugSkipAssert())) || (cond)) |
| #else |
| #define SkOPASSERT(cond) SkASSERT((this->globalState() && \ |
| this->globalState()->debugSkipAssert()) || (cond)) |
| #endif |
| #define SkOPOBJASSERT(obj, cond) SkASSERT((obj->globalState() && \ |
| obj->globalState()->debugSkipAssert()) || (cond)) |
| #else |
| #define SkOPASSERT(cond) |
| #define SkOPOBJASSERT(obj, cond) |
| #endif |
| |
| // Use Almost Equal when comparing coordinates. Use epsilon to compare T values. |
| bool AlmostEqualUlps(float a, float b); |
| inline bool AlmostEqualUlps(double a, double b) { |
| return AlmostEqualUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); |
| } |
| |
| bool AlmostEqualUlpsNoNormalCheck(float a, float b); |
| inline bool AlmostEqualUlpsNoNormalCheck(double a, double b) { |
| return AlmostEqualUlpsNoNormalCheck(SkDoubleToScalar(a), SkDoubleToScalar(b)); |
| } |
| |
| bool AlmostEqualUlps_Pin(float a, float b); |
| inline bool AlmostEqualUlps_Pin(double a, double b) { |
| return AlmostEqualUlps_Pin(SkDoubleToScalar(a), SkDoubleToScalar(b)); |
| } |
| |
| // Use Almost Dequal when comparing should not special case denormalized values. |
| bool AlmostDequalUlps(float a, float b); |
| bool AlmostDequalUlps(double a, double b); |
| |
| bool NotAlmostEqualUlps(float a, float b); |
| inline bool NotAlmostEqualUlps(double a, double b) { |
| return NotAlmostEqualUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); |
| } |
| |
| bool NotAlmostEqualUlps_Pin(float a, float b); |
| inline bool NotAlmostEqualUlps_Pin(double a, double b) { |
| return NotAlmostEqualUlps_Pin(SkDoubleToScalar(a), SkDoubleToScalar(b)); |
| } |
| |
| bool NotAlmostDequalUlps(float a, float b); |
| inline bool NotAlmostDequalUlps(double a, double b) { |
| return NotAlmostDequalUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); |
| } |
| |
| // Use Almost Bequal when comparing coordinates in conjunction with between. |
| bool AlmostBequalUlps(float a, float b); |
| inline bool AlmostBequalUlps(double a, double b) { |
| return AlmostBequalUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); |
| } |
| |
| bool AlmostPequalUlps(float a, float b); |
| inline bool AlmostPequalUlps(double a, double b) { |
| return AlmostPequalUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); |
| } |
| |
| bool RoughlyEqualUlps(float a, float b); |
| inline bool RoughlyEqualUlps(double a, double b) { |
| return RoughlyEqualUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); |
| } |
| |
| bool AlmostLessUlps(float a, float b); |
| inline bool AlmostLessUlps(double a, double b) { |
| return AlmostLessUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); |
| } |
| |
| bool AlmostLessOrEqualUlps(float a, float b); |
| inline bool AlmostLessOrEqualUlps(double a, double b) { |
| return AlmostLessOrEqualUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); |
| } |
| |
| bool AlmostBetweenUlps(float a, float b, float c); |
| inline bool AlmostBetweenUlps(double a, double b, double c) { |
| return AlmostBetweenUlps(SkDoubleToScalar(a), SkDoubleToScalar(b), SkDoubleToScalar(c)); |
| } |
| |
| int UlpsDistance(float a, float b); |
| inline int UlpsDistance(double a, double b) { |
| return UlpsDistance(SkDoubleToScalar(a), SkDoubleToScalar(b)); |
| } |
| |
| // FLT_EPSILON == 1.19209290E-07 == 1 / (2 ^ 23) |
| // DBL_EPSILON == 2.22045e-16 |
| const double FLT_EPSILON_CUBED = FLT_EPSILON * FLT_EPSILON * FLT_EPSILON; |
| const double FLT_EPSILON_HALF = FLT_EPSILON / 2; |
| const double FLT_EPSILON_DOUBLE = FLT_EPSILON * 2; |
| const double FLT_EPSILON_ORDERABLE_ERR = FLT_EPSILON * 16; |
| const double FLT_EPSILON_SQUARED = FLT_EPSILON * FLT_EPSILON; |
| // Use a compile-time constant for FLT_EPSILON_SQRT to avoid initializers. |
| // A 17 digit constant guarantees exact results. |
| const double FLT_EPSILON_SQRT = 0.00034526697709225118; // sqrt(FLT_EPSILON); |
| const double FLT_EPSILON_INVERSE = 1 / FLT_EPSILON; |
| const double DBL_EPSILON_ERR = DBL_EPSILON * 4; // FIXME: tune -- allow a few bits of error |
| const double DBL_EPSILON_SUBDIVIDE_ERR = DBL_EPSILON * 16; |
| const double ROUGH_EPSILON = FLT_EPSILON * 64; |
| const double MORE_ROUGH_EPSILON = FLT_EPSILON * 256; |
| const double WAY_ROUGH_EPSILON = FLT_EPSILON * 2048; |
| const double BUMP_EPSILON = FLT_EPSILON * 4096; |
| |
| const SkScalar INVERSE_NUMBER_RANGE = FLT_EPSILON_ORDERABLE_ERR; |
| |
| inline bool zero_or_one(double x) { |
| return x == 0 || x == 1; |
| } |
| |
| inline bool approximately_zero(double x) { |
| return fabs(x) < FLT_EPSILON; |
| } |
| |
| inline bool precisely_zero(double x) { |
| return fabs(x) < DBL_EPSILON_ERR; |
| } |
| |
| inline bool precisely_subdivide_zero(double x) { |
| return fabs(x) < DBL_EPSILON_SUBDIVIDE_ERR; |
| } |
| |
| inline bool approximately_zero(float x) { |
| return fabs(x) < FLT_EPSILON; |
| } |
| |
| inline bool approximately_zero_half(double x) { |
| return fabs(x) < FLT_EPSILON_HALF; |
| } |
| |
| inline bool approximately_zero_double(double x) { |
| return fabs(x) < FLT_EPSILON_DOUBLE; |
| } |
| |
| inline bool approximately_zero_orderable(double x) { |
| return fabs(x) < FLT_EPSILON_ORDERABLE_ERR; |
| } |
| |
| inline bool approximately_zero_squared(double x) { |
| return fabs(x) < FLT_EPSILON_SQUARED; |
| } |
| |
| inline bool approximately_zero_sqrt(double x) { |
| return fabs(x) < FLT_EPSILON_SQRT; |
| } |
| |
| inline bool roughly_zero(double x) { |
| return fabs(x) < ROUGH_EPSILON; |
| } |
| |
| inline bool approximately_zero_inverse(double x) { |
| return fabs(x) > FLT_EPSILON_INVERSE; |
| } |
| |
| inline bool approximately_zero_when_compared_to(double x, double y) { |
| return x == 0 || fabs(x) < fabs(y * FLT_EPSILON); |
| } |
| |
| inline bool precisely_zero_when_compared_to(double x, double y) { |
| return x == 0 || fabs(x) < fabs(y * DBL_EPSILON); |
| } |
| |
| inline bool roughly_zero_when_compared_to(double x, double y) { |
| return x == 0 || fabs(x) < fabs(y * ROUGH_EPSILON); |
| } |
| |
| // Use this for comparing Ts in the range of 0 to 1. For general numbers (larger and smaller) use |
| // AlmostEqualUlps instead. |
| inline bool approximately_equal(double x, double y) { |
| return approximately_zero(x - y); |
| } |
| |
| inline bool precisely_equal(double x, double y) { |
| return precisely_zero(x - y); |
| } |
| |
| inline bool precisely_subdivide_equal(double x, double y) { |
| return precisely_subdivide_zero(x - y); |
| } |
| |
| inline bool approximately_equal_half(double x, double y) { |
| return approximately_zero_half(x - y); |
| } |
| |
| inline bool approximately_equal_double(double x, double y) { |
| return approximately_zero_double(x - y); |
| } |
| |
| inline bool approximately_equal_orderable(double x, double y) { |
| return approximately_zero_orderable(x - y); |
| } |
| |
| inline bool approximately_equal_squared(double x, double y) { |
| return approximately_equal(x, y); |
| } |
| |
| inline bool approximately_greater(double x, double y) { |
| return x - FLT_EPSILON >= y; |
| } |
| |
| inline bool approximately_greater_double(double x, double y) { |
| return x - FLT_EPSILON_DOUBLE >= y; |
| } |
| |
| inline bool approximately_greater_orderable(double x, double y) { |
| return x - FLT_EPSILON_ORDERABLE_ERR >= y; |
| } |
| |
| inline bool approximately_greater_or_equal(double x, double y) { |
| return x + FLT_EPSILON > y; |
| } |
| |
| inline bool approximately_greater_or_equal_double(double x, double y) { |
| return x + FLT_EPSILON_DOUBLE > y; |
| } |
| |
| inline bool approximately_greater_or_equal_orderable(double x, double y) { |
| return x + FLT_EPSILON_ORDERABLE_ERR > y; |
| } |
| |
| inline bool approximately_lesser(double x, double y) { |
| return x + FLT_EPSILON <= y; |
| } |
| |
| inline bool approximately_lesser_double(double x, double y) { |
| return x + FLT_EPSILON_DOUBLE <= y; |
| } |
| |
| inline bool approximately_lesser_orderable(double x, double y) { |
| return x + FLT_EPSILON_ORDERABLE_ERR <= y; |
| } |
| |
| inline bool approximately_lesser_or_equal(double x, double y) { |
| return x - FLT_EPSILON < y; |
| } |
| |
| inline bool approximately_lesser_or_equal_double(double x, double y) { |
| return x - FLT_EPSILON_DOUBLE < y; |
| } |
| |
| inline bool approximately_lesser_or_equal_orderable(double x, double y) { |
| return x - FLT_EPSILON_ORDERABLE_ERR < y; |
| } |
| |
| inline bool approximately_greater_than_one(double x) { |
| return x > 1 - FLT_EPSILON; |
| } |
| |
| inline bool precisely_greater_than_one(double x) { |
| return x > 1 - DBL_EPSILON_ERR; |
| } |
| |
| inline bool approximately_less_than_zero(double x) { |
| return x < FLT_EPSILON; |
| } |
| |
| inline bool precisely_less_than_zero(double x) { |
| return x < DBL_EPSILON_ERR; |
| } |
| |
| inline bool approximately_negative(double x) { |
| return x < FLT_EPSILON; |
| } |
| |
| inline bool approximately_negative_orderable(double x) { |
| return x < FLT_EPSILON_ORDERABLE_ERR; |
| } |
| |
| inline bool precisely_negative(double x) { |
| return x < DBL_EPSILON_ERR; |
| } |
| |
| inline bool approximately_one_or_less(double x) { |
| return x < 1 + FLT_EPSILON; |
| } |
| |
| inline bool approximately_one_or_less_double(double x) { |
| return x < 1 + FLT_EPSILON_DOUBLE; |
| } |
| |
| inline bool approximately_positive(double x) { |
| return x > -FLT_EPSILON; |
| } |
| |
| inline bool approximately_positive_squared(double x) { |
| return x > -(FLT_EPSILON_SQUARED); |
| } |
| |
| inline bool approximately_zero_or_more(double x) { |
| return x > -FLT_EPSILON; |
| } |
| |
| inline bool approximately_zero_or_more_double(double x) { |
| return x > -FLT_EPSILON_DOUBLE; |
| } |
| |
| inline bool approximately_between_orderable(double a, double b, double c) { |
| return a <= c |
| ? approximately_negative_orderable(a - b) && approximately_negative_orderable(b - c) |
| : approximately_negative_orderable(b - a) && approximately_negative_orderable(c - b); |
| } |
| |
| inline bool approximately_between(double a, double b, double c) { |
| return a <= c ? approximately_negative(a - b) && approximately_negative(b - c) |
| : approximately_negative(b - a) && approximately_negative(c - b); |
| } |
| |
| inline bool precisely_between(double a, double b, double c) { |
| return a <= c ? precisely_negative(a - b) && precisely_negative(b - c) |
| : precisely_negative(b - a) && precisely_negative(c - b); |
| } |
| |
| // returns true if (a <= b <= c) || (a >= b >= c) |
| inline bool between(double a, double b, double c) { |
| SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0) |
| || (precisely_zero(a) && precisely_zero(b) && precisely_zero(c))); |
| return (a - b) * (c - b) <= 0; |
| } |
| |
| inline bool roughly_equal(double x, double y) { |
| return fabs(x - y) < ROUGH_EPSILON; |
| } |
| |
| inline bool roughly_negative(double x) { |
| return x < ROUGH_EPSILON; |
| } |
| |
| inline bool roughly_between(double a, double b, double c) { |
| return a <= c ? roughly_negative(a - b) && roughly_negative(b - c) |
| : roughly_negative(b - a) && roughly_negative(c - b); |
| } |
| |
| inline bool more_roughly_equal(double x, double y) { |
| return fabs(x - y) < MORE_ROUGH_EPSILON; |
| } |
| |
| inline SkPath::Verb SkPathOpsPointsToVerb(int points) { |
| int verb = (1 << points) >> 1; |
| #ifdef SK_DEBUG |
| switch (points) { |
| case 0: SkASSERT(SkPath::kMove_Verb == verb); break; |
| case 1: SkASSERT(SkPath::kLine_Verb == verb); break; |
| case 2: SkASSERT(SkPath::kQuad_Verb == verb); break; |
| case 3: SkASSERT(SkPath::kCubic_Verb == verb); break; |
| default: SkDEBUGFAIL("should not be here"); |
| } |
| #endif |
| return (SkPath::Verb)verb; |
| } |
| |
| inline int SkPathOpsVerbToPoints(SkPath::Verb verb) { |
| int points = (int) verb - (((int) verb + 1) >> 2); |
| #ifdef SK_DEBUG |
| switch (verb) { |
| case SkPath::kLine_Verb: SkASSERT(1 == points); break; |
| case SkPath::kQuad_Verb: SkASSERT(2 == points); break; |
| case SkPath::kConic_Verb: SkASSERT(2 == points); break; |
| case SkPath::kCubic_Verb: SkASSERT(3 == points); break; |
| default: SkDEBUGFAIL("should not get here"); |
| } |
| #endif |
| return points; |
| } |
| |
| inline double SkDInterp(double A, double B, double t) { |
| return A + (B - A) * t; |
| } |
| |
| /* Returns -1 if negative, 0 if zero, 1 if positive |
| */ |
| inline int SkDSign(double x) { |
| return (x > 0) - (x < 0); |
| } |
| |
| /* Returns 0 if negative, 1 if zero, 2 if positive |
| */ |
| inline int SKDSide(double x) { |
| return (x > 0) + (x >= 0); |
| } |
| |
| /* Returns 1 if negative, 2 if zero, 4 if positive |
| */ |
| inline int SkDSideBit(double x) { |
| return 1 << SKDSide(x); |
| } |
| |
| inline double SkPinT(double t) { |
| return precisely_less_than_zero(t) ? 0 : precisely_greater_than_one(t) ? 1 : t; |
| } |
| |
| #endif |