LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/pathops - SkPathOpsWinding.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 227 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 22 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright 2015 Google Inc.
       3             :  *
       4             :  * Use of this source code is governed by a BSD-style license that can be
       5             :  * found in the LICENSE file.
       6             :  */
       7             : 
       8             : // given a prospective edge, compute its initial winding by projecting a ray
       9             : // if the ray hits another edge
      10             :     // if the edge doesn't have a winding yet, hop up to that edge and start over
      11             :         // concern : check for hops forming a loop
      12             :     // if the edge is unsortable, or
      13             :     // the intersection is nearly at the ends, or
      14             :     // the tangent at the intersection is nearly coincident to the ray,
      15             :         // choose a different ray and try again
      16             :             // concern : if it is unable to succeed after N tries, try another edge? direction?
      17             : // if no edge is hit, compute the winding directly
      18             : 
      19             : // given the top span, project the most perpendicular ray and look for intersections
      20             :     // let's try up and then down. What the hey
      21             : 
      22             : // bestXY is initialized by caller with basePt
      23             : 
      24             : #include "SkOpContour.h"
      25             : #include "SkOpSegment.h"
      26             : #include "SkPathOpsCurve.h"
      27             : 
      28             : enum class SkOpRayDir {
      29             :     kLeft,
      30             :     kTop,
      31             :     kRight,
      32             :     kBottom,
      33             : };
      34             : 
      35             : #if DEBUG_WINDING
      36             : const char* gDebugRayDirName[] = {
      37             :     "kLeft",
      38             :     "kTop",
      39             :     "kRight",
      40             :     "kBottom"
      41             : };
      42             : #endif
      43             : 
      44           0 : static int xy_index(SkOpRayDir dir) {
      45           0 :     return static_cast<int>(dir) & 1;
      46             : }
      47             : 
      48           0 : static SkScalar pt_xy(const SkPoint& pt, SkOpRayDir dir) {
      49           0 :     return (&pt.fX)[xy_index(dir)];
      50             : }
      51             : 
      52           0 : static SkScalar pt_yx(const SkPoint& pt, SkOpRayDir dir) {
      53           0 :     return (&pt.fX)[!xy_index(dir)];
      54             : }
      55             : 
      56           0 : static double pt_dxdy(const SkDVector& v, SkOpRayDir dir) {
      57           0 :     return (&v.fX)[xy_index(dir)];
      58             : }
      59             : 
      60           0 : static double pt_dydx(const SkDVector& v, SkOpRayDir dir) {
      61           0 :     return (&v.fX)[!xy_index(dir)];
      62             : }
      63             : 
      64           0 : static SkScalar rect_side(const SkRect& r, SkOpRayDir dir) {
      65           0 :     return (&r.fLeft)[static_cast<int>(dir)];
      66             : }
      67             : 
      68           0 : static bool sideways_overlap(const SkRect& rect, const SkPoint& pt, SkOpRayDir dir) {
      69           0 :     int i = !xy_index(dir);
      70           0 :     return approximately_between((&rect.fLeft)[i], (&pt.fX)[i], (&rect.fRight)[i]);
      71             : }
      72             : 
      73           0 : static bool less_than(SkOpRayDir dir) {
      74           0 :     return static_cast<bool>((static_cast<int>(dir) & 2) == 0);
      75             : }
      76             : 
      77           0 : static bool ccw_dxdy(const SkDVector& v, SkOpRayDir dir) {
      78           0 :     bool vPartPos = pt_dydx(v, dir) > 0;
      79           0 :     bool leftBottom = ((static_cast<int>(dir) + 1) & 2) != 0;
      80           0 :     return vPartPos == leftBottom;
      81             : }
      82             : 
      83             : struct SkOpRayHit {
      84           0 :     SkOpRayDir makeTestBase(SkOpSpan* span, double t) {
      85           0 :         fNext = nullptr;
      86           0 :         fSpan = span;
      87           0 :         fT = span->t() * (1 - t) + span->next()->t() * t;
      88           0 :         SkOpSegment* segment = span->segment();
      89           0 :         fSlope = segment->dSlopeAtT(fT);
      90           0 :         fPt = segment->ptAtT(fT);
      91           0 :         fValid = true;
      92           0 :         return fabs(fSlope.fX) < fabs(fSlope.fY) ? SkOpRayDir::kLeft : SkOpRayDir::kTop;
      93             :     }
      94             : 
      95             :     SkOpRayHit* fNext;
      96             :     SkOpSpan* fSpan;
      97             :     SkPoint fPt;
      98             :     double fT;
      99             :     SkDVector fSlope;
     100             :     bool fValid;
     101             : };
     102             : 
     103           0 : void SkOpContour::rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits,
     104             :                            SkArenaAlloc* allocator) {
     105             :     // if the bounds extreme is outside the best, we're done
     106           0 :     SkScalar baseXY = pt_xy(base.fPt, dir);
     107           0 :     SkScalar boundsXY = rect_side(fBounds, dir);
     108           0 :     bool checkLessThan = less_than(dir);
     109           0 :     if (!approximately_equal(baseXY, boundsXY) && (baseXY < boundsXY) == checkLessThan) {
     110           0 :         return;
     111             :     }
     112           0 :     SkOpSegment* testSegment = &fHead;
     113           0 :     do {
     114           0 :         testSegment->rayCheck(base, dir, hits, allocator);
     115             :     } while ((testSegment = testSegment->next()));
     116             : }
     117             : 
     118           0 : void SkOpSegment::rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits,
     119             :                            SkArenaAlloc* allocator) {
     120           0 :     if (!sideways_overlap(fBounds, base.fPt, dir)) {
     121           0 :         return;
     122             :     }
     123           0 :     SkScalar baseXY = pt_xy(base.fPt, dir);
     124           0 :     SkScalar boundsXY = rect_side(fBounds, dir);
     125           0 :     bool checkLessThan = less_than(dir);
     126           0 :     if (!approximately_equal(baseXY, boundsXY) && (baseXY < boundsXY) == checkLessThan) {
     127           0 :         return;
     128             :     }
     129             :     double tVals[3];
     130           0 :     SkScalar baseYX = pt_yx(base.fPt, dir);
     131           0 :     int roots = (*CurveIntercept[fVerb * 2 + xy_index(dir)])(fPts, fWeight, baseYX, tVals);
     132           0 :     for (int index = 0; index < roots; ++index) {
     133           0 :         double t = tVals[index];
     134           0 :         if (base.fSpan->segment() == this && approximately_equal(base.fT, t)) {
     135           0 :             continue;
     136             :         }
     137             :         SkDVector slope;
     138             :         SkPoint pt;
     139           0 :         SkDEBUGCODE(sk_bzero(&slope, sizeof(slope)));
     140           0 :         bool valid = false;
     141           0 :         if (approximately_zero(t)) {
     142           0 :             pt = fPts[0];
     143           0 :         } else if (approximately_equal(t, 1)) {
     144           0 :             pt = fPts[SkPathOpsVerbToPoints(fVerb)];
     145             :         } else {
     146           0 :             SkASSERT(between(0, t, 1));
     147           0 :             pt = this->ptAtT(t);
     148           0 :             if (SkDPoint::ApproximatelyEqual(pt, base.fPt)) {
     149           0 :                 if (base.fSpan->segment() == this) {
     150           0 :                     continue;
     151             :                 }
     152             :             } else {
     153           0 :                 SkScalar ptXY = pt_xy(pt, dir);
     154           0 :                 if (!approximately_equal(baseXY, ptXY) && (baseXY < ptXY) == checkLessThan) {
     155           0 :                     continue;
     156             :                 }
     157           0 :                 slope = this->dSlopeAtT(t);
     158           0 :                 if (fVerb == SkPath::kCubic_Verb && base.fSpan->segment() == this
     159           0 :                         && roughly_equal(base.fT, t)
     160           0 :                         && SkDPoint::RoughlyEqual(pt, base.fPt)) {
     161             :     #if DEBUG_WINDING
     162             :                     SkDebugf("%s (rarely expect this)\n", __FUNCTION__);
     163             :     #endif
     164           0 :                     continue;
     165             :                 }
     166           0 :                 if (fabs(pt_dydx(slope, dir) * 10000) > fabs(pt_dxdy(slope, dir))) {
     167           0 :                     valid = true;
     168             :                 }
     169             :             }
     170             :         }
     171           0 :         SkOpSpan* span = this->windingSpanAtT(t);
     172           0 :         if (!span) {
     173           0 :             valid = false;
     174           0 :         } else if (!span->windValue() && !span->oppValue()) {
     175           0 :             continue;
     176             :         }
     177           0 :         SkOpRayHit* newHit = SkOpTAllocator<SkOpRayHit>::Allocate(allocator);
     178           0 :         newHit->fNext = *hits;
     179           0 :         newHit->fPt = pt;
     180           0 :         newHit->fSlope = slope;
     181           0 :         newHit->fSpan = span;
     182           0 :         newHit->fT = t;
     183           0 :         newHit->fValid = valid;
     184           0 :         *hits = newHit;
     185             :     }
     186             : }
     187             : 
     188           0 : SkOpSpan* SkOpSegment::windingSpanAtT(double tHit) {
     189           0 :     SkOpSpan* span = &fHead;
     190             :     SkOpSpanBase* next;
     191           0 :     do {
     192           0 :         next = span->next();
     193           0 :         if (approximately_equal(tHit, next->t())) {
     194           0 :             return nullptr;
     195             :         }
     196           0 :         if (tHit < next->t()) {
     197           0 :             return span;
     198             :         }
     199           0 :     } while (!next->final() && (span = next->upCast()));
     200           0 :     return nullptr;
     201             : }
     202             : 
     203           0 : static bool hit_compare_x(const SkOpRayHit* a, const SkOpRayHit* b) {
     204           0 :     return a->fPt.fX < b->fPt.fX;
     205             : }
     206             : 
     207           0 : static bool reverse_hit_compare_x(const SkOpRayHit* a, const SkOpRayHit* b) {
     208           0 :     return b->fPt.fX  < a->fPt.fX;
     209             : }
     210             : 
     211           0 : static bool hit_compare_y(const SkOpRayHit* a, const SkOpRayHit* b) {
     212           0 :     return a->fPt.fY < b->fPt.fY;
     213             : }
     214             : 
     215           0 : static bool reverse_hit_compare_y(const SkOpRayHit* a, const SkOpRayHit* b) {
     216           0 :     return b->fPt.fY  < a->fPt.fY;
     217             : }
     218             : 
     219           0 : static double get_t_guess(int tTry, int* dirOffset) {
     220           0 :     double t = 0.5;
     221           0 :     *dirOffset = tTry & 1;
     222           0 :     int tBase = tTry >> 1;
     223           0 :     int tBits = 0;
     224           0 :     while (tTry >>= 1) {
     225           0 :         t /= 2;
     226           0 :         ++tBits;
     227             :     }
     228           0 :     if (tBits) {
     229           0 :         int tIndex = (tBase - 1) & ((1 << tBits) - 1);
     230           0 :         t += t * 2 * tIndex;
     231             :     }
     232           0 :     return t;
     233             : }
     234             : 
     235           0 : bool SkOpSpan::sortableTop(SkOpContour* contourHead) {
     236             :     char storage[1024];
     237           0 :     SkArenaAlloc allocator(storage);
     238             :     int dirOffset;
     239           0 :     double t = get_t_guess(fTopTTry++, &dirOffset);
     240             :     SkOpRayHit hitBase;
     241           0 :     SkOpRayDir dir = hitBase.makeTestBase(this, t);
     242           0 :     if (hitBase.fSlope.fX == 0 && hitBase.fSlope.fY == 0) {
     243           0 :         return false;
     244             :     }
     245           0 :     SkOpRayHit* hitHead = &hitBase;
     246           0 :     dir = static_cast<SkOpRayDir>(static_cast<int>(dir) + dirOffset);
     247           0 :     if (hitBase.fSpan && hitBase.fSpan->segment()->verb() > SkPath::kLine_Verb
     248           0 :             && !pt_yx(hitBase.fSlope.asSkVector(), dir)) {
     249           0 :         return false;
     250             :     }
     251           0 :     SkOpContour* contour = contourHead;
     252           0 :     do {
     253           0 :         if (!contour->count()) {
     254           0 :             continue;
     255             :         }
     256           0 :         contour->rayCheck(hitBase, dir, &hitHead, &allocator);
     257             :     } while ((contour = contour->next()));
     258             :     // sort hits
     259           0 :     SkSTArray<1, SkOpRayHit*> sorted;
     260           0 :     SkOpRayHit* hit = hitHead;
     261           0 :     while (hit) {
     262           0 :         sorted.push_back(hit);
     263           0 :         hit = hit->fNext;
     264             :     }
     265           0 :     int count = sorted.count();
     266           0 :     SkTQSort(sorted.begin(), sorted.end() - 1, xy_index(dir)
     267           0 :             ? less_than(dir) ? hit_compare_y : reverse_hit_compare_y
     268           0 :             : less_than(dir) ? hit_compare_x : reverse_hit_compare_x);
     269             :     // verify windings
     270             : #if DEBUG_WINDING
     271             :     SkDebugf("%s dir=%s seg=%d t=%1.9g pt=(%1.9g,%1.9g)\n", __FUNCTION__,
     272             :             gDebugRayDirName[static_cast<int>(dir)], hitBase.fSpan->segment()->debugID(),
     273             :             hitBase.fT, hitBase.fPt.fX, hitBase.fPt.fY);
     274             :     for (int index = 0; index < count; ++index) {
     275             :         hit = sorted[index];
     276             :         SkOpSpan* span = hit->fSpan;
     277             :         SkOpSegment* hitSegment = span ? span->segment() : nullptr;
     278             :         bool operand = span ? hitSegment->operand() : false;
     279             :         bool ccw = ccw_dxdy(hit->fSlope, dir);
     280             :         SkDebugf("%s [%d] valid=%d operand=%d span=%d ccw=%d ", __FUNCTION__, index,
     281             :                 hit->fValid, operand, span ? span->debugID() : -1, ccw);
     282             :         if (span) {
     283             :             hitSegment->dumpPtsInner();
     284             :         }
     285             :         SkDebugf(" t=%1.9g pt=(%1.9g,%1.9g) slope=(%1.9g,%1.9g)\n", hit->fT,
     286             :                 hit->fPt.fX, hit->fPt.fY, hit->fSlope.fX, hit->fSlope.fY);
     287             :     }
     288             : #endif
     289           0 :     const SkPoint* last = nullptr;
     290           0 :     int wind = 0;
     291           0 :     int oppWind = 0;
     292           0 :     for (int index = 0; index < count; ++index) {
     293           0 :         hit = sorted[index];
     294           0 :         if (!hit->fValid) {
     295           0 :             return false;
     296             :         }
     297           0 :         bool ccw = ccw_dxdy(hit->fSlope, dir);
     298             : //        SkASSERT(!approximately_zero(hit->fT) || !hit->fValid);
     299           0 :         SkOpSpan* span = hit->fSpan;
     300           0 :         if (!span) {
     301           0 :             return false;
     302             :         }
     303           0 :         SkOpSegment* hitSegment = span->segment();
     304           0 :         if (span->windValue() == 0 && span->oppValue() == 0) {
     305           0 :             continue;
     306             :         }
     307           0 :         if (last && SkDPoint::ApproximatelyEqual(*last, hit->fPt)) {
     308           0 :             return false;
     309             :         }
     310           0 :         if (index < count - 1) {
     311           0 :             const SkPoint& next = sorted[index + 1]->fPt;
     312           0 :             if (SkDPoint::ApproximatelyEqual(next, hit->fPt)) {
     313           0 :                 return false;
     314             :             }
     315             :         }
     316           0 :         bool operand = hitSegment->operand();
     317           0 :         if (operand) {
     318           0 :             SkTSwap(wind, oppWind);
     319             :         }
     320           0 :         int lastWind = wind;
     321           0 :         int lastOpp = oppWind;
     322           0 :         int windValue = ccw ? -span->windValue() : span->windValue();
     323           0 :         int oppValue = ccw ? -span->oppValue() : span->oppValue();
     324           0 :         wind += windValue;
     325           0 :         oppWind += oppValue;
     326           0 :         bool sumSet = false;
     327           0 :         int spanSum = span->windSum();
     328           0 :         int windSum = SkOpSegment::UseInnerWinding(lastWind, wind) ? wind : lastWind;
     329           0 :         if (spanSum == SK_MinS32) {
     330           0 :             span->setWindSum(windSum);
     331           0 :             sumSet = true;
     332             :         } else {
     333             :             // the need for this condition suggests that UseInnerWinding is flawed
     334             :             // happened when last = 1 wind = -1
     335             : #if 0
     336             :             SkASSERT((hitSegment->isXor() ? (windSum & 1) == (spanSum & 1) : windSum == spanSum)
     337             :                     || (abs(wind) == abs(lastWind)
     338             :                     && (windSum ^ wind ^ lastWind) == spanSum));
     339             : #endif
     340             :         }
     341           0 :         int oSpanSum = span->oppSum();
     342           0 :         int oppSum = SkOpSegment::UseInnerWinding(lastOpp, oppWind) ? oppWind : lastOpp;
     343           0 :         if (oSpanSum == SK_MinS32) {
     344           0 :             span->setOppSum(oppSum);
     345             :         } else {
     346             : #if 0
     347             :             SkASSERT(hitSegment->oppXor() ? (oppSum & 1) == (oSpanSum & 1) : oppSum == oSpanSum
     348             :                     || (abs(oppWind) == abs(lastOpp)
     349             :                     && (oppSum ^ oppWind ^ lastOpp) == oSpanSum));
     350             : #endif
     351             :         }
     352           0 :         if (sumSet) {
     353           0 :             if (this->globalState()->phase() == SkOpPhase::kFixWinding) {
     354           0 :                 hitSegment->contour()->setCcw(ccw);
     355             :             } else {
     356           0 :                 (void) hitSegment->markAndChaseWinding(span, span->next(), windSum, oppSum, nullptr);
     357           0 :                 (void) hitSegment->markAndChaseWinding(span->next(), span, windSum, oppSum, nullptr);
     358             :             }
     359             :         }
     360           0 :         if (operand) {
     361           0 :             SkTSwap(wind, oppWind);
     362             :         }
     363           0 :         last = &hit->fPt;
     364           0 :         this->globalState()->bumpNested();
     365             :     }
     366           0 :     return true;
     367             : }
     368             : 
     369           0 : SkOpSpan* SkOpSegment::findSortableTop(SkOpContour* contourHead) {
     370           0 :     SkOpSpan* span = &fHead;
     371             :     SkOpSpanBase* next;
     372           0 :     do {
     373           0 :         next = span->next();
     374           0 :         if (span->done()) {
     375           0 :             continue;
     376             :         }
     377           0 :         if (span->windSum() != SK_MinS32) {
     378           0 :             return span;
     379             :         }
     380           0 :         if (span->sortableTop(contourHead)) {
     381           0 :             return span;
     382             :         }
     383           0 :     } while (!next->final() && (span = next->upCast()));
     384           0 :     return nullptr;
     385             : }
     386             : 
     387           0 : SkOpSpan* SkOpContour::findSortableTop(SkOpContour* contourHead) {
     388           0 :     bool allDone = true;
     389           0 :     if (fCount) {
     390           0 :         SkOpSegment* testSegment = &fHead;
     391           0 :         do {
     392           0 :             if (testSegment->done()) {
     393           0 :                 continue;
     394             :             }
     395           0 :             allDone = false;
     396           0 :             SkOpSpan* result = testSegment->findSortableTop(contourHead);
     397           0 :             if (result) {
     398           0 :                 return result;
     399             :             }
     400             :         } while ((testSegment = testSegment->next()));
     401             :     }
     402           0 :     if (allDone) {
     403           0 :       fDone = true;
     404             :     }
     405           0 :     return nullptr;
     406             : }
     407             : 
     408           0 : SkOpSpan* FindSortableTop(SkOpContourHead* contourHead) {
     409           0 :     for (int index = 0; index < SkOpGlobalState::kMaxWindingTries; ++index) {
     410           0 :         SkOpContour* contour = contourHead;
     411           0 :         do {
     412           0 :             if (contour->done()) {
     413           0 :                 continue;
     414             :             }
     415           0 :             SkOpSpan* result = contour->findSortableTop(contourHead);
     416           0 :             if (result) {
     417           0 :                 return result;
     418             :             }
     419             :         } while ((contour = contour->next()));
     420             :     }
     421           0 :     return nullptr;
     422             : }

Generated by: LCOV version 1.13