SkLineClipper.cpp revision e28ff55d980d2992618b6b721c848aba96cf759a
1#include "SkLineClipper.h" 2 3// return X coordinate of intersection with horizontal line at Y 4static 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 return src[0].fX + SkScalarMulDiv(Y - src[0].fY, src[1].fX - src[0].fX, 10 dy); 11 } 12} 13 14// return Y coordinate of intersection with vertical line at X 15static SkScalar sect_with_vertical(const SkPoint src[2], SkScalar X) { 16 SkScalar dx = src[1].fX - src[0].fX; 17 if (SkScalarNearlyZero(dx)) { 18 return SkScalarAve(src[0].fY, src[1].fY); 19 } else { 20 return src[0].fY + SkScalarMulDiv(X - src[0].fX, src[1].fY - src[0].fY, 21 dx); 22 } 23} 24 25/////////////////////////////////////////////////////////////////////////////// 26 27bool SkLineClipper::IntersectLine(const SkPoint src[2], const SkRect& clip, 28 SkPoint dst[2]) { 29 SkRect bounds; 30 31 bounds.set(src, 2); 32 if (bounds.fLeft >= clip.fRight || clip.fLeft >= bounds.fRight || 33 bounds.fTop >= clip.fBottom || clip.fTop >= bounds.fBottom) { 34 return false; 35 } 36 if (clip.contains(bounds)) { 37 if (src != dst) { 38 memcpy(dst, src, 2 * sizeof(SkPoint)); 39 } 40 return true; 41 } 42 43 int index0, index1; 44 45 if (src[0].fY < src[1].fY) { 46 index0 = 0; 47 index1 = 1; 48 } else { 49 index0 = 1; 50 index1 = 0; 51 } 52 53 SkPoint tmp[2]; 54 memcpy(tmp, src, sizeof(tmp)); 55 56 // now compute Y intersections 57 if (tmp[index0].fY < clip.fTop) { 58 tmp[index0].set(sect_with_horizontal(src, clip.fTop), clip.fTop); 59 } 60 if (tmp[index1].fY > clip.fBottom) { 61 tmp[index1].set(sect_with_horizontal(src, clip.fBottom), clip.fBottom); 62 } 63 64 if (tmp[0].fX < tmp[1].fX) { 65 index0 = 0; 66 index1 = 1; 67 } else { 68 index0 = 1; 69 index1 = 0; 70 } 71 72 // check for quick-reject in X again, now that we may have been chopped 73 if (tmp[index1].fX <= clip.fLeft || tmp[index0].fX >= clip.fRight) { 74 return false; 75 } 76 77 if (tmp[index0].fX < clip.fLeft) { 78 tmp[index0].set(clip.fLeft, sect_with_vertical(src, clip.fLeft)); 79 } 80 if (tmp[index1].fX > clip.fRight) { 81 tmp[index1].set(clip.fRight, sect_with_vertical(src, clip.fRight)); 82 } 83 memcpy(dst, tmp, sizeof(tmp)); 84 return true; 85} 86 87int SkLineClipper::ClipLine(const SkPoint pts[], const SkRect& clip, 88 SkPoint lines[]) { 89 int index0, index1; 90 91 if (pts[0].fY < pts[1].fY) { 92 index0 = 0; 93 index1 = 1; 94 } else { 95 index0 = 1; 96 index1 = 0; 97 } 98 99 // Check if we're completely clipped out in Y (above or below 100 101 if (pts[index1].fY <= clip.fTop) { // we're above the clip 102 return 0; 103 } 104 if (pts[index0].fY >= clip.fBottom) { // we're below the clip 105 return 0; 106 } 107 108 // Chop in Y to produce a single segment, stored in tmp[0..1] 109 110 SkPoint tmp[2]; 111 memcpy(tmp, pts, sizeof(tmp)); 112 113 // now compute intersections 114 if (pts[index0].fY < clip.fTop) { 115 tmp[index0].set(sect_with_horizontal(pts, clip.fTop), clip.fTop); 116 } 117 if (tmp[index1].fY > clip.fBottom) { 118 tmp[index1].set(sect_with_horizontal(pts, clip.fBottom), clip.fBottom); 119 } 120 121 // Chop it into 1..3 segments that are wholly within the clip in X. 122 123 // temp storage for up to 3 segments 124 SkPoint resultStorage[kMaxPoints]; 125 SkPoint* result; // points to our results, either tmp or resultStorage 126 int lineCount = 1; 127 128 if (pts[0].fX < pts[1].fX) { 129 index0 = 0; 130 index1 = 1; 131 } else { 132 index0 = 1; 133 index1 = 0; 134 } 135 136 if (tmp[index1].fX <= clip.fLeft) { // wholly to the left 137 tmp[0].fX = tmp[1].fX = clip.fLeft; 138 result = tmp; 139 } else if (tmp[index0].fX >= clip.fRight) { // wholly to the right 140 tmp[0].fX = tmp[1].fX = clip.fRight; 141 result = tmp; 142 } else { 143 result = resultStorage; 144 SkPoint* r = result; 145 146 if (tmp[index0].fX < clip.fLeft) { 147 r->set(clip.fLeft, tmp[index0].fY); 148 r += 1; 149 r->set(clip.fLeft, sect_with_vertical(pts, clip.fLeft)); 150 } else { 151 *r = tmp[index0]; 152 } 153 r += 1; 154 155 if (tmp[index1].fX > clip.fRight) { 156 r->set(clip.fRight, sect_with_vertical(pts, clip.fRight)); 157 r += 1; 158 r->set(clip.fRight, tmp[index1].fY); 159 } else { 160 *r = tmp[index1]; 161 } 162 163 lineCount = r - result; 164 } 165 166 // Now copy the results into the caller's lines[] parameter 167 if (0 == index1) { 168 // copy the pts in reverse order to maintain winding order 169 for (int i = 0; i <= lineCount; i++) { 170 lines[lineCount - i] = result[i]; 171 } 172 } else { 173 memcpy(lines, result, (lineCount + 1) * sizeof(SkPoint)); 174 } 175 return lineCount; 176} 177 178