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 static SkScalar mono_cubic_closestT(const SkScalar src[], SkScalar x) {
    229     SkScalar t = 0.5f;
    230     SkScalar lastT;
    231     SkScalar bestT  SK_INIT_TO_AVOID_WARNING;
    232     SkScalar step = 0.25f;
    233     SkScalar D = src[0];
    234     SkScalar A = src[6] + 3*(src[2] - src[4]) - D;
    235     SkScalar B = 3*(src[4] - src[2] - src[2] + D);
    236     SkScalar C = 3*(src[2] - D);
    237     x -= D;
    238     SkScalar closest = SK_ScalarMax;
    239     do {
    240         SkScalar loc = ((A * t + B) * t + C) * t;
    241         SkScalar dist = SkScalarAbs(loc - x);
    242         if (closest > dist) {
    243             closest = dist;
    244             bestT = t;
    245         }
    246         lastT = t;
    247         t += loc < x ? step : -step;
    248         step *= 0.5f;
    249     } while (closest > 0.25f && lastT != t);
    250     return bestT;
    251 }
    252 
    253 static void chop_mono_cubic_at_y(SkPoint src[4], SkScalar y, SkPoint dst[7]) {
    254     if (SkChopMonoCubicAtY(src, y, dst)) {
    255         return;
    256     }
    257     SkChopCubicAt(src, dst, mono_cubic_closestT(&src->fY, y));
    258 }
    259 
    260 // Modify pts[] in place so that it is clipped in Y to the clip rect
    261 static void chop_cubic_in_Y(SkPoint pts[4], const SkRect& clip) {
    262 
    263     // are we partially above
    264     if (pts[0].fY < clip.fTop) {
    265         SkPoint tmp[7];
    266         chop_mono_cubic_at_y(pts, clip.fTop, tmp);
    267         // tmp[3, 4].fY should all be to the below clip.fTop.
    268         // Since we can't trust the numerics of
    269         // the chopper, we force those conditions now
    270         tmp[3].fY = clip.fTop;
    271         clamp_ge(tmp[4].fY, clip.fTop);
    272 
    273         pts[0] = tmp[3];
    274         pts[1] = tmp[4];
    275         pts[2] = tmp[5];
    276     }
    277 
    278     // are we partially below
    279     if (pts[3].fY > clip.fBottom) {
    280         SkPoint tmp[7];
    281         chop_mono_cubic_at_y(pts, clip.fBottom, tmp);
    282         tmp[3].fY = clip.fBottom;
    283         clamp_le(tmp[2].fY, clip.fBottom);
    284 
    285         pts[1] = tmp[1];
    286         pts[2] = tmp[2];
    287         pts[3] = tmp[3];
    288     }
    289 }
    290 
    291 static void chop_mono_cubic_at_x(SkPoint src[4], SkScalar x, SkPoint dst[7]) {
    292     if (SkChopMonoCubicAtX(src, x, dst)) {
    293         return;
    294     }
    295     SkChopCubicAt(src, dst, mono_cubic_closestT(&src->fX, x));
    296 }
    297 
    298 // srcPts[] must be monotonic in X and Y
    299 void SkEdgeClipper::clipMonoCubic(const SkPoint src[4], const SkRect& clip) {
    300     SkPoint pts[4];
    301     bool reverse = sort_increasing_Y(pts, src, 4);
    302 
    303     // are we completely above or below
    304     if (pts[3].fY <= clip.fTop || pts[0].fY >= clip.fBottom) {
    305         return;
    306     }
    307 
    308     // Now chop so that pts is contained within clip in Y
    309     chop_cubic_in_Y(pts, clip);
    310 
    311     if (pts[0].fX > pts[3].fX) {
    312         SkTSwap<SkPoint>(pts[0], pts[3]);
    313         SkTSwap<SkPoint>(pts[1], pts[2]);
    314         reverse = !reverse;
    315     }
    316 
    317     // Now chop in X has needed, and record the segments
    318 
    319     if (pts[3].fX <= clip.fLeft) {  // wholly to the left
    320         this->appendVLine(clip.fLeft, pts[0].fY, pts[3].fY, reverse);
    321         return;
    322     }
    323     if (pts[0].fX >= clip.fRight) {  // wholly to the right
    324         if (!this->canCullToTheRight()) {
    325             this->appendVLine(clip.fRight, pts[0].fY, pts[3].fY, reverse);
    326         }
    327         return;
    328     }
    329 
    330     // are we partially to the left
    331     if (pts[0].fX < clip.fLeft) {
    332         SkPoint tmp[7];
    333         chop_mono_cubic_at_x(pts, clip.fLeft, tmp);
    334         this->appendVLine(clip.fLeft, tmp[0].fY, tmp[3].fY, reverse);
    335 
    336         // tmp[3, 4].fX should all be to the right of clip.fLeft.
    337         // Since we can't trust the numerics of
    338         // the chopper, we force those conditions now
    339         tmp[3].fX = clip.fLeft;
    340         clamp_ge(tmp[4].fX, clip.fLeft);
    341 
    342         pts[0] = tmp[3];
    343         pts[1] = tmp[4];
    344         pts[2] = tmp[5];
    345     }
    346 
    347     // are we partially to the right
    348     if (pts[3].fX > clip.fRight) {
    349         SkPoint tmp[7];
    350         chop_mono_cubic_at_x(pts, clip.fRight, tmp);
    351         tmp[3].fX = clip.fRight;
    352         clamp_le(tmp[2].fX, clip.fRight);
    353 
    354         this->appendCubic(tmp, reverse);
    355         this->appendVLine(clip.fRight, tmp[3].fY, tmp[6].fY, reverse);
    356     } else {    // wholly inside the clip
    357         this->appendCubic(pts, reverse);
    358     }
    359 }
    360 
    361 static bool quick_reject_in_y(const SkPoint pts[4], const SkRect& clip) {
    362     Sk4s ys(pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY);
    363     Sk4s t(clip.top());
    364     Sk4s b(clip.bottom());
    365 
    366     return (ys < t).allTrue() || (ys > b).allTrue();
    367 }
    368 
    369 bool SkEdgeClipper::clipCubic(const SkPoint srcPts[4], const SkRect& clip) {
    370     fCurrPoint = fPoints;
    371     fCurrVerb = fVerbs;
    372 
    373     if (!quick_reject_in_y(srcPts, clip)) {
    374         SkPoint monoY[10];
    375         int countY = SkChopCubicAtYExtrema(srcPts, monoY);
    376         for (int y = 0; y <= countY; y++) {
    377             SkPoint monoX[10];
    378             int countX = SkChopCubicAtXExtrema(&monoY[y * 3], monoX);
    379             for (int x = 0; x <= countX; x++) {
    380                 this->clipMonoCubic(&monoX[x * 3], clip);
    381                 SkASSERT(fCurrVerb - fVerbs < kMaxVerbs);
    382                 SkASSERT(fCurrPoint - fPoints <= kMaxPoints);
    383             }
    384         }
    385     }
    386 
    387     *fCurrVerb = SkPath::kDone_Verb;
    388     fCurrPoint = fPoints;
    389     fCurrVerb = fVerbs;
    390     return SkPath::kDone_Verb != fVerbs[0];
    391 }
    392 
    393 ///////////////////////////////////////////////////////////////////////////////
    394 
    395 void SkEdgeClipper::appendVLine(SkScalar x, SkScalar y0, SkScalar y1,
    396                                 bool reverse) {
    397     *fCurrVerb++ = SkPath::kLine_Verb;
    398 
    399     if (reverse) {
    400         SkTSwap<SkScalar>(y0, y1);
    401     }
    402     fCurrPoint[0].set(x, y0);
    403     fCurrPoint[1].set(x, y1);
    404     fCurrPoint += 2;
    405 }
    406 
    407 void SkEdgeClipper::appendQuad(const SkPoint pts[3], bool reverse) {
    408     *fCurrVerb++ = SkPath::kQuad_Verb;
    409 
    410     if (reverse) {
    411         fCurrPoint[0] = pts[2];
    412         fCurrPoint[2] = pts[0];
    413     } else {
    414         fCurrPoint[0] = pts[0];
    415         fCurrPoint[2] = pts[2];
    416     }
    417     fCurrPoint[1] = pts[1];
    418     fCurrPoint += 3;
    419 }
    420 
    421 void SkEdgeClipper::appendCubic(const SkPoint pts[4], bool reverse) {
    422     *fCurrVerb++ = SkPath::kCubic_Verb;
    423 
    424     if (reverse) {
    425         for (int i = 0; i < 4; i++) {
    426             fCurrPoint[i] = pts[3 - i];
    427         }
    428     } else {
    429         memcpy(fCurrPoint, pts, 4 * sizeof(SkPoint));
    430     }
    431     fCurrPoint += 4;
    432 }
    433 
    434 SkPath::Verb SkEdgeClipper::next(SkPoint pts[]) {
    435     SkPath::Verb verb = *fCurrVerb;
    436 
    437     switch (verb) {
    438         case SkPath::kLine_Verb:
    439             memcpy(pts, fCurrPoint, 2 * sizeof(SkPoint));
    440             fCurrPoint += 2;
    441             fCurrVerb += 1;
    442             break;
    443         case SkPath::kQuad_Verb:
    444             memcpy(pts, fCurrPoint, 3 * sizeof(SkPoint));
    445             fCurrPoint += 3;
    446             fCurrVerb += 1;
    447             break;
    448         case SkPath::kCubic_Verb:
    449             memcpy(pts, fCurrPoint, 4 * sizeof(SkPoint));
    450             fCurrPoint += 4;
    451             fCurrVerb += 1;
    452             break;
    453         case SkPath::kDone_Verb:
    454             break;
    455         default:
    456             SkDEBUGFAIL("unexpected verb in quadclippper2 iter");
    457             break;
    458     }
    459     return verb;
    460 }
    461 
    462 ///////////////////////////////////////////////////////////////////////////////
    463 
    464 #ifdef SK_DEBUG
    465 static void assert_monotonic(const SkScalar coord[], int count) {
    466     if (coord[0] > coord[(count - 1) * 2]) {
    467         for (int i = 1; i < count; i++) {
    468             SkASSERT(coord[2 * (i - 1)] >= coord[i * 2]);
    469         }
    470     } else if (coord[0] < coord[(count - 1) * 2]) {
    471         for (int i = 1; i < count; i++) {
    472             SkASSERT(coord[2 * (i - 1)] <= coord[i * 2]);
    473         }
    474     } else {
    475         for (int i = 1; i < count; i++) {
    476             SkASSERT(coord[2 * (i - 1)] == coord[i * 2]);
    477         }
    478     }
    479 }
    480 
    481 void sk_assert_monotonic_y(const SkPoint pts[], int count) {
    482     if (count > 1) {
    483         assert_monotonic(&pts[0].fY, count);
    484     }
    485 }
    486 
    487 void sk_assert_monotonic_x(const SkPoint pts[], int count) {
    488     if (count > 1) {
    489         assert_monotonic(&pts[0].fX, count);
    490     }
    491 }
    492 #endif
    493