Home | History | Annotate | Download | only in core
      1 #include "SkLineClipper.h"
      2 
      3 // return X coordinate of intersection with horizontal line at Y
      4 static SkScalar sect_with_horizontal(const SkPoint src[2], SkScalar Y) {
      5     SkScalar dy = src[1].fY - src[0].fY;
      6     if (SkScalarNearlyZero(dy)) {
      7         return SkScalarAve(src[0].fX, src[1].fX);
      8     } else {
      9 #ifdef SK_SCALAR_IS_FLOAT
     10         // need the extra precision so we don't compute a value that exceeds
     11         // our original limits
     12         double X0 = src[0].fX;
     13         double Y0 = src[0].fY;
     14         double X1 = src[1].fX;
     15         double Y1 = src[1].fY;
     16         double result = X0 + ((double)Y - Y0) * (X1 - X0) / (Y1 - Y0);
     17         return (float)result;
     18 #else
     19         return src[0].fX + SkScalarMulDiv(Y - src[0].fY, src[1].fX - src[0].fX,
     20                                           dy);
     21 #endif
     22     }
     23 }
     24 
     25 // return Y coordinate of intersection with vertical line at X
     26 static SkScalar sect_with_vertical(const SkPoint src[2], SkScalar X) {
     27     SkScalar dx = src[1].fX - src[0].fX;
     28     if (SkScalarNearlyZero(dx)) {
     29         return SkScalarAve(src[0].fY, src[1].fY);
     30     } else {
     31 #ifdef SK_SCALAR_IS_FLOAT
     32         // need the extra precision so we don't compute a value that exceeds
     33         // our original limits
     34         double X0 = src[0].fX;
     35         double Y0 = src[0].fY;
     36         double X1 = src[1].fX;
     37         double Y1 = src[1].fY;
     38         double result = Y0 + ((double)X - X0) * (Y1 - Y0) / (X1 - X0);
     39         return (float)result;
     40 #else
     41         return src[0].fY + SkScalarMulDiv(X - src[0].fX, src[1].fY - src[0].fY,
     42                                           dx);
     43 #endif
     44     }
     45 }
     46 
     47 ///////////////////////////////////////////////////////////////////////////////
     48 
     49 static inline bool nestedLT(SkScalar a, SkScalar b, SkScalar dim) {
     50     return a <= b && (a < b || dim > 0);
     51 }
     52 
     53 // returns true if outer contains inner, even if inner is empty.
     54 // note: outer.contains(inner) always returns false if inner is empty.
     55 static inline bool containsNoEmptyCheck(const SkRect& outer,
     56                                         const SkRect& inner) {
     57     return  outer.fLeft <= inner.fLeft && outer.fTop <= inner.fTop &&
     58             outer.fRight >= inner.fRight && outer.fBottom >= inner.fBottom;
     59 }
     60 
     61 bool SkLineClipper::IntersectLine(const SkPoint src[2], const SkRect& clip,
     62                                   SkPoint dst[2]) {
     63     SkRect bounds;
     64 
     65     bounds.set(src, 2);
     66     if (containsNoEmptyCheck(clip, bounds)) {
     67         if (src != dst) {
     68             memcpy(dst, src, 2 * sizeof(SkPoint));
     69         }
     70         return true;
     71     }
     72     // check for no overlap, and only permit coincident edges if the line
     73     // and the edge are colinear
     74     if (nestedLT(bounds.fRight, clip.fLeft, bounds.width()) ||
     75         nestedLT(clip.fRight, bounds.fLeft, bounds.width()) ||
     76         nestedLT(bounds.fBottom, clip.fTop, bounds.height()) ||
     77         nestedLT(clip.fBottom, bounds.fTop, bounds.height())) {
     78         return false;
     79     }
     80 
     81     int index0, index1;
     82 
     83     if (src[0].fY < src[1].fY) {
     84         index0 = 0;
     85         index1 = 1;
     86     } else {
     87         index0 = 1;
     88         index1 = 0;
     89     }
     90 
     91     SkPoint tmp[2];
     92     memcpy(tmp, src, sizeof(tmp));
     93 
     94     // now compute Y intersections
     95     if (tmp[index0].fY < clip.fTop) {
     96         tmp[index0].set(sect_with_horizontal(src, clip.fTop), clip.fTop);
     97     }
     98     if (tmp[index1].fY > clip.fBottom) {
     99         tmp[index1].set(sect_with_horizontal(src, clip.fBottom), clip.fBottom);
    100     }
    101 
    102     if (tmp[0].fX < tmp[1].fX) {
    103         index0 = 0;
    104         index1 = 1;
    105     } else {
    106         index0 = 1;
    107         index1 = 0;
    108     }
    109 
    110     // check for quick-reject in X again, now that we may have been chopped
    111     if ((tmp[index1].fX <= clip.fLeft || tmp[index0].fX >= clip.fRight) &&
    112         tmp[index0].fX < tmp[index1].fX) {
    113         // only reject if we have a non-zero width
    114         return false;
    115     }
    116 
    117     if (tmp[index0].fX < clip.fLeft) {
    118         tmp[index0].set(clip.fLeft, sect_with_vertical(src, clip.fLeft));
    119     }
    120     if (tmp[index1].fX > clip.fRight) {
    121         tmp[index1].set(clip.fRight, sect_with_vertical(src, clip.fRight));
    122     }
    123 #ifdef SK_DEBUG
    124     bounds.set(tmp, 2);
    125     SkASSERT(containsNoEmptyCheck(clip, bounds));
    126 #endif
    127     memcpy(dst, tmp, sizeof(tmp));
    128     return true;
    129 }
    130 
    131 #ifdef SK_DEBUG
    132 // return value between the two limits, where the limits are either ascending
    133 // or descending.
    134 static bool is_between_unsorted(SkScalar value,
    135                                 SkScalar limit0, SkScalar limit1) {
    136     if (limit0 < limit1) {
    137         return limit0 <= value && value <= limit1;
    138     } else {
    139         return limit1 <= value && value <= limit0;
    140     }
    141 }
    142 #endif
    143 
    144 int SkLineClipper::ClipLine(const SkPoint pts[], const SkRect& clip,
    145                             SkPoint lines[]) {
    146     int index0, index1;
    147 
    148     if (pts[0].fY < pts[1].fY) {
    149         index0 = 0;
    150         index1 = 1;
    151     } else {
    152         index0 = 1;
    153         index1 = 0;
    154     }
    155 
    156     // Check if we're completely clipped out in Y (above or below
    157 
    158     if (pts[index1].fY <= clip.fTop) {  // we're above the clip
    159         return 0;
    160     }
    161     if (pts[index0].fY >= clip.fBottom) {  // we're below the clip
    162         return 0;
    163     }
    164 
    165     // Chop in Y to produce a single segment, stored in tmp[0..1]
    166 
    167     SkPoint tmp[2];
    168     memcpy(tmp, pts, sizeof(tmp));
    169 
    170     // now compute intersections
    171     if (pts[index0].fY < clip.fTop) {
    172         tmp[index0].set(sect_with_horizontal(pts, clip.fTop), clip.fTop);
    173         SkASSERT(is_between_unsorted(tmp[index0].fX, pts[0].fX, pts[1].fX));
    174     }
    175     if (tmp[index1].fY > clip.fBottom) {
    176         tmp[index1].set(sect_with_horizontal(pts, clip.fBottom), clip.fBottom);
    177         SkASSERT(is_between_unsorted(tmp[index1].fX, pts[0].fX, pts[1].fX));
    178     }
    179 
    180     // Chop it into 1..3 segments that are wholly within the clip in X.
    181 
    182     // temp storage for up to 3 segments
    183     SkPoint resultStorage[kMaxPoints];
    184     SkPoint* result;    // points to our results, either tmp or resultStorage
    185     int lineCount = 1;
    186     bool reverse;
    187 
    188     if (pts[0].fX < pts[1].fX) {
    189         index0 = 0;
    190         index1 = 1;
    191         reverse = false;
    192     } else {
    193         index0 = 1;
    194         index1 = 0;
    195         reverse = true;
    196     }
    197 
    198     if (tmp[index1].fX <= clip.fLeft) {  // wholly to the left
    199         tmp[0].fX = tmp[1].fX = clip.fLeft;
    200         result = tmp;
    201         reverse = false;
    202     } else if (tmp[index0].fX >= clip.fRight) {    // wholly to the right
    203         tmp[0].fX = tmp[1].fX = clip.fRight;
    204         result = tmp;
    205         reverse = false;
    206     } else {
    207         result = resultStorage;
    208         SkPoint* r = result;
    209 
    210         if (tmp[index0].fX < clip.fLeft) {
    211             r->set(clip.fLeft, tmp[index0].fY);
    212             r += 1;
    213             r->set(clip.fLeft, sect_with_vertical(tmp, clip.fLeft));
    214             SkASSERT(is_between_unsorted(r->fY, tmp[0].fY, tmp[1].fY));
    215         } else {
    216             *r = tmp[index0];
    217         }
    218         r += 1;
    219 
    220         if (tmp[index1].fX > clip.fRight) {
    221             r->set(clip.fRight, sect_with_vertical(tmp, clip.fRight));
    222             SkASSERT(is_between_unsorted(r->fY, tmp[0].fY, tmp[1].fY));
    223             r += 1;
    224             r->set(clip.fRight, tmp[index1].fY);
    225         } else {
    226             *r = tmp[index1];
    227         }
    228 
    229         lineCount = r - result;
    230     }
    231 
    232     // Now copy the results into the caller's lines[] parameter
    233     if (reverse) {
    234         // copy the pts in reverse order to maintain winding order
    235         for (int i = 0; i <= lineCount; i++) {
    236             lines[lineCount - i] = result[i];
    237         }
    238     } else {
    239         memcpy(lines, result, (lineCount + 1) * sizeof(SkPoint));
    240     }
    241     return lineCount;
    242 }
    243 
    244