1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2011 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/private/SkTo.h" 9cb93a386Sopenharmony_ci#include "src/core/SkLineClipper.h" 10cb93a386Sopenharmony_ci 11cb93a386Sopenharmony_ci#include <utility> 12cb93a386Sopenharmony_ci 13cb93a386Sopenharmony_citemplate <typename T> T pin_unsorted(T value, T limit0, T limit1) { 14cb93a386Sopenharmony_ci if (limit1 < limit0) { 15cb93a386Sopenharmony_ci using std::swap; 16cb93a386Sopenharmony_ci swap(limit0, limit1); 17cb93a386Sopenharmony_ci } 18cb93a386Sopenharmony_ci // now the limits are sorted 19cb93a386Sopenharmony_ci SkASSERT(limit0 <= limit1); 20cb93a386Sopenharmony_ci 21cb93a386Sopenharmony_ci if (value < limit0) { 22cb93a386Sopenharmony_ci value = limit0; 23cb93a386Sopenharmony_ci } else if (value > limit1) { 24cb93a386Sopenharmony_ci value = limit1; 25cb93a386Sopenharmony_ci } 26cb93a386Sopenharmony_ci return value; 27cb93a386Sopenharmony_ci} 28cb93a386Sopenharmony_ci 29cb93a386Sopenharmony_ci// return X coordinate of intersection with horizontal line at Y 30cb93a386Sopenharmony_cistatic SkScalar sect_with_horizontal(const SkPoint src[2], SkScalar Y) { 31cb93a386Sopenharmony_ci SkScalar dy = src[1].fY - src[0].fY; 32cb93a386Sopenharmony_ci if (SkScalarNearlyZero(dy)) { 33cb93a386Sopenharmony_ci return SkScalarAve(src[0].fX, src[1].fX); 34cb93a386Sopenharmony_ci } else { 35cb93a386Sopenharmony_ci // need the extra precision so we don't compute a value that exceeds 36cb93a386Sopenharmony_ci // our original limits 37cb93a386Sopenharmony_ci double X0 = src[0].fX; 38cb93a386Sopenharmony_ci double Y0 = src[0].fY; 39cb93a386Sopenharmony_ci double X1 = src[1].fX; 40cb93a386Sopenharmony_ci double Y1 = src[1].fY; 41cb93a386Sopenharmony_ci double result = X0 + ((double)Y - Y0) * (X1 - X0) / (Y1 - Y0); 42cb93a386Sopenharmony_ci 43cb93a386Sopenharmony_ci // The computed X value might still exceed [X0..X1] due to quantum flux 44cb93a386Sopenharmony_ci // when the doubles were added and subtracted, so we have to pin the 45cb93a386Sopenharmony_ci // answer :( 46cb93a386Sopenharmony_ci return (float)pin_unsorted(result, X0, X1); 47cb93a386Sopenharmony_ci } 48cb93a386Sopenharmony_ci} 49cb93a386Sopenharmony_ci 50cb93a386Sopenharmony_ci// return Y coordinate of intersection with vertical line at X 51cb93a386Sopenharmony_cistatic SkScalar sect_with_vertical(const SkPoint src[2], SkScalar X) { 52cb93a386Sopenharmony_ci SkScalar dx = src[1].fX - src[0].fX; 53cb93a386Sopenharmony_ci if (SkScalarNearlyZero(dx)) { 54cb93a386Sopenharmony_ci return SkScalarAve(src[0].fY, src[1].fY); 55cb93a386Sopenharmony_ci } else { 56cb93a386Sopenharmony_ci // need the extra precision so we don't compute a value that exceeds 57cb93a386Sopenharmony_ci // our original limits 58cb93a386Sopenharmony_ci double X0 = src[0].fX; 59cb93a386Sopenharmony_ci double Y0 = src[0].fY; 60cb93a386Sopenharmony_ci double X1 = src[1].fX; 61cb93a386Sopenharmony_ci double Y1 = src[1].fY; 62cb93a386Sopenharmony_ci double result = Y0 + ((double)X - X0) * (Y1 - Y0) / (X1 - X0); 63cb93a386Sopenharmony_ci return (float)result; 64cb93a386Sopenharmony_ci } 65cb93a386Sopenharmony_ci} 66cb93a386Sopenharmony_ci 67cb93a386Sopenharmony_cistatic SkScalar sect_clamp_with_vertical(const SkPoint src[2], SkScalar x) { 68cb93a386Sopenharmony_ci SkScalar y = sect_with_vertical(src, x); 69cb93a386Sopenharmony_ci // Our caller expects y to be between src[0].fY and src[1].fY (unsorted), but due to the 70cb93a386Sopenharmony_ci // numerics of floats/doubles, we might have computed a value slightly outside of that, 71cb93a386Sopenharmony_ci // so we have to manually clamp afterwards. 72cb93a386Sopenharmony_ci // See skbug.com/7491 73cb93a386Sopenharmony_ci return pin_unsorted(y, src[0].fY, src[1].fY); 74cb93a386Sopenharmony_ci} 75cb93a386Sopenharmony_ci 76cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 77cb93a386Sopenharmony_ci 78cb93a386Sopenharmony_cistatic inline bool nestedLT(SkScalar a, SkScalar b, SkScalar dim) { 79cb93a386Sopenharmony_ci return a <= b && (a < b || dim > 0); 80cb93a386Sopenharmony_ci} 81cb93a386Sopenharmony_ci 82cb93a386Sopenharmony_ci// returns true if outer contains inner, even if inner is empty. 83cb93a386Sopenharmony_ci// note: outer.contains(inner) always returns false if inner is empty. 84cb93a386Sopenharmony_cistatic inline bool containsNoEmptyCheck(const SkRect& outer, 85cb93a386Sopenharmony_ci const SkRect& inner) { 86cb93a386Sopenharmony_ci return outer.fLeft <= inner.fLeft && outer.fTop <= inner.fTop && 87cb93a386Sopenharmony_ci outer.fRight >= inner.fRight && outer.fBottom >= inner.fBottom; 88cb93a386Sopenharmony_ci} 89cb93a386Sopenharmony_ci 90cb93a386Sopenharmony_cibool SkLineClipper::IntersectLine(const SkPoint src[2], const SkRect& clip, 91cb93a386Sopenharmony_ci SkPoint dst[2]) { 92cb93a386Sopenharmony_ci SkRect bounds; 93cb93a386Sopenharmony_ci 94cb93a386Sopenharmony_ci bounds.set(src[0], src[1]); 95cb93a386Sopenharmony_ci if (containsNoEmptyCheck(clip, bounds)) { 96cb93a386Sopenharmony_ci if (src != dst) { 97cb93a386Sopenharmony_ci memcpy(dst, src, 2 * sizeof(SkPoint)); 98cb93a386Sopenharmony_ci } 99cb93a386Sopenharmony_ci return true; 100cb93a386Sopenharmony_ci } 101cb93a386Sopenharmony_ci // check for no overlap, and only permit coincident edges if the line 102cb93a386Sopenharmony_ci // and the edge are colinear 103cb93a386Sopenharmony_ci if (nestedLT(bounds.fRight, clip.fLeft, bounds.width()) || 104cb93a386Sopenharmony_ci nestedLT(clip.fRight, bounds.fLeft, bounds.width()) || 105cb93a386Sopenharmony_ci nestedLT(bounds.fBottom, clip.fTop, bounds.height()) || 106cb93a386Sopenharmony_ci nestedLT(clip.fBottom, bounds.fTop, bounds.height())) { 107cb93a386Sopenharmony_ci return false; 108cb93a386Sopenharmony_ci } 109cb93a386Sopenharmony_ci 110cb93a386Sopenharmony_ci int index0, index1; 111cb93a386Sopenharmony_ci 112cb93a386Sopenharmony_ci if (src[0].fY < src[1].fY) { 113cb93a386Sopenharmony_ci index0 = 0; 114cb93a386Sopenharmony_ci index1 = 1; 115cb93a386Sopenharmony_ci } else { 116cb93a386Sopenharmony_ci index0 = 1; 117cb93a386Sopenharmony_ci index1 = 0; 118cb93a386Sopenharmony_ci } 119cb93a386Sopenharmony_ci 120cb93a386Sopenharmony_ci SkPoint tmp[2]; 121cb93a386Sopenharmony_ci memcpy(tmp, src, sizeof(tmp)); 122cb93a386Sopenharmony_ci 123cb93a386Sopenharmony_ci // now compute Y intersections 124cb93a386Sopenharmony_ci if (tmp[index0].fY < clip.fTop) { 125cb93a386Sopenharmony_ci tmp[index0].set(sect_with_horizontal(src, clip.fTop), clip.fTop); 126cb93a386Sopenharmony_ci } 127cb93a386Sopenharmony_ci if (tmp[index1].fY > clip.fBottom) { 128cb93a386Sopenharmony_ci tmp[index1].set(sect_with_horizontal(src, clip.fBottom), clip.fBottom); 129cb93a386Sopenharmony_ci } 130cb93a386Sopenharmony_ci 131cb93a386Sopenharmony_ci if (tmp[0].fX < tmp[1].fX) { 132cb93a386Sopenharmony_ci index0 = 0; 133cb93a386Sopenharmony_ci index1 = 1; 134cb93a386Sopenharmony_ci } else { 135cb93a386Sopenharmony_ci index0 = 1; 136cb93a386Sopenharmony_ci index1 = 0; 137cb93a386Sopenharmony_ci } 138cb93a386Sopenharmony_ci 139cb93a386Sopenharmony_ci // check for quick-reject in X again, now that we may have been chopped 140cb93a386Sopenharmony_ci if ((tmp[index1].fX <= clip.fLeft || tmp[index0].fX >= clip.fRight)) { 141cb93a386Sopenharmony_ci // usually we will return false, but we don't if the line is vertical and coincident 142cb93a386Sopenharmony_ci // with the clip. 143cb93a386Sopenharmony_ci if (tmp[0].fX != tmp[1].fX || tmp[0].fX < clip.fLeft || tmp[0].fX > clip.fRight) { 144cb93a386Sopenharmony_ci return false; 145cb93a386Sopenharmony_ci } 146cb93a386Sopenharmony_ci } 147cb93a386Sopenharmony_ci 148cb93a386Sopenharmony_ci if (tmp[index0].fX < clip.fLeft) { 149cb93a386Sopenharmony_ci tmp[index0].set(clip.fLeft, sect_with_vertical(src, clip.fLeft)); 150cb93a386Sopenharmony_ci } 151cb93a386Sopenharmony_ci if (tmp[index1].fX > clip.fRight) { 152cb93a386Sopenharmony_ci tmp[index1].set(clip.fRight, sect_with_vertical(src, clip.fRight)); 153cb93a386Sopenharmony_ci } 154cb93a386Sopenharmony_ci#ifdef SK_DEBUG 155cb93a386Sopenharmony_ci bounds.set(tmp[0], tmp[1]); 156cb93a386Sopenharmony_ci SkASSERT(containsNoEmptyCheck(clip, bounds)); 157cb93a386Sopenharmony_ci#endif 158cb93a386Sopenharmony_ci memcpy(dst, tmp, sizeof(tmp)); 159cb93a386Sopenharmony_ci return true; 160cb93a386Sopenharmony_ci} 161cb93a386Sopenharmony_ci 162cb93a386Sopenharmony_ci#ifdef SK_DEBUG 163cb93a386Sopenharmony_ci// return value between the two limits, where the limits are either ascending 164cb93a386Sopenharmony_ci// or descending. 165cb93a386Sopenharmony_cistatic bool is_between_unsorted(SkScalar value, 166cb93a386Sopenharmony_ci SkScalar limit0, SkScalar limit1) { 167cb93a386Sopenharmony_ci if (limit0 < limit1) { 168cb93a386Sopenharmony_ci return limit0 <= value && value <= limit1; 169cb93a386Sopenharmony_ci } else { 170cb93a386Sopenharmony_ci return limit1 <= value && value <= limit0; 171cb93a386Sopenharmony_ci } 172cb93a386Sopenharmony_ci} 173cb93a386Sopenharmony_ci#endif 174cb93a386Sopenharmony_ci 175cb93a386Sopenharmony_ciint SkLineClipper::ClipLine(const SkPoint pts[], const SkRect& clip, SkPoint lines[], 176cb93a386Sopenharmony_ci bool canCullToTheRight) { 177cb93a386Sopenharmony_ci int index0, index1; 178cb93a386Sopenharmony_ci 179cb93a386Sopenharmony_ci if (pts[0].fY < pts[1].fY) { 180cb93a386Sopenharmony_ci index0 = 0; 181cb93a386Sopenharmony_ci index1 = 1; 182cb93a386Sopenharmony_ci } else { 183cb93a386Sopenharmony_ci index0 = 1; 184cb93a386Sopenharmony_ci index1 = 0; 185cb93a386Sopenharmony_ci } 186cb93a386Sopenharmony_ci 187cb93a386Sopenharmony_ci // Check if we're completely clipped out in Y (above or below 188cb93a386Sopenharmony_ci 189cb93a386Sopenharmony_ci if (pts[index1].fY <= clip.fTop) { // we're above the clip 190cb93a386Sopenharmony_ci return 0; 191cb93a386Sopenharmony_ci } 192cb93a386Sopenharmony_ci if (pts[index0].fY >= clip.fBottom) { // we're below the clip 193cb93a386Sopenharmony_ci return 0; 194cb93a386Sopenharmony_ci } 195cb93a386Sopenharmony_ci 196cb93a386Sopenharmony_ci // Chop in Y to produce a single segment, stored in tmp[0..1] 197cb93a386Sopenharmony_ci 198cb93a386Sopenharmony_ci SkPoint tmp[2]; 199cb93a386Sopenharmony_ci memcpy(tmp, pts, sizeof(tmp)); 200cb93a386Sopenharmony_ci 201cb93a386Sopenharmony_ci // now compute intersections 202cb93a386Sopenharmony_ci if (pts[index0].fY < clip.fTop) { 203cb93a386Sopenharmony_ci tmp[index0].set(sect_with_horizontal(pts, clip.fTop), clip.fTop); 204cb93a386Sopenharmony_ci SkASSERT(is_between_unsorted(tmp[index0].fX, pts[0].fX, pts[1].fX)); 205cb93a386Sopenharmony_ci } 206cb93a386Sopenharmony_ci if (tmp[index1].fY > clip.fBottom) { 207cb93a386Sopenharmony_ci tmp[index1].set(sect_with_horizontal(pts, clip.fBottom), clip.fBottom); 208cb93a386Sopenharmony_ci SkASSERT(is_between_unsorted(tmp[index1].fX, pts[0].fX, pts[1].fX)); 209cb93a386Sopenharmony_ci } 210cb93a386Sopenharmony_ci 211cb93a386Sopenharmony_ci // Chop it into 1..3 segments that are wholly within the clip in X. 212cb93a386Sopenharmony_ci 213cb93a386Sopenharmony_ci // temp storage for up to 3 segments 214cb93a386Sopenharmony_ci SkPoint resultStorage[kMaxPoints]; 215cb93a386Sopenharmony_ci SkPoint* result; // points to our results, either tmp or resultStorage 216cb93a386Sopenharmony_ci int lineCount = 1; 217cb93a386Sopenharmony_ci bool reverse; 218cb93a386Sopenharmony_ci 219cb93a386Sopenharmony_ci if (pts[0].fX < pts[1].fX) { 220cb93a386Sopenharmony_ci index0 = 0; 221cb93a386Sopenharmony_ci index1 = 1; 222cb93a386Sopenharmony_ci reverse = false; 223cb93a386Sopenharmony_ci } else { 224cb93a386Sopenharmony_ci index0 = 1; 225cb93a386Sopenharmony_ci index1 = 0; 226cb93a386Sopenharmony_ci reverse = true; 227cb93a386Sopenharmony_ci } 228cb93a386Sopenharmony_ci 229cb93a386Sopenharmony_ci if (tmp[index1].fX <= clip.fLeft) { // wholly to the left 230cb93a386Sopenharmony_ci tmp[0].fX = tmp[1].fX = clip.fLeft; 231cb93a386Sopenharmony_ci result = tmp; 232cb93a386Sopenharmony_ci reverse = false; 233cb93a386Sopenharmony_ci } else if (tmp[index0].fX >= clip.fRight) { // wholly to the right 234cb93a386Sopenharmony_ci if (canCullToTheRight) { 235cb93a386Sopenharmony_ci return 0; 236cb93a386Sopenharmony_ci } 237cb93a386Sopenharmony_ci tmp[0].fX = tmp[1].fX = clip.fRight; 238cb93a386Sopenharmony_ci result = tmp; 239cb93a386Sopenharmony_ci reverse = false; 240cb93a386Sopenharmony_ci } else { 241cb93a386Sopenharmony_ci result = resultStorage; 242cb93a386Sopenharmony_ci SkPoint* r = result; 243cb93a386Sopenharmony_ci 244cb93a386Sopenharmony_ci if (tmp[index0].fX < clip.fLeft) { 245cb93a386Sopenharmony_ci r->set(clip.fLeft, tmp[index0].fY); 246cb93a386Sopenharmony_ci r += 1; 247cb93a386Sopenharmony_ci r->set(clip.fLeft, sect_clamp_with_vertical(tmp, clip.fLeft)); 248cb93a386Sopenharmony_ci SkASSERT(is_between_unsorted(r->fY, tmp[0].fY, tmp[1].fY)); 249cb93a386Sopenharmony_ci } else { 250cb93a386Sopenharmony_ci *r = tmp[index0]; 251cb93a386Sopenharmony_ci } 252cb93a386Sopenharmony_ci r += 1; 253cb93a386Sopenharmony_ci 254cb93a386Sopenharmony_ci if (tmp[index1].fX > clip.fRight) { 255cb93a386Sopenharmony_ci r->set(clip.fRight, sect_clamp_with_vertical(tmp, clip.fRight)); 256cb93a386Sopenharmony_ci SkASSERT(is_between_unsorted(r->fY, tmp[0].fY, tmp[1].fY)); 257cb93a386Sopenharmony_ci r += 1; 258cb93a386Sopenharmony_ci r->set(clip.fRight, tmp[index1].fY); 259cb93a386Sopenharmony_ci } else { 260cb93a386Sopenharmony_ci *r = tmp[index1]; 261cb93a386Sopenharmony_ci } 262cb93a386Sopenharmony_ci 263cb93a386Sopenharmony_ci lineCount = SkToInt(r - result); 264cb93a386Sopenharmony_ci } 265cb93a386Sopenharmony_ci 266cb93a386Sopenharmony_ci // Now copy the results into the caller's lines[] parameter 267cb93a386Sopenharmony_ci if (reverse) { 268cb93a386Sopenharmony_ci // copy the pts in reverse order to maintain winding order 269cb93a386Sopenharmony_ci for (int i = 0; i <= lineCount; i++) { 270cb93a386Sopenharmony_ci lines[lineCount - i] = result[i]; 271cb93a386Sopenharmony_ci } 272cb93a386Sopenharmony_ci } else { 273cb93a386Sopenharmony_ci memcpy(lines, result, (lineCount + 1) * sizeof(SkPoint)); 274cb93a386Sopenharmony_ci } 275cb93a386Sopenharmony_ci return lineCount; 276cb93a386Sopenharmony_ci} 277