Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2011 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 #include "SkAnalyticEdge.h"
      9 #include "SkEdge.h"
     10 #include "SkEdgeBuilder.h"
     11 #include "SkEdgeClipper.h"
     12 #include "SkGeometry.h"
     13 #include "SkLineClipper.h"
     14 #include "SkPath.h"
     15 #include "SkPathPriv.h"
     16 #include "SkSafeMath.h"
     17 #include "SkTo.h"
     18 
     19 SkEdgeBuilder::Combine SkBasicEdgeBuilder::combineVertical(const SkEdge* edge, SkEdge* last) {
     20     if (last->fCurveCount || last->fDX || edge->fX != last->fX) {
     21         return kNo_Combine;
     22     }
     23     if (edge->fWinding == last->fWinding) {
     24         if (edge->fLastY + 1 == last->fFirstY) {
     25             last->fFirstY = edge->fFirstY;
     26             return kPartial_Combine;
     27         }
     28         if (edge->fFirstY == last->fLastY + 1) {
     29             last->fLastY = edge->fLastY;
     30             return kPartial_Combine;
     31         }
     32         return kNo_Combine;
     33     }
     34     if (edge->fFirstY == last->fFirstY) {
     35         if (edge->fLastY == last->fLastY) {
     36             return kTotal_Combine;
     37         }
     38         if (edge->fLastY < last->fLastY) {
     39             last->fFirstY = edge->fLastY + 1;
     40             return kPartial_Combine;
     41         }
     42         last->fFirstY = last->fLastY + 1;
     43         last->fLastY = edge->fLastY;
     44         last->fWinding = edge->fWinding;
     45         return kPartial_Combine;
     46     }
     47     if (edge->fLastY == last->fLastY) {
     48         if (edge->fFirstY > last->fFirstY) {
     49             last->fLastY = edge->fFirstY - 1;
     50             return kPartial_Combine;
     51         }
     52         last->fLastY = last->fFirstY - 1;
     53         last->fFirstY = edge->fFirstY;
     54         last->fWinding = edge->fWinding;
     55         return kPartial_Combine;
     56     }
     57     return kNo_Combine;
     58 }
     59 
     60 SkEdgeBuilder::Combine SkAnalyticEdgeBuilder::combineVertical(const SkAnalyticEdge* edge,
     61                                                               SkAnalyticEdge* last) {
     62     auto approximately_equal = [](SkFixed a, SkFixed b) {
     63         return SkAbs32(a - b) < 0x100;
     64     };
     65 
     66     if (last->fCurveCount || last->fDX || edge->fX != last->fX) {
     67         return kNo_Combine;
     68     }
     69     if (edge->fWinding == last->fWinding) {
     70         if (edge->fLowerY == last->fUpperY) {
     71             last->fUpperY = edge->fUpperY;
     72             last->fY = last->fUpperY;
     73             return kPartial_Combine;
     74         }
     75         if (approximately_equal(edge->fUpperY, last->fLowerY)) {
     76             last->fLowerY = edge->fLowerY;
     77             return kPartial_Combine;
     78         }
     79         return kNo_Combine;
     80     }
     81     if (approximately_equal(edge->fUpperY, last->fUpperY)) {
     82         if (approximately_equal(edge->fLowerY, last->fLowerY)) {
     83             return kTotal_Combine;
     84         }
     85         if (edge->fLowerY < last->fLowerY) {
     86             last->fUpperY = edge->fLowerY;
     87             last->fY = last->fUpperY;
     88             return kPartial_Combine;
     89         }
     90         last->fUpperY = last->fLowerY;
     91         last->fY = last->fUpperY;
     92         last->fLowerY = edge->fLowerY;
     93         last->fWinding = edge->fWinding;
     94         return kPartial_Combine;
     95     }
     96     if (approximately_equal(edge->fLowerY, last->fLowerY)) {
     97         if (edge->fUpperY > last->fUpperY) {
     98             last->fLowerY = edge->fUpperY;
     99             return kPartial_Combine;
    100         }
    101         last->fLowerY = last->fUpperY;
    102         last->fUpperY = edge->fUpperY;
    103         last->fY = last->fUpperY;
    104         last->fWinding = edge->fWinding;
    105         return kPartial_Combine;
    106     }
    107     return kNo_Combine;
    108 }
    109 
    110 template <typename Edge>
    111 static bool is_vertical(const Edge* edge) {
    112     return edge->fDX         == 0
    113         && edge->fCurveCount == 0;
    114 }
    115 
    116 // TODO: we can deallocate the edge if edge->setFoo() fails
    117 // or when we don't use it (kPartial_Combine or kTotal_Combine).
    118 
    119 void SkBasicEdgeBuilder::addLine(const SkPoint pts[]) {
    120     SkEdge* edge = fAlloc.make<SkEdge>();
    121     if (edge->setLine(pts[0], pts[1], fClipShift)) {
    122         Combine combine = is_vertical(edge) && !fList.empty()
    123             ? this->combineVertical(edge, (SkEdge*)fList.top())
    124             : kNo_Combine;
    125 
    126         switch (combine) {
    127             case kTotal_Combine:    fList.pop();           break;
    128             case kPartial_Combine:                         break;
    129             case kNo_Combine:       fList.push_back(edge); break;
    130         }
    131     }
    132 }
    133 void SkAnalyticEdgeBuilder::addLine(const SkPoint pts[]) {
    134     SkAnalyticEdge* edge = fAlloc.make<SkAnalyticEdge>();
    135     if (edge->setLine(pts[0], pts[1])) {
    136 
    137         Combine combine = is_vertical(edge) && !fList.empty()
    138             ? this->combineVertical(edge, (SkAnalyticEdge*)fList.top())
    139             : kNo_Combine;
    140 
    141         switch (combine) {
    142             case kTotal_Combine:    fList.pop();           break;
    143             case kPartial_Combine:                         break;
    144             case kNo_Combine:       fList.push_back(edge); break;
    145         }
    146     }
    147 }
    148 void SkBezierEdgeBuilder::addLine(const SkPoint pts[]) {
    149     SkLine* line = fAlloc.make<SkLine>();
    150     if (line->set(pts)) {
    151         fList.push_back(line);
    152     }
    153 }
    154 
    155 void SkBasicEdgeBuilder::addQuad(const SkPoint pts[]) {
    156     SkQuadraticEdge* edge = fAlloc.make<SkQuadraticEdge>();
    157     if (edge->setQuadratic(pts, fClipShift)) {
    158         fList.push_back(edge);
    159     }
    160 }
    161 void SkAnalyticEdgeBuilder::addQuad(const SkPoint pts[]) {
    162     SkAnalyticQuadraticEdge* edge = fAlloc.make<SkAnalyticQuadraticEdge>();
    163     if (edge->setQuadratic(pts)) {
    164         fList.push_back(edge);
    165     }
    166 }
    167 void SkBezierEdgeBuilder::addQuad(const SkPoint pts[]) {
    168     SkQuad* quad = fAlloc.make<SkQuad>();
    169     if (quad->set(pts)) {
    170         fList.push_back(quad);
    171     }
    172 }
    173 
    174 void SkBasicEdgeBuilder::addCubic(const SkPoint pts[]) {
    175     SkCubicEdge* edge = fAlloc.make<SkCubicEdge>();
    176     if (edge->setCubic(pts, fClipShift)) {
    177         fList.push_back(edge);
    178     }
    179 }
    180 void SkAnalyticEdgeBuilder::addCubic(const SkPoint pts[]) {
    181     SkAnalyticCubicEdge* edge = fAlloc.make<SkAnalyticCubicEdge>();
    182     if (edge->setCubic(pts)) {
    183         fList.push_back(edge);
    184     }
    185 }
    186 void SkBezierEdgeBuilder::addCubic(const SkPoint pts[]) {
    187     SkCubic* cubic = fAlloc.make<SkCubic>();
    188     if (cubic->set(pts)) {
    189         fList.push_back(cubic);
    190     }
    191 }
    192 
    193 // TODO: merge addLine() and addPolyLine()?
    194 
    195 SkEdgeBuilder::Combine SkBasicEdgeBuilder::addPolyLine(SkPoint pts[],
    196                                                        char* arg_edge, char** arg_edgePtr) {
    197     auto edge    = (SkEdge*) arg_edge;
    198     auto edgePtr = (SkEdge**)arg_edgePtr;
    199 
    200     if (edge->setLine(pts[0], pts[1], fClipShift)) {
    201         return is_vertical(edge) && edgePtr > (SkEdge**)fEdgeList
    202             ? this->combineVertical(edge, edgePtr[-1])
    203             : kNo_Combine;
    204     }
    205     return SkEdgeBuilder::kPartial_Combine;  // A convenient lie.  Same do-nothing behavior.
    206 }
    207 SkEdgeBuilder::Combine SkAnalyticEdgeBuilder::addPolyLine(SkPoint pts[],
    208                                                           char* arg_edge, char** arg_edgePtr) {
    209     auto edge    = (SkAnalyticEdge*) arg_edge;
    210     auto edgePtr = (SkAnalyticEdge**)arg_edgePtr;
    211 
    212     if (edge->setLine(pts[0], pts[1])) {
    213         return is_vertical(edge) && edgePtr > (SkAnalyticEdge**)fEdgeList
    214             ? this->combineVertical(edge, edgePtr[-1])
    215             : kNo_Combine;
    216     }
    217     return SkEdgeBuilder::kPartial_Combine;  // As above.
    218 }
    219 SkEdgeBuilder::Combine SkBezierEdgeBuilder::addPolyLine(SkPoint pts[],
    220                                                         char* arg_edge, char** arg_edgePtr) {
    221     auto edge = (SkLine*)arg_edge;
    222 
    223     if (edge->set(pts)) {
    224         return kNo_Combine;
    225     }
    226     return SkEdgeBuilder::kPartial_Combine;  // As above.
    227 }
    228 
    229 SkRect SkBasicEdgeBuilder::recoverClip(const SkIRect& src) const {
    230     return { SkIntToScalar(src.fLeft   >> fClipShift),
    231              SkIntToScalar(src.fTop    >> fClipShift),
    232              SkIntToScalar(src.fRight  >> fClipShift),
    233              SkIntToScalar(src.fBottom >> fClipShift), };
    234 }
    235 SkRect SkAnalyticEdgeBuilder::recoverClip(const SkIRect& src) const {
    236     return SkRect::Make(src);
    237 }
    238 SkRect SkBezierEdgeBuilder::recoverClip(const SkIRect& src) const {
    239     return SkRect::Make(src);
    240 }
    241 
    242 char* SkBasicEdgeBuilder::allocEdges(size_t n, size_t* size) {
    243     *size = sizeof(SkEdge);
    244     return (char*)fAlloc.makeArrayDefault<SkEdge>(n);
    245 }
    246 char* SkAnalyticEdgeBuilder::allocEdges(size_t n, size_t* size) {
    247     *size = sizeof(SkAnalyticEdge);
    248     return (char*)fAlloc.makeArrayDefault<SkAnalyticEdge>(n);
    249 }
    250 char* SkBezierEdgeBuilder::allocEdges(size_t n, size_t* size) {
    251     *size = sizeof(SkLine);
    252     return (char*)fAlloc.makeArrayDefault<SkLine>(n);
    253 }
    254 
    255 // TODO: maybe get rid of buildPoly() entirely?
    256 int SkEdgeBuilder::buildPoly(const SkPath& path, const SkIRect* iclip, bool canCullToTheRight) {
    257     SkPath::Iter    iter(path, true);
    258     SkPoint         pts[4];
    259     SkPath::Verb    verb;
    260 
    261     size_t maxEdgeCount = path.countPoints();
    262     if (iclip) {
    263         // clipping can turn 1 line into (up to) kMaxClippedLineSegments, since
    264         // we turn portions that are clipped out on the left/right into vertical
    265         // segments.
    266         SkSafeMath safe;
    267         maxEdgeCount = safe.mul(maxEdgeCount, SkLineClipper::kMaxClippedLineSegments);
    268         if (!safe) {
    269             return 0;
    270         }
    271     }
    272 
    273     size_t edgeSize;
    274     char* edge = this->allocEdges(maxEdgeCount, &edgeSize);
    275 
    276     SkDEBUGCODE(char* edgeStart = edge);
    277     char** edgePtr = fAlloc.makeArrayDefault<char*>(maxEdgeCount);
    278     fEdgeList = (void**)edgePtr;
    279 
    280     if (iclip) {
    281         SkRect clip = this->recoverClip(*iclip);
    282 
    283         while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
    284             switch (verb) {
    285                 case SkPath::kMove_Verb:
    286                 case SkPath::kClose_Verb:
    287                     // we ignore these, and just get the whole segment from
    288                     // the corresponding line/quad/cubic verbs
    289                     break;
    290                 case SkPath::kLine_Verb: {
    291                     SkPoint lines[SkLineClipper::kMaxPoints];
    292                     int lineCount = SkLineClipper::ClipLine(pts, clip, lines, canCullToTheRight);
    293                     SkASSERT(lineCount <= SkLineClipper::kMaxClippedLineSegments);
    294                     for (int i = 0; i < lineCount; i++) {
    295                         switch( this->addPolyLine(lines + i, edge, edgePtr) ) {
    296                             case kTotal_Combine:   edgePtr--; break;
    297                             case kPartial_Combine:            break;
    298                             case kNo_Combine: *edgePtr++ = edge;
    299                                                edge += edgeSize;
    300                         }
    301                     }
    302                     break;
    303                 }
    304                 default:
    305                     SkDEBUGFAIL("unexpected verb");
    306                     break;
    307             }
    308         }
    309     } else {
    310         while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
    311             switch (verb) {
    312                 case SkPath::kMove_Verb:
    313                 case SkPath::kClose_Verb:
    314                     // we ignore these, and just get the whole segment from
    315                     // the corresponding line/quad/cubic verbs
    316                     break;
    317                 case SkPath::kLine_Verb: {
    318                     switch( this->addPolyLine(pts, edge, edgePtr) ) {
    319                         case kTotal_Combine:   edgePtr--; break;
    320                         case kPartial_Combine:            break;
    321                         case kNo_Combine: *edgePtr++ = edge;
    322                                            edge += edgeSize;
    323                     }
    324                     break;
    325                 }
    326                 default:
    327                     SkDEBUGFAIL("unexpected verb");
    328                     break;
    329             }
    330         }
    331     }
    332     SkASSERT((size_t)(edge - edgeStart) <= maxEdgeCount * edgeSize);
    333     SkASSERT((size_t)(edgePtr - (char**)fEdgeList) <= maxEdgeCount);
    334     return SkToInt(edgePtr - (char**)fEdgeList);
    335 }
    336 
    337 int SkEdgeBuilder::build(const SkPath& path, const SkIRect* iclip, bool canCullToTheRight) {
    338     SkAutoConicToQuads quadder;
    339     const SkScalar conicTol = SK_Scalar1 / 4;
    340 
    341     SkPath::Iter    iter(path, true);
    342     SkPoint         pts[4];
    343     SkPath::Verb    verb;
    344 
    345     bool is_finite = true;
    346 
    347     if (iclip) {
    348         SkRect clip = this->recoverClip(*iclip);
    349         SkEdgeClipper clipper(canCullToTheRight);
    350 
    351         auto apply_clipper = [this, &clipper, &is_finite] {
    352             SkPoint      pts[4];
    353             SkPath::Verb verb;
    354 
    355             while ((verb = clipper.next(pts)) != SkPath::kDone_Verb) {
    356                 const int count = SkPathPriv::PtsInIter(verb);
    357                 if (!SkScalarsAreFinite(&pts[0].fX, count*2)) {
    358                     is_finite = false;
    359                     return;
    360                 }
    361                 switch (verb) {
    362                     case SkPath::kLine_Verb:  this->addLine (pts); break;
    363                     case SkPath::kQuad_Verb:  this->addQuad (pts); break;
    364                     case SkPath::kCubic_Verb: this->addCubic(pts); break;
    365                     default: break;
    366                 }
    367             }
    368         };
    369 
    370         while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
    371             switch (verb) {
    372                 case SkPath::kMove_Verb:
    373                 case SkPath::kClose_Verb:
    374                     // we ignore these, and just get the whole segment from
    375                     // the corresponding line/quad/cubic verbs
    376                     break;
    377                 case SkPath::kLine_Verb:
    378                     if (clipper.clipLine(pts[0], pts[1], clip)) {
    379                         apply_clipper();
    380                     }
    381                     break;
    382                 case SkPath::kQuad_Verb:
    383                     if (clipper.clipQuad(pts, clip)) {
    384                         apply_clipper();
    385                     }
    386                     break;
    387                 case SkPath::kConic_Verb: {
    388                     const SkPoint* quadPts = quadder.computeQuads(
    389                                           pts, iter.conicWeight(), conicTol);
    390                     for (int i = 0; i < quadder.countQuads(); ++i) {
    391                         if (clipper.clipQuad(quadPts, clip)) {
    392                             apply_clipper();
    393                         }
    394                         quadPts += 2;
    395                     }
    396                 } break;
    397                 case SkPath::kCubic_Verb:
    398                     if (clipper.clipCubic(pts, clip)) {
    399                         apply_clipper();
    400                     }
    401                     break;
    402                 default:
    403                     SkDEBUGFAIL("unexpected verb");
    404                     break;
    405             }
    406         }
    407     } else {
    408         while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
    409             auto handle_quad = [this](const SkPoint pts[3]) {
    410                 SkPoint monoX[5];
    411                 int n = SkChopQuadAtYExtrema(pts, monoX);
    412                 for (int i = 0; i <= n; i++) {
    413                     this->addQuad(&monoX[i * 2]);
    414                 }
    415             };
    416 
    417             switch (verb) {
    418                 case SkPath::kMove_Verb:
    419                 case SkPath::kClose_Verb:
    420                     // we ignore these, and just get the whole segment from
    421                     // the corresponding line/quad/cubic verbs
    422                     break;
    423                 case SkPath::kLine_Verb:
    424                     this->addLine(pts);
    425                     break;
    426                 case SkPath::kQuad_Verb: {
    427                     handle_quad(pts);
    428                     break;
    429                 }
    430                 case SkPath::kConic_Verb: {
    431                     const SkPoint* quadPts = quadder.computeQuads(
    432                                           pts, iter.conicWeight(), conicTol);
    433                     for (int i = 0; i < quadder.countQuads(); ++i) {
    434                         handle_quad(quadPts);
    435                         quadPts += 2;
    436                     }
    437                 } break;
    438                 case SkPath::kCubic_Verb: {
    439                     if (!this->chopCubics()) {
    440                         this->addCubic(pts);
    441                         break;
    442                     }
    443                     SkPoint monoY[10];
    444                     int n = SkChopCubicAtYExtrema(pts, monoY);
    445                     for (int i = 0; i <= n; i++) {
    446                         this->addCubic(&monoY[i * 3]);
    447                     }
    448                     break;
    449                 }
    450                 default:
    451                     SkDEBUGFAIL("unexpected verb");
    452                     break;
    453             }
    454         }
    455     }
    456     fEdgeList = fList.begin();
    457     return is_finite ? fList.count() : 0;
    458 }
    459 
    460 int SkEdgeBuilder::buildEdges(const SkPath& path,
    461                               const SkIRect* shiftedClip) {
    462     // If we're convex, then we need both edges, even if the right edge is past the clip.
    463     const bool canCullToTheRight = !path.isConvex();
    464 
    465     // We can use our buildPoly() optimization if all the segments are lines.
    466     // (Edges are homogenous and stored contiguously in memory, no need for indirection.)
    467     const int count = SkPath::kLine_SegmentMask == path.getSegmentMasks()
    468         ? this->buildPoly(path, shiftedClip, canCullToTheRight)
    469         : this->build    (path, shiftedClip, canCullToTheRight);
    470 
    471     SkASSERT(count >= 0);
    472 
    473     // If we can't cull to the right, we should have count > 1 (or 0),
    474     // unless we're in DAA which doesn't need to chop edges at y extrema.
    475     // For example, a single cubic edge with a valley shape \_/ is fine for DAA.
    476     if (!canCullToTheRight && count == 1) {
    477         SkASSERT(!this->chopCubics());
    478     }
    479 
    480     return count;
    481 }
    482