Home | History | Annotate | Download | only in core
      1 
      2 /*
      3  * Copyright 2009 The Android Open Source Project
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include "SkEdgeClipper.h"
     11 #include "SkGeometry.h"
     12 
     13 static bool quick_reject(const SkRect& bounds, const SkRect& clip) {
     14     return bounds.fTop >= clip.fBottom || bounds.fBottom <= clip.fTop;
     15 }
     16 
     17 static inline void clamp_le(SkScalar& value, SkScalar max) {
     18     if (value > max) {
     19         value = max;
     20     }
     21 }
     22 
     23 static inline void clamp_ge(SkScalar& value, SkScalar min) {
     24     if (value < min) {
     25         value = min;
     26     }
     27 }
     28 
     29 /*  src[] must be monotonic in Y. This routine copies src into dst, and sorts
     30  it to be increasing in Y. If it had to reverse the order of the points,
     31  it returns true, otherwise it returns false
     32  */
     33 static bool sort_increasing_Y(SkPoint dst[], const SkPoint src[], int count) {
     34     // we need the data to be monotonically increasing in Y
     35     if (src[0].fY > src[count - 1].fY) {
     36         for (int i = 0; i < count; i++) {
     37             dst[i] = src[count - i - 1];
     38         }
     39         return true;
     40     } else {
     41         memcpy(dst, src, count * sizeof(SkPoint));
     42         return false;
     43     }
     44 }
     45 
     46 ///////////////////////////////////////////////////////////////////////////////
     47 
     48 static bool chopMonoQuadAt(SkScalar c0, SkScalar c1, SkScalar c2,
     49                            SkScalar target, SkScalar* t) {
     50     /* Solve F(t) = y where F(t) := [0](1-t)^2 + 2[1]t(1-t) + [2]t^2
     51      *  We solve for t, using quadratic equation, hence we have to rearrange
     52      * our cooefficents to look like At^2 + Bt + C
     53      */
     54     SkScalar A = c0 - c1 - c1 + c2;
     55     SkScalar B = 2*(c1 - c0);
     56     SkScalar C = c0 - target;
     57 
     58     SkScalar roots[2];  // we only expect one, but make room for 2 for safety
     59     int count = SkFindUnitQuadRoots(A, B, C, roots);
     60     if (count) {
     61         *t = roots[0];
     62         return true;
     63     }
     64     return false;
     65 }
     66 
     67 static bool chopMonoQuadAtY(SkPoint pts[3], SkScalar y, SkScalar* t) {
     68     return chopMonoQuadAt(pts[0].fY, pts[1].fY, pts[2].fY, y, t);
     69 }
     70 
     71 static bool chopMonoQuadAtX(SkPoint pts[3], SkScalar x, SkScalar* t) {
     72     return chopMonoQuadAt(pts[0].fX, pts[1].fX, pts[2].fX, x, t);
     73 }
     74 
     75 // Modify pts[] in place so that it is clipped in Y to the clip rect
     76 static void chop_quad_in_Y(SkPoint pts[3], const SkRect& clip) {
     77     SkScalar t;
     78     SkPoint tmp[5]; // for SkChopQuadAt
     79 
     80     // are we partially above
     81     if (pts[0].fY < clip.fTop) {
     82         if (chopMonoQuadAtY(pts, clip.fTop, &t)) {
     83             // take the 2nd chopped quad
     84             SkChopQuadAt(pts, tmp, t);
     85             // clamp to clean up imprecise numerics in the chop
     86             tmp[2].fY = clip.fTop;
     87             clamp_ge(tmp[3].fY, clip.fTop);
     88 
     89             pts[0] = tmp[2];
     90             pts[1] = tmp[3];
     91         } else {
     92             // if chopMonoQuadAtY failed, then we may have hit inexact numerics
     93             // so we just clamp against the top
     94             for (int i = 0; i < 3; i++) {
     95                 if (pts[i].fY < clip.fTop) {
     96                     pts[i].fY = clip.fTop;
     97                 }
     98             }
     99         }
    100     }
    101 
    102     // are we partially below
    103     if (pts[2].fY > clip.fBottom) {
    104         if (chopMonoQuadAtY(pts, clip.fBottom, &t)) {
    105             SkChopQuadAt(pts, tmp, t);
    106             // clamp to clean up imprecise numerics in the chop
    107             clamp_le(tmp[1].fY, clip.fBottom);
    108             tmp[2].fY = clip.fBottom;
    109 
    110             pts[1] = tmp[1];
    111             pts[2] = tmp[2];
    112         } else {
    113             // if chopMonoQuadAtY failed, then we may have hit inexact numerics
    114             // so we just clamp against the bottom
    115             for (int i = 0; i < 3; i++) {
    116                 if (pts[i].fY > clip.fBottom) {
    117                     pts[i].fY = clip.fBottom;
    118                 }
    119             }
    120         }
    121     }
    122 }
    123 
    124 // srcPts[] must be monotonic in X and Y
    125 void SkEdgeClipper::clipMonoQuad(const SkPoint srcPts[3], const SkRect& clip) {
    126     SkPoint pts[3];
    127     bool reverse = sort_increasing_Y(pts, srcPts, 3);
    128 
    129     // are we completely above or below
    130     if (pts[2].fY <= clip.fTop || pts[0].fY >= clip.fBottom) {
    131         return;
    132     }
    133 
    134     // Now chop so that pts is contained within clip in Y
    135     chop_quad_in_Y(pts, clip);
    136 
    137     if (pts[0].fX > pts[2].fX) {
    138         SkTSwap<SkPoint>(pts[0], pts[2]);
    139         reverse = !reverse;
    140     }
    141     SkASSERT(pts[0].fX <= pts[1].fX);
    142     SkASSERT(pts[1].fX <= pts[2].fX);
    143 
    144     // Now chop in X has needed, and record the segments
    145 
    146     if (pts[2].fX <= clip.fLeft) {  // wholly to the left
    147         this->appendVLine(clip.fLeft, pts[0].fY, pts[2].fY, reverse);
    148         return;
    149     }
    150     if (pts[0].fX >= clip.fRight) {  // wholly to the right
    151         if (!this->canCullToTheRight()) {
    152             this->appendVLine(clip.fRight, pts[0].fY, pts[2].fY, reverse);
    153         }
    154         return;
    155     }
    156 
    157     SkScalar t;
    158     SkPoint tmp[5]; // for SkChopQuadAt
    159 
    160     // are we partially to the left
    161     if (pts[0].fX < clip.fLeft) {
    162         if (chopMonoQuadAtX(pts, clip.fLeft, &t)) {
    163             SkChopQuadAt(pts, tmp, t);
    164             this->appendVLine(clip.fLeft, tmp[0].fY, tmp[2].fY, reverse);
    165             // clamp to clean up imprecise numerics in the chop
    166             tmp[2].fX = clip.fLeft;
    167             clamp_ge(tmp[3].fX, clip.fLeft);
    168 
    169             pts[0] = tmp[2];
    170             pts[1] = tmp[3];
    171         } else {
    172             // if chopMonoQuadAtY failed, then we may have hit inexact numerics
    173             // so we just clamp against the left
    174             this->appendVLine(clip.fLeft, pts[0].fY, pts[2].fY, reverse);
    175             return;
    176         }
    177     }
    178 
    179     // are we partially to the right
    180     if (pts[2].fX > clip.fRight) {
    181         if (chopMonoQuadAtX(pts, clip.fRight, &t)) {
    182             SkChopQuadAt(pts, tmp, t);
    183             // clamp to clean up imprecise numerics in the chop
    184             clamp_le(tmp[1].fX, clip.fRight);
    185             tmp[2].fX = clip.fRight;
    186 
    187             this->appendQuad(tmp, reverse);
    188             this->appendVLine(clip.fRight, tmp[2].fY, tmp[4].fY, reverse);
    189         } else {
    190             // if chopMonoQuadAtY failed, then we may have hit inexact numerics
    191             // so we just clamp against the right
    192             this->appendVLine(clip.fRight, pts[0].fY, pts[2].fY, reverse);
    193         }
    194     } else {    // wholly inside the clip
    195         this->appendQuad(pts, reverse);
    196     }
    197 }
    198 
    199 bool SkEdgeClipper::clipQuad(const SkPoint srcPts[3], const SkRect& clip) {
    200     fCurrPoint = fPoints;
    201     fCurrVerb = fVerbs;
    202 
    203     SkRect  bounds;
    204     bounds.set(srcPts, 3);
    205 
    206     if (!quick_reject(bounds, clip)) {
    207         SkPoint monoY[5];
    208         int countY = SkChopQuadAtYExtrema(srcPts, monoY);
    209         for (int y = 0; y <= countY; y++) {
    210             SkPoint monoX[5];
    211             int countX = SkChopQuadAtXExtrema(&monoY[y * 2], monoX);
    212             for (int x = 0; x <= countX; x++) {
    213                 this->clipMonoQuad(&monoX[x * 2], clip);
    214                 SkASSERT(fCurrVerb - fVerbs < kMaxVerbs);
    215                 SkASSERT(fCurrPoint - fPoints <= kMaxPoints);
    216             }
    217         }
    218     }
    219 
    220     *fCurrVerb = SkPath::kDone_Verb;
    221     fCurrPoint = fPoints;
    222     fCurrVerb = fVerbs;
    223     return SkPath::kDone_Verb != fVerbs[0];
    224 }
    225 
    226 ///////////////////////////////////////////////////////////////////////////////
    227 
    228 // Modify pts[] in place so that it is clipped in Y to the clip rect
    229 static void chop_cubic_in_Y(SkPoint pts[4], const SkRect& clip) {
    230 
    231     // are we partially above
    232     if (pts[0].fY < clip.fTop) {
    233         SkPoint tmp[7];
    234         if (SkChopMonoCubicAtY(pts, clip.fTop, tmp)) {
    235             // tmp[3, 4].fY should all be to the below clip.fTop.
    236             // Since we can't trust the numerics of
    237             // the chopper, we force those conditions now
    238             tmp[3].fY = clip.fTop;
    239             clamp_ge(tmp[4].fY, clip.fTop);
    240 
    241             pts[0] = tmp[3];
    242             pts[1] = tmp[4];
    243             pts[2] = tmp[5];
    244         } else {
    245             // if chopMonoCubicAtY failed, then we may have hit inexact numerics
    246             // so we just clamp against the top
    247             for (int i = 0; i < 4; i++) {
    248                 clamp_ge(pts[i].fY, clip.fTop);
    249             }
    250         }
    251     }
    252 
    253     // are we partially below
    254     if (pts[3].fY > clip.fBottom) {
    255         SkPoint tmp[7];
    256         if (SkChopMonoCubicAtY(pts, clip.fBottom, tmp)) {
    257             tmp[3].fY = clip.fBottom;
    258             clamp_le(tmp[2].fY, clip.fBottom);
    259 
    260             pts[1] = tmp[1];
    261             pts[2] = tmp[2];
    262             pts[3] = tmp[3];
    263         } else {
    264             // if chopMonoCubicAtY failed, then we may have hit inexact numerics
    265             // so we just clamp against the bottom
    266             for (int i = 0; i < 4; i++) {
    267                 clamp_le(pts[i].fY, clip.fBottom);
    268             }
    269         }
    270     }
    271 }
    272 
    273 // srcPts[] must be monotonic in X and Y
    274 void SkEdgeClipper::clipMonoCubic(const SkPoint src[4], const SkRect& clip) {
    275     SkPoint pts[4];
    276     bool reverse = sort_increasing_Y(pts, src, 4);
    277 
    278     // are we completely above or below
    279     if (pts[3].fY <= clip.fTop || pts[0].fY >= clip.fBottom) {
    280         return;
    281     }
    282 
    283     // Now chop so that pts is contained within clip in Y
    284     chop_cubic_in_Y(pts, clip);
    285 
    286     if (pts[0].fX > pts[3].fX) {
    287         SkTSwap<SkPoint>(pts[0], pts[3]);
    288         SkTSwap<SkPoint>(pts[1], pts[2]);
    289         reverse = !reverse;
    290     }
    291 
    292     // Now chop in X has needed, and record the segments
    293 
    294     if (pts[3].fX <= clip.fLeft) {  // wholly to the left
    295         this->appendVLine(clip.fLeft, pts[0].fY, pts[3].fY, reverse);
    296         return;
    297     }
    298     if (pts[0].fX >= clip.fRight) {  // wholly to the right
    299         if (!this->canCullToTheRight()) {
    300             this->appendVLine(clip.fRight, pts[0].fY, pts[3].fY, reverse);
    301         }
    302         return;
    303     }
    304 
    305     // are we partially to the left
    306     if (pts[0].fX < clip.fLeft) {
    307         SkPoint tmp[7];
    308         if (SkChopMonoCubicAtX(pts, clip.fLeft, tmp)) {
    309             this->appendVLine(clip.fLeft, tmp[0].fY, tmp[3].fY, reverse);
    310 
    311             // tmp[3, 4].fX should all be to the right of clip.fLeft.
    312             // Since we can't trust the numerics of
    313             // the chopper, we force those conditions now
    314             tmp[3].fX = clip.fLeft;
    315             clamp_ge(tmp[4].fX, clip.fLeft);
    316 
    317             pts[0] = tmp[3];
    318             pts[1] = tmp[4];
    319             pts[2] = tmp[5];
    320         } else {
    321             // if chopMonocubicAtY failed, then we may have hit inexact numerics
    322             // so we just clamp against the left
    323             this->appendVLine(clip.fLeft, pts[0].fY, pts[3].fY, reverse);
    324             return;
    325         }
    326     }
    327 
    328     // are we partially to the right
    329     if (pts[3].fX > clip.fRight) {
    330         SkPoint tmp[7];
    331         if (SkChopMonoCubicAtX(pts, clip.fRight, tmp)) {
    332             tmp[3].fX = clip.fRight;
    333             clamp_le(tmp[2].fX, clip.fRight);
    334 
    335             this->appendCubic(tmp, reverse);
    336             this->appendVLine(clip.fRight, tmp[3].fY, tmp[6].fY, reverse);
    337         } else {
    338             // if chopMonoCubicAtX failed, then we may have hit inexact numerics
    339             // so we just clamp against the right
    340             this->appendVLine(clip.fRight, pts[0].fY, pts[3].fY, reverse);
    341         }
    342     } else {    // wholly inside the clip
    343         this->appendCubic(pts, reverse);
    344     }
    345 }
    346 
    347 bool SkEdgeClipper::clipCubic(const SkPoint srcPts[4], const SkRect& clip) {
    348     fCurrPoint = fPoints;
    349     fCurrVerb = fVerbs;
    350 
    351     SkRect  bounds;
    352     bounds.set(srcPts, 4);
    353 
    354     if (!quick_reject(bounds, clip)) {
    355         SkPoint monoY[10];
    356         int countY = SkChopCubicAtYExtrema(srcPts, monoY);
    357         for (int y = 0; y <= countY; y++) {
    358             SkPoint monoX[10];
    359             int countX = SkChopCubicAtXExtrema(&monoY[y * 3], monoX);
    360             for (int x = 0; x <= countX; x++) {
    361                 this->clipMonoCubic(&monoX[x * 3], clip);
    362                 SkASSERT(fCurrVerb - fVerbs < kMaxVerbs);
    363                 SkASSERT(fCurrPoint - fPoints <= kMaxPoints);
    364             }
    365         }
    366     }
    367 
    368     *fCurrVerb = SkPath::kDone_Verb;
    369     fCurrPoint = fPoints;
    370     fCurrVerb = fVerbs;
    371     return SkPath::kDone_Verb != fVerbs[0];
    372 }
    373 
    374 ///////////////////////////////////////////////////////////////////////////////
    375 
    376 void SkEdgeClipper::appendVLine(SkScalar x, SkScalar y0, SkScalar y1,
    377                                 bool reverse) {
    378     *fCurrVerb++ = SkPath::kLine_Verb;
    379 
    380     if (reverse) {
    381         SkTSwap<SkScalar>(y0, y1);
    382     }
    383     fCurrPoint[0].set(x, y0);
    384     fCurrPoint[1].set(x, y1);
    385     fCurrPoint += 2;
    386 }
    387 
    388 void SkEdgeClipper::appendQuad(const SkPoint pts[3], bool reverse) {
    389     *fCurrVerb++ = SkPath::kQuad_Verb;
    390 
    391     if (reverse) {
    392         fCurrPoint[0] = pts[2];
    393         fCurrPoint[2] = pts[0];
    394     } else {
    395         fCurrPoint[0] = pts[0];
    396         fCurrPoint[2] = pts[2];
    397     }
    398     fCurrPoint[1] = pts[1];
    399     fCurrPoint += 3;
    400 }
    401 
    402 void SkEdgeClipper::appendCubic(const SkPoint pts[4], bool reverse) {
    403     *fCurrVerb++ = SkPath::kCubic_Verb;
    404 
    405     if (reverse) {
    406         for (int i = 0; i < 4; i++) {
    407             fCurrPoint[i] = pts[3 - i];
    408         }
    409     } else {
    410         memcpy(fCurrPoint, pts, 4 * sizeof(SkPoint));
    411     }
    412     fCurrPoint += 4;
    413 }
    414 
    415 SkPath::Verb SkEdgeClipper::next(SkPoint pts[]) {
    416     SkPath::Verb verb = *fCurrVerb;
    417 
    418     switch (verb) {
    419         case SkPath::kLine_Verb:
    420             memcpy(pts, fCurrPoint, 2 * sizeof(SkPoint));
    421             fCurrPoint += 2;
    422             fCurrVerb += 1;
    423             break;
    424         case SkPath::kQuad_Verb:
    425             memcpy(pts, fCurrPoint, 3 * sizeof(SkPoint));
    426             fCurrPoint += 3;
    427             fCurrVerb += 1;
    428             break;
    429         case SkPath::kCubic_Verb:
    430             memcpy(pts, fCurrPoint, 4 * sizeof(SkPoint));
    431             fCurrPoint += 4;
    432             fCurrVerb += 1;
    433             break;
    434         case SkPath::kDone_Verb:
    435             break;
    436         default:
    437             SkDEBUGFAIL("unexpected verb in quadclippper2 iter");
    438             break;
    439     }
    440     return verb;
    441 }
    442 
    443 ///////////////////////////////////////////////////////////////////////////////
    444 
    445 #ifdef SK_DEBUG
    446 static void assert_monotonic(const SkScalar coord[], int count) {
    447     if (coord[0] > coord[(count - 1) * 2]) {
    448         for (int i = 1; i < count; i++) {
    449             SkASSERT(coord[2 * (i - 1)] >= coord[i * 2]);
    450         }
    451     } else if (coord[0] < coord[(count - 1) * 2]) {
    452         for (int i = 1; i < count; i++) {
    453             SkASSERT(coord[2 * (i - 1)] <= coord[i * 2]);
    454         }
    455     } else {
    456         for (int i = 1; i < count; i++) {
    457             SkASSERT(coord[2 * (i - 1)] == coord[i * 2]);
    458         }
    459     }
    460 }
    461 
    462 void sk_assert_monotonic_y(const SkPoint pts[], int count) {
    463     if (count > 1) {
    464         assert_monotonic(&pts[0].fY, count);
    465     }
    466 }
    467 
    468 void sk_assert_monotonic_x(const SkPoint pts[], int count) {
    469     if (count > 1) {
    470         assert_monotonic(&pts[0].fX, count);
    471     }
    472 }
    473 #endif
    474