Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2012 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 #include "PathOpsTestCommon.h"
      8 #include "SkPathOpsBounds.h"
      9 #include "SkPathOpsConic.h"
     10 #include "SkPathOpsCubic.h"
     11 #include "SkPathOpsLine.h"
     12 #include "SkPathOpsQuad.h"
     13 #include "SkPathOpsTSect.h"
     14 #include "SkReduceOrder.h"
     15 #include "SkTSort.h"
     16 
     17 #include <utility>
     18 
     19 static double calc_t_div(const SkDCubic& cubic, double precision, double start) {
     20     const double adjust = sqrt(3.) / 36;
     21     SkDCubic sub;
     22     const SkDCubic* cPtr;
     23     if (start == 0) {
     24         cPtr = &cubic;
     25     } else {
     26         // OPTIMIZE: special-case half-split ?
     27         sub = cubic.subDivide(start, 1);
     28         cPtr = &sub;
     29     }
     30     const SkDCubic& c = *cPtr;
     31     double dx = c[3].fX - 3 * (c[2].fX - c[1].fX) - c[0].fX;
     32     double dy = c[3].fY - 3 * (c[2].fY - c[1].fY) - c[0].fY;
     33     double dist = sqrt(dx * dx + dy * dy);
     34     double tDiv3 = precision / (adjust * dist);
     35     double t = SkDCubeRoot(tDiv3);
     36     if (start > 0) {
     37         t = start + (1 - start) * t;
     38     }
     39     return t;
     40 }
     41 
     42 static bool add_simple_ts(const SkDCubic& cubic, double precision, SkTArray<double, true>* ts) {
     43     double tDiv = calc_t_div(cubic, precision, 0);
     44     if (tDiv >= 1) {
     45         return true;
     46     }
     47     if (tDiv >= 0.5) {
     48         ts->push_back(0.5);
     49         return true;
     50     }
     51     return false;
     52 }
     53 
     54 static void addTs(const SkDCubic& cubic, double precision, double start, double end,
     55         SkTArray<double, true>* ts) {
     56     double tDiv = calc_t_div(cubic, precision, 0);
     57     double parts = ceil(1.0 / tDiv);
     58     for (double index = 0; index < parts; ++index) {
     59         double newT = start + (index / parts) * (end - start);
     60         if (newT > 0 && newT < 1) {
     61             ts->push_back(newT);
     62         }
     63     }
     64 }
     65 
     66 static void toQuadraticTs(const SkDCubic* cubic, double precision, SkTArray<double, true>* ts) {
     67     SkReduceOrder reducer;
     68     int order = reducer.reduce(*cubic, SkReduceOrder::kAllow_Quadratics);
     69     if (order < 3) {
     70         return;
     71     }
     72     double inflectT[5];
     73     int inflections = cubic->findInflections(inflectT);
     74     SkASSERT(inflections <= 2);
     75     if (!cubic->endsAreExtremaInXOrY()) {
     76         inflections += cubic->findMaxCurvature(&inflectT[inflections]);
     77         SkASSERT(inflections <= 5);
     78     }
     79     SkTQSort<double>(inflectT, &inflectT[inflections - 1]);
     80     // OPTIMIZATION: is this filtering common enough that it needs to be pulled out into its
     81     // own subroutine?
     82     while (inflections && approximately_less_than_zero(inflectT[0])) {
     83         memmove(inflectT, &inflectT[1], sizeof(inflectT[0]) * --inflections);
     84     }
     85     int start = 0;
     86     int next = 1;
     87     while (next < inflections) {
     88         if (!approximately_equal(inflectT[start], inflectT[next])) {
     89             ++start;
     90         ++next;
     91             continue;
     92         }
     93         memmove(&inflectT[start], &inflectT[next], sizeof(inflectT[0]) * (--inflections - start));
     94     }
     95 
     96     while (inflections && approximately_greater_than_one(inflectT[inflections - 1])) {
     97         --inflections;
     98     }
     99     SkDCubicPair pair;
    100     if (inflections == 1) {
    101         pair = cubic->chopAt(inflectT[0]);
    102         int orderP1 = reducer.reduce(pair.first(), SkReduceOrder::kNo_Quadratics);
    103         if (orderP1 < 2) {
    104             --inflections;
    105         } else {
    106             int orderP2 = reducer.reduce(pair.second(), SkReduceOrder::kNo_Quadratics);
    107             if (orderP2 < 2) {
    108                 --inflections;
    109             }
    110         }
    111     }
    112     if (inflections == 0 && add_simple_ts(*cubic, precision, ts)) {
    113         return;
    114     }
    115     if (inflections == 1) {
    116         pair = cubic->chopAt(inflectT[0]);
    117         addTs(pair.first(), precision, 0, inflectT[0], ts);
    118         addTs(pair.second(), precision, inflectT[0], 1, ts);
    119         return;
    120     }
    121     if (inflections > 1) {
    122         SkDCubic part = cubic->subDivide(0, inflectT[0]);
    123         addTs(part, precision, 0, inflectT[0], ts);
    124         int last = inflections - 1;
    125         for (int idx = 0; idx < last; ++idx) {
    126             part = cubic->subDivide(inflectT[idx], inflectT[idx + 1]);
    127             addTs(part, precision, inflectT[idx], inflectT[idx + 1], ts);
    128         }
    129         part = cubic->subDivide(inflectT[last], 1);
    130         addTs(part, precision, inflectT[last], 1, ts);
    131         return;
    132     }
    133     addTs(*cubic, precision, 0, 1, ts);
    134 }
    135 
    136 void CubicToQuads(const SkDCubic& cubic, double precision, SkTArray<SkDQuad, true>& quads) {
    137     SkTArray<double, true> ts;
    138     toQuadraticTs(&cubic, precision, &ts);
    139     if (ts.count() <= 0) {
    140         SkDQuad quad = cubic.toQuad();
    141         quads.push_back(quad);
    142         return;
    143     }
    144     double tStart = 0;
    145     for (int i1 = 0; i1 <= ts.count(); ++i1) {
    146         const double tEnd = i1 < ts.count() ? ts[i1] : 1;
    147         SkDRect bounds;
    148         bounds.setBounds(cubic);
    149         SkDCubic part = cubic.subDivide(tStart, tEnd);
    150         SkDQuad quad = part.toQuad();
    151         if (quad[1].fX < bounds.fLeft) {
    152             quad[1].fX = bounds.fLeft;
    153         } else if (quad[1].fX > bounds.fRight) {
    154             quad[1].fX = bounds.fRight;
    155         }
    156         if (quad[1].fY < bounds.fTop) {
    157             quad[1].fY = bounds.fTop;
    158         } else if (quad[1].fY > bounds.fBottom) {
    159             quad[1].fY = bounds.fBottom;
    160         }
    161         quads.push_back(quad);
    162         tStart = tEnd;
    163     }
    164 }
    165 
    166 void CubicPathToQuads(const SkPath& cubicPath, SkPath* quadPath) {
    167     quadPath->reset();
    168     SkDCubic cubic;
    169     SkTArray<SkDQuad, true> quads;
    170     SkPath::RawIter iter(cubicPath);
    171     uint8_t verb;
    172     SkPoint pts[4];
    173     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
    174         switch (verb) {
    175             case SkPath::kMove_Verb:
    176                 quadPath->moveTo(pts[0].fX, pts[0].fY);
    177                 continue;
    178             case SkPath::kLine_Verb:
    179                 quadPath->lineTo(pts[1].fX, pts[1].fY);
    180                 break;
    181             case SkPath::kQuad_Verb:
    182                 quadPath->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
    183                 break;
    184             case SkPath::kCubic_Verb:
    185                 quads.reset();
    186                 cubic.set(pts);
    187                 CubicToQuads(cubic, cubic.calcPrecision(), quads);
    188                 for (int index = 0; index < quads.count(); ++index) {
    189                     SkPoint qPts[2] = {
    190                         quads[index][1].asSkPoint(),
    191                         quads[index][2].asSkPoint()
    192                     };
    193                     quadPath->quadTo(qPts[0].fX, qPts[0].fY, qPts[1].fX, qPts[1].fY);
    194                 }
    195                 break;
    196             case SkPath::kClose_Verb:
    197                  quadPath->close();
    198                 break;
    199             default:
    200                 SkDEBUGFAIL("bad verb");
    201                 return;
    202         }
    203     }
    204 }
    205 
    206 void CubicPathToSimple(const SkPath& cubicPath, SkPath* simplePath) {
    207     simplePath->reset();
    208     SkDCubic cubic;
    209     SkPath::RawIter iter(cubicPath);
    210     uint8_t verb;
    211     SkPoint pts[4];
    212     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
    213         switch (verb) {
    214             case SkPath::kMove_Verb:
    215                 simplePath->moveTo(pts[0].fX, pts[0].fY);
    216                 continue;
    217             case SkPath::kLine_Verb:
    218                 simplePath->lineTo(pts[1].fX, pts[1].fY);
    219                 break;
    220             case SkPath::kQuad_Verb:
    221                 simplePath->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
    222                 break;
    223             case SkPath::kCubic_Verb: {
    224                 cubic.set(pts);
    225                 double tInflects[2];
    226                 int inflections = cubic.findInflections(tInflects);
    227                 if (inflections > 1 && tInflects[0] > tInflects[1]) {
    228                     using std::swap;
    229                     swap(tInflects[0], tInflects[1]);
    230                 }
    231                 double lo = 0;
    232                 for (int index = 0; index <= inflections; ++index) {
    233                     double hi = index < inflections ? tInflects[index] : 1;
    234                     SkDCubic part = cubic.subDivide(lo, hi);
    235                     SkPoint cPts[3];
    236                     cPts[0] = part[1].asSkPoint();
    237                     cPts[1] = part[2].asSkPoint();
    238                     cPts[2] = part[3].asSkPoint();
    239                     simplePath->cubicTo(cPts[0].fX, cPts[0].fY, cPts[1].fX, cPts[1].fY,
    240                             cPts[2].fX, cPts[2].fY);
    241                     lo = hi;
    242                 }
    243                 break;
    244             }
    245             case SkPath::kClose_Verb:
    246                  simplePath->close();
    247                 break;
    248             default:
    249                 SkDEBUGFAIL("bad verb");
    250                 return;
    251         }
    252     }
    253 }
    254 
    255 bool ValidBounds(const SkPathOpsBounds& bounds) {
    256     if (SkScalarIsNaN(bounds.fLeft)) {
    257         return false;
    258     }
    259     if (SkScalarIsNaN(bounds.fTop)) {
    260         return false;
    261     }
    262     if (SkScalarIsNaN(bounds.fRight)) {
    263         return false;
    264     }
    265     return !SkScalarIsNaN(bounds.fBottom);
    266 }
    267 
    268 bool ValidConic(const SkDConic& conic) {
    269     for (int index = 0; index < SkDConic::kPointCount; ++index) {
    270         if (!ValidPoint(conic[index])) {
    271             return false;
    272         }
    273     }
    274     if (SkDoubleIsNaN(conic.fWeight)) {
    275         return false;
    276     }
    277     return true;
    278 }
    279 
    280 bool ValidCubic(const SkDCubic& cubic) {
    281     for (int index = 0; index < 4; ++index) {
    282         if (!ValidPoint(cubic[index])) {
    283             return false;
    284         }
    285     }
    286     return true;
    287 }
    288 
    289 bool ValidLine(const SkDLine& line) {
    290     for (int index = 0; index < 2; ++index) {
    291         if (!ValidPoint(line[index])) {
    292             return false;
    293         }
    294     }
    295     return true;
    296 }
    297 
    298 bool ValidPoint(const SkDPoint& pt) {
    299     if (SkDoubleIsNaN(pt.fX)) {
    300         return false;
    301     }
    302     return !SkDoubleIsNaN(pt.fY);
    303 }
    304 
    305 bool ValidPoints(const SkPoint* pts, int count) {
    306     for (int index = 0; index < count; ++index) {
    307         if (SkScalarIsNaN(pts[index].fX)) {
    308             return false;
    309         }
    310         if (SkScalarIsNaN(pts[index].fY)) {
    311             return false;
    312         }
    313     }
    314     return true;
    315 }
    316 
    317 bool ValidQuad(const SkDQuad& quad) {
    318     for (int index = 0; index < 3; ++index) {
    319         if (!ValidPoint(quad[index])) {
    320             return false;
    321         }
    322     }
    323     return true;
    324 }
    325 
    326 bool ValidVector(const SkDVector& v) {
    327     if (SkDoubleIsNaN(v.fX)) {
    328         return false;
    329     }
    330     return !SkDoubleIsNaN(v.fY);
    331 }
    332