|
1 |
|
2 /* |
|
3 * Copyright 2006 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 #ifndef SkRect_DEFINED |
|
11 #define SkRect_DEFINED |
|
12 |
|
13 #include "SkPoint.h" |
|
14 #include "SkSize.h" |
|
15 |
|
16 /** \struct SkIRect |
|
17 |
|
18 SkIRect holds four 32 bit integer coordinates for a rectangle |
|
19 */ |
|
20 struct SK_API SkIRect { |
|
21 int32_t fLeft, fTop, fRight, fBottom; |
|
22 |
|
23 static SkIRect SK_WARN_UNUSED_RESULT MakeEmpty() { |
|
24 SkIRect r; |
|
25 r.setEmpty(); |
|
26 return r; |
|
27 } |
|
28 |
|
29 static SkIRect SK_WARN_UNUSED_RESULT MakeLargest() { |
|
30 SkIRect r; |
|
31 r.setLargest(); |
|
32 return r; |
|
33 } |
|
34 |
|
35 static SkIRect SK_WARN_UNUSED_RESULT MakeWH(int32_t w, int32_t h) { |
|
36 SkIRect r; |
|
37 r.set(0, 0, w, h); |
|
38 return r; |
|
39 } |
|
40 |
|
41 static SkIRect SK_WARN_UNUSED_RESULT MakeSize(const SkISize& size) { |
|
42 SkIRect r; |
|
43 r.set(0, 0, size.width(), size.height()); |
|
44 return r; |
|
45 } |
|
46 |
|
47 static SkIRect SK_WARN_UNUSED_RESULT MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b) { |
|
48 SkIRect rect; |
|
49 rect.set(l, t, r, b); |
|
50 return rect; |
|
51 } |
|
52 |
|
53 static SkIRect SK_WARN_UNUSED_RESULT MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h) { |
|
54 SkIRect r; |
|
55 r.set(x, y, x + w, y + h); |
|
56 return r; |
|
57 } |
|
58 |
|
59 int left() const { return fLeft; } |
|
60 int top() const { return fTop; } |
|
61 int right() const { return fRight; } |
|
62 int bottom() const { return fBottom; } |
|
63 |
|
64 /** return the left edge of the rect */ |
|
65 int x() const { return fLeft; } |
|
66 /** return the top edge of the rect */ |
|
67 int y() const { return fTop; } |
|
68 /** |
|
69 * Returns the rectangle's width. This does not check for a valid rect |
|
70 * (i.e. left <= right) so the result may be negative. |
|
71 */ |
|
72 int width() const { return fRight - fLeft; } |
|
73 |
|
74 /** |
|
75 * Returns the rectangle's height. This does not check for a valid rect |
|
76 * (i.e. top <= bottom) so the result may be negative. |
|
77 */ |
|
78 int height() const { return fBottom - fTop; } |
|
79 |
|
80 /** |
|
81 * Since the center of an integer rect may fall on a factional value, this |
|
82 * method is defined to return (right + left) >> 1. |
|
83 * |
|
84 * This is a specific "truncation" of the average, which is different than |
|
85 * (right + left) / 2 when the sum is negative. |
|
86 */ |
|
87 int centerX() const { return (fRight + fLeft) >> 1; } |
|
88 |
|
89 /** |
|
90 * Since the center of an integer rect may fall on a factional value, this |
|
91 * method is defined to return (bottom + top) >> 1 |
|
92 * |
|
93 * This is a specific "truncation" of the average, which is different than |
|
94 * (bottom + top) / 2 when the sum is negative. |
|
95 */ |
|
96 int centerY() const { return (fBottom + fTop) >> 1; } |
|
97 |
|
98 /** |
|
99 * Return true if the rectangle's width or height are <= 0 |
|
100 */ |
|
101 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } |
|
102 |
|
103 bool isLargest() const { return SK_MinS32 == fLeft && |
|
104 SK_MinS32 == fTop && |
|
105 SK_MaxS32 == fRight && |
|
106 SK_MaxS32 == fBottom; } |
|
107 |
|
108 friend bool operator==(const SkIRect& a, const SkIRect& b) { |
|
109 return !memcmp(&a, &b, sizeof(a)); |
|
110 } |
|
111 |
|
112 friend bool operator!=(const SkIRect& a, const SkIRect& b) { |
|
113 return !(a == b); |
|
114 } |
|
115 |
|
116 bool is16Bit() const { |
|
117 return SkIsS16(fLeft) && SkIsS16(fTop) && |
|
118 SkIsS16(fRight) && SkIsS16(fBottom); |
|
119 } |
|
120 |
|
121 /** Set the rectangle to (0,0,0,0) |
|
122 */ |
|
123 void setEmpty() { memset(this, 0, sizeof(*this)); } |
|
124 |
|
125 void set(int32_t left, int32_t top, int32_t right, int32_t bottom) { |
|
126 fLeft = left; |
|
127 fTop = top; |
|
128 fRight = right; |
|
129 fBottom = bottom; |
|
130 } |
|
131 // alias for set(l, t, r, b) |
|
132 void setLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) { |
|
133 this->set(left, top, right, bottom); |
|
134 } |
|
135 |
|
136 void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height) { |
|
137 fLeft = x; |
|
138 fTop = y; |
|
139 fRight = x + width; |
|
140 fBottom = y + height; |
|
141 } |
|
142 |
|
143 /** |
|
144 * Make the largest representable rectangle |
|
145 */ |
|
146 void setLargest() { |
|
147 fLeft = fTop = SK_MinS32; |
|
148 fRight = fBottom = SK_MaxS32; |
|
149 } |
|
150 |
|
151 /** |
|
152 * Make the largest representable rectangle, but inverted (e.g. fLeft will |
|
153 * be max 32bit and right will be min 32bit). |
|
154 */ |
|
155 void setLargestInverted() { |
|
156 fLeft = fTop = SK_MaxS32; |
|
157 fRight = fBottom = SK_MinS32; |
|
158 } |
|
159 |
|
160 /** Offset set the rectangle by adding dx to its left and right, |
|
161 and adding dy to its top and bottom. |
|
162 */ |
|
163 void offset(int32_t dx, int32_t dy) { |
|
164 fLeft += dx; |
|
165 fTop += dy; |
|
166 fRight += dx; |
|
167 fBottom += dy; |
|
168 } |
|
169 |
|
170 void offset(const SkIPoint& delta) { |
|
171 this->offset(delta.fX, delta.fY); |
|
172 } |
|
173 |
|
174 /** |
|
175 * Offset this rect such its new x() and y() will equal newX and newY. |
|
176 */ |
|
177 void offsetTo(int32_t newX, int32_t newY) { |
|
178 fRight += newX - fLeft; |
|
179 fBottom += newY - fTop; |
|
180 fLeft = newX; |
|
181 fTop = newY; |
|
182 } |
|
183 |
|
184 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are moved inwards, |
|
185 making the rectangle narrower. If dx is negative, then the sides are moved outwards, |
|
186 making the rectangle wider. The same holds true for dy and the top and bottom. |
|
187 */ |
|
188 void inset(int32_t dx, int32_t dy) { |
|
189 fLeft += dx; |
|
190 fTop += dy; |
|
191 fRight -= dx; |
|
192 fBottom -= dy; |
|
193 } |
|
194 |
|
195 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are |
|
196 moved outwards, making the rectangle wider. If dx is negative, then the |
|
197 sides are moved inwards, making the rectangle narrower. The same holds |
|
198 true for dy and the top and bottom. |
|
199 */ |
|
200 void outset(int32_t dx, int32_t dy) { this->inset(-dx, -dy); } |
|
201 |
|
202 bool quickReject(int l, int t, int r, int b) const { |
|
203 return l >= fRight || fLeft >= r || t >= fBottom || fTop >= b; |
|
204 } |
|
205 |
|
206 /** Returns true if (x,y) is inside the rectangle and the rectangle is not |
|
207 empty. The left and top are considered to be inside, while the right |
|
208 and bottom are not. Thus for the rectangle (0, 0, 5, 10), the |
|
209 points (0,0) and (0,9) are inside, while (-1,0) and (5,9) are not. |
|
210 */ |
|
211 bool contains(int32_t x, int32_t y) const { |
|
212 return (unsigned)(x - fLeft) < (unsigned)(fRight - fLeft) && |
|
213 (unsigned)(y - fTop) < (unsigned)(fBottom - fTop); |
|
214 } |
|
215 |
|
216 /** Returns true if the 4 specified sides of a rectangle are inside or equal to this rectangle. |
|
217 If either rectangle is empty, contains() returns false. |
|
218 */ |
|
219 bool contains(int32_t left, int32_t top, int32_t right, int32_t bottom) const { |
|
220 return left < right && top < bottom && !this->isEmpty() && // check for empties |
|
221 fLeft <= left && fTop <= top && |
|
222 fRight >= right && fBottom >= bottom; |
|
223 } |
|
224 |
|
225 /** Returns true if the specified rectangle r is inside or equal to this rectangle. |
|
226 */ |
|
227 bool contains(const SkIRect& r) const { |
|
228 return !r.isEmpty() && !this->isEmpty() && // check for empties |
|
229 fLeft <= r.fLeft && fTop <= r.fTop && |
|
230 fRight >= r.fRight && fBottom >= r.fBottom; |
|
231 } |
|
232 |
|
233 /** Return true if this rectangle contains the specified rectangle. |
|
234 For speed, this method does not check if either this or the specified |
|
235 rectangles are empty, and if either is, its return value is undefined. |
|
236 In the debugging build however, we assert that both this and the |
|
237 specified rectangles are non-empty. |
|
238 */ |
|
239 bool containsNoEmptyCheck(int32_t left, int32_t top, |
|
240 int32_t right, int32_t bottom) const { |
|
241 SkASSERT(fLeft < fRight && fTop < fBottom); |
|
242 SkASSERT(left < right && top < bottom); |
|
243 |
|
244 return fLeft <= left && fTop <= top && |
|
245 fRight >= right && fBottom >= bottom; |
|
246 } |
|
247 |
|
248 bool containsNoEmptyCheck(const SkIRect& r) const { |
|
249 return containsNoEmptyCheck(r.fLeft, r.fTop, r.fRight, r.fBottom); |
|
250 } |
|
251 |
|
252 /** If r intersects this rectangle, return true and set this rectangle to that |
|
253 intersection, otherwise return false and do not change this rectangle. |
|
254 If either rectangle is empty, do nothing and return false. |
|
255 */ |
|
256 bool intersect(const SkIRect& r) { |
|
257 SkASSERT(&r); |
|
258 return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom); |
|
259 } |
|
260 |
|
261 /** If rectangles a and b intersect, return true and set this rectangle to |
|
262 that intersection, otherwise return false and do not change this |
|
263 rectangle. If either rectangle is empty, do nothing and return false. |
|
264 */ |
|
265 bool intersect(const SkIRect& a, const SkIRect& b) { |
|
266 SkASSERT(&a && &b); |
|
267 |
|
268 if (!a.isEmpty() && !b.isEmpty() && |
|
269 a.fLeft < b.fRight && b.fLeft < a.fRight && |
|
270 a.fTop < b.fBottom && b.fTop < a.fBottom) { |
|
271 fLeft = SkMax32(a.fLeft, b.fLeft); |
|
272 fTop = SkMax32(a.fTop, b.fTop); |
|
273 fRight = SkMin32(a.fRight, b.fRight); |
|
274 fBottom = SkMin32(a.fBottom, b.fBottom); |
|
275 return true; |
|
276 } |
|
277 return false; |
|
278 } |
|
279 |
|
280 /** If rectangles a and b intersect, return true and set this rectangle to |
|
281 that intersection, otherwise return false and do not change this |
|
282 rectangle. For speed, no check to see if a or b are empty is performed. |
|
283 If either is, then the return result is undefined. In the debug build, |
|
284 we assert that both rectangles are non-empty. |
|
285 */ |
|
286 bool intersectNoEmptyCheck(const SkIRect& a, const SkIRect& b) { |
|
287 SkASSERT(&a && &b); |
|
288 SkASSERT(!a.isEmpty() && !b.isEmpty()); |
|
289 |
|
290 if (a.fLeft < b.fRight && b.fLeft < a.fRight && |
|
291 a.fTop < b.fBottom && b.fTop < a.fBottom) { |
|
292 fLeft = SkMax32(a.fLeft, b.fLeft); |
|
293 fTop = SkMax32(a.fTop, b.fTop); |
|
294 fRight = SkMin32(a.fRight, b.fRight); |
|
295 fBottom = SkMin32(a.fBottom, b.fBottom); |
|
296 return true; |
|
297 } |
|
298 return false; |
|
299 } |
|
300 |
|
301 /** If the rectangle specified by left,top,right,bottom intersects this rectangle, |
|
302 return true and set this rectangle to that intersection, |
|
303 otherwise return false and do not change this rectangle. |
|
304 If either rectangle is empty, do nothing and return false. |
|
305 */ |
|
306 bool intersect(int32_t left, int32_t top, int32_t right, int32_t bottom) { |
|
307 if (left < right && top < bottom && !this->isEmpty() && |
|
308 fLeft < right && left < fRight && fTop < bottom && top < fBottom) { |
|
309 if (fLeft < left) fLeft = left; |
|
310 if (fTop < top) fTop = top; |
|
311 if (fRight > right) fRight = right; |
|
312 if (fBottom > bottom) fBottom = bottom; |
|
313 return true; |
|
314 } |
|
315 return false; |
|
316 } |
|
317 |
|
318 /** Returns true if a and b are not empty, and they intersect |
|
319 */ |
|
320 static bool Intersects(const SkIRect& a, const SkIRect& b) { |
|
321 return !a.isEmpty() && !b.isEmpty() && // check for empties |
|
322 a.fLeft < b.fRight && b.fLeft < a.fRight && |
|
323 a.fTop < b.fBottom && b.fTop < a.fBottom; |
|
324 } |
|
325 |
|
326 /** |
|
327 * Returns true if a and b intersect. debug-asserts that neither are empty. |
|
328 */ |
|
329 static bool IntersectsNoEmptyCheck(const SkIRect& a, const SkIRect& b) { |
|
330 SkASSERT(!a.isEmpty()); |
|
331 SkASSERT(!b.isEmpty()); |
|
332 return a.fLeft < b.fRight && b.fLeft < a.fRight && |
|
333 a.fTop < b.fBottom && b.fTop < a.fBottom; |
|
334 } |
|
335 |
|
336 /** Update this rectangle to enclose itself and the specified rectangle. |
|
337 If this rectangle is empty, just set it to the specified rectangle. If the specified |
|
338 rectangle is empty, do nothing. |
|
339 */ |
|
340 void join(int32_t left, int32_t top, int32_t right, int32_t bottom); |
|
341 |
|
342 /** Update this rectangle to enclose itself and the specified rectangle. |
|
343 If this rectangle is empty, just set it to the specified rectangle. If the specified |
|
344 rectangle is empty, do nothing. |
|
345 */ |
|
346 void join(const SkIRect& r) { |
|
347 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom); |
|
348 } |
|
349 |
|
350 /** Swap top/bottom or left/right if there are flipped. |
|
351 This can be called if the edges are computed separately, |
|
352 and may have crossed over each other. |
|
353 When this returns, left <= right && top <= bottom |
|
354 */ |
|
355 void sort(); |
|
356 |
|
357 static const SkIRect& SK_WARN_UNUSED_RESULT EmptyIRect() { |
|
358 static const SkIRect gEmpty = { 0, 0, 0, 0 }; |
|
359 return gEmpty; |
|
360 } |
|
361 }; |
|
362 |
|
363 /** \struct SkRect |
|
364 */ |
|
365 struct SK_API SkRect { |
|
366 SkScalar fLeft, fTop, fRight, fBottom; |
|
367 |
|
368 static SkRect SK_WARN_UNUSED_RESULT MakeEmpty() { |
|
369 SkRect r; |
|
370 r.setEmpty(); |
|
371 return r; |
|
372 } |
|
373 |
|
374 static SkRect SK_WARN_UNUSED_RESULT MakeLargest() { |
|
375 SkRect r; |
|
376 r.setLargest(); |
|
377 return r; |
|
378 } |
|
379 |
|
380 static SkRect SK_WARN_UNUSED_RESULT MakeWH(SkScalar w, SkScalar h) { |
|
381 SkRect r; |
|
382 r.set(0, 0, w, h); |
|
383 return r; |
|
384 } |
|
385 |
|
386 static SkRect SK_WARN_UNUSED_RESULT MakeSize(const SkSize& size) { |
|
387 SkRect r; |
|
388 r.set(0, 0, size.width(), size.height()); |
|
389 return r; |
|
390 } |
|
391 |
|
392 static SkRect SK_WARN_UNUSED_RESULT MakeLTRB(SkScalar l, SkScalar t, SkScalar r, SkScalar b) { |
|
393 SkRect rect; |
|
394 rect.set(l, t, r, b); |
|
395 return rect; |
|
396 } |
|
397 |
|
398 static SkRect SK_WARN_UNUSED_RESULT MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) { |
|
399 SkRect r; |
|
400 r.set(x, y, x + w, y + h); |
|
401 return r; |
|
402 } |
|
403 |
|
404 SK_ATTR_DEPRECATED("use Make()") |
|
405 static SkRect SK_WARN_UNUSED_RESULT MakeFromIRect(const SkIRect& irect) { |
|
406 SkRect r; |
|
407 r.set(SkIntToScalar(irect.fLeft), |
|
408 SkIntToScalar(irect.fTop), |
|
409 SkIntToScalar(irect.fRight), |
|
410 SkIntToScalar(irect.fBottom)); |
|
411 return r; |
|
412 } |
|
413 |
|
414 static SkRect SK_WARN_UNUSED_RESULT Make(const SkIRect& irect) { |
|
415 SkRect r; |
|
416 r.set(SkIntToScalar(irect.fLeft), |
|
417 SkIntToScalar(irect.fTop), |
|
418 SkIntToScalar(irect.fRight), |
|
419 SkIntToScalar(irect.fBottom)); |
|
420 return r; |
|
421 } |
|
422 |
|
423 /** |
|
424 * Return true if the rectangle's width or height are <= 0 |
|
425 */ |
|
426 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } |
|
427 |
|
428 bool isLargest() const { return SK_ScalarMin == fLeft && |
|
429 SK_ScalarMin == fTop && |
|
430 SK_ScalarMax == fRight && |
|
431 SK_ScalarMax == fBottom; } |
|
432 |
|
433 /** |
|
434 * Returns true iff all values in the rect are finite. If any are |
|
435 * infinite or NaN (or SK_FixedNaN when SkScalar is fixed) then this |
|
436 * returns false. |
|
437 */ |
|
438 bool isFinite() const { |
|
439 float accum = 0; |
|
440 accum *= fLeft; |
|
441 accum *= fTop; |
|
442 accum *= fRight; |
|
443 accum *= fBottom; |
|
444 |
|
445 // accum is either NaN or it is finite (zero). |
|
446 SkASSERT(0 == accum || !(accum == accum)); |
|
447 |
|
448 // value==value will be true iff value is not NaN |
|
449 // TODO: is it faster to say !accum or accum==accum? |
|
450 return accum == accum; |
|
451 } |
|
452 |
|
453 SkScalar x() const { return fLeft; } |
|
454 SkScalar y() const { return fTop; } |
|
455 SkScalar left() const { return fLeft; } |
|
456 SkScalar top() const { return fTop; } |
|
457 SkScalar right() const { return fRight; } |
|
458 SkScalar bottom() const { return fBottom; } |
|
459 SkScalar width() const { return fRight - fLeft; } |
|
460 SkScalar height() const { return fBottom - fTop; } |
|
461 SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); } |
|
462 SkScalar centerY() const { return SkScalarHalf(fTop + fBottom); } |
|
463 |
|
464 friend bool operator==(const SkRect& a, const SkRect& b) { |
|
465 return SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4); |
|
466 } |
|
467 |
|
468 friend bool operator!=(const SkRect& a, const SkRect& b) { |
|
469 return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4); |
|
470 } |
|
471 |
|
472 /** return the 4 points that enclose the rectangle (top-left, top-right, bottom-right, |
|
473 bottom-left). TODO: Consider adding param to control whether quad is CW or CCW. |
|
474 */ |
|
475 void toQuad(SkPoint quad[4]) const; |
|
476 |
|
477 /** Set this rectangle to the empty rectangle (0,0,0,0) |
|
478 */ |
|
479 void setEmpty() { memset(this, 0, sizeof(*this)); } |
|
480 |
|
481 void set(const SkIRect& src) { |
|
482 fLeft = SkIntToScalar(src.fLeft); |
|
483 fTop = SkIntToScalar(src.fTop); |
|
484 fRight = SkIntToScalar(src.fRight); |
|
485 fBottom = SkIntToScalar(src.fBottom); |
|
486 } |
|
487 |
|
488 void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) { |
|
489 fLeft = left; |
|
490 fTop = top; |
|
491 fRight = right; |
|
492 fBottom = bottom; |
|
493 } |
|
494 // alias for set(l, t, r, b) |
|
495 void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) { |
|
496 this->set(left, top, right, bottom); |
|
497 } |
|
498 |
|
499 /** Initialize the rect with the 4 specified integers. The routine handles |
|
500 converting them to scalars (by calling SkIntToScalar) |
|
501 */ |
|
502 void iset(int left, int top, int right, int bottom) { |
|
503 fLeft = SkIntToScalar(left); |
|
504 fTop = SkIntToScalar(top); |
|
505 fRight = SkIntToScalar(right); |
|
506 fBottom = SkIntToScalar(bottom); |
|
507 } |
|
508 |
|
509 /** |
|
510 * Set this rectangle to be left/top at 0,0, and have the specified width |
|
511 * and height (automatically converted to SkScalar). |
|
512 */ |
|
513 void isetWH(int width, int height) { |
|
514 fLeft = fTop = 0; |
|
515 fRight = SkIntToScalar(width); |
|
516 fBottom = SkIntToScalar(height); |
|
517 } |
|
518 |
|
519 /** Set this rectangle to be the bounds of the array of points. |
|
520 If the array is empty (count == 0), then set this rectangle |
|
521 to the empty rectangle (0,0,0,0) |
|
522 */ |
|
523 void set(const SkPoint pts[], int count) { |
|
524 // set() had been checking for non-finite values, so keep that behavior |
|
525 // for now. Now that we have setBoundsCheck(), we may decide to make |
|
526 // set() be simpler/faster, and not check for those. |
|
527 (void)this->setBoundsCheck(pts, count); |
|
528 } |
|
529 |
|
530 // alias for set(pts, count) |
|
531 void setBounds(const SkPoint pts[], int count) { |
|
532 (void)this->setBoundsCheck(pts, count); |
|
533 } |
|
534 |
|
535 /** |
|
536 * Compute the bounds of the array of points, and set this rect to that |
|
537 * bounds and return true... unless a non-finite value is encountered, |
|
538 * in which case this rect is set to empty and false is returned. |
|
539 */ |
|
540 bool setBoundsCheck(const SkPoint pts[], int count); |
|
541 |
|
542 void set(const SkPoint& p0, const SkPoint& p1) { |
|
543 fLeft = SkMinScalar(p0.fX, p1.fX); |
|
544 fRight = SkMaxScalar(p0.fX, p1.fX); |
|
545 fTop = SkMinScalar(p0.fY, p1.fY); |
|
546 fBottom = SkMaxScalar(p0.fY, p1.fY); |
|
547 } |
|
548 |
|
549 void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) { |
|
550 fLeft = x; |
|
551 fTop = y; |
|
552 fRight = x + width; |
|
553 fBottom = y + height; |
|
554 } |
|
555 |
|
556 void setWH(SkScalar width, SkScalar height) { |
|
557 fLeft = 0; |
|
558 fTop = 0; |
|
559 fRight = width; |
|
560 fBottom = height; |
|
561 } |
|
562 |
|
563 /** |
|
564 * Make the largest representable rectangle |
|
565 */ |
|
566 void setLargest() { |
|
567 fLeft = fTop = SK_ScalarMin; |
|
568 fRight = fBottom = SK_ScalarMax; |
|
569 } |
|
570 |
|
571 /** |
|
572 * Make the largest representable rectangle, but inverted (e.g. fLeft will |
|
573 * be max and right will be min). |
|
574 */ |
|
575 void setLargestInverted() { |
|
576 fLeft = fTop = SK_ScalarMax; |
|
577 fRight = fBottom = SK_ScalarMin; |
|
578 } |
|
579 |
|
580 /** Offset set the rectangle by adding dx to its left and right, |
|
581 and adding dy to its top and bottom. |
|
582 */ |
|
583 void offset(SkScalar dx, SkScalar dy) { |
|
584 fLeft += dx; |
|
585 fTop += dy; |
|
586 fRight += dx; |
|
587 fBottom += dy; |
|
588 } |
|
589 |
|
590 void offset(const SkPoint& delta) { |
|
591 this->offset(delta.fX, delta.fY); |
|
592 } |
|
593 |
|
594 /** |
|
595 * Offset this rect such its new x() and y() will equal newX and newY. |
|
596 */ |
|
597 void offsetTo(SkScalar newX, SkScalar newY) { |
|
598 fRight += newX - fLeft; |
|
599 fBottom += newY - fTop; |
|
600 fLeft = newX; |
|
601 fTop = newY; |
|
602 } |
|
603 |
|
604 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are |
|
605 moved inwards, making the rectangle narrower. If dx is negative, then |
|
606 the sides are moved outwards, making the rectangle wider. The same holds |
|
607 true for dy and the top and bottom. |
|
608 */ |
|
609 void inset(SkScalar dx, SkScalar dy) { |
|
610 fLeft += dx; |
|
611 fTop += dy; |
|
612 fRight -= dx; |
|
613 fBottom -= dy; |
|
614 } |
|
615 |
|
616 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are |
|
617 moved outwards, making the rectangle wider. If dx is negative, then the |
|
618 sides are moved inwards, making the rectangle narrower. The same holds |
|
619 true for dy and the top and bottom. |
|
620 */ |
|
621 void outset(SkScalar dx, SkScalar dy) { this->inset(-dx, -dy); } |
|
622 |
|
623 /** If this rectangle intersects r, return true and set this rectangle to that |
|
624 intersection, otherwise return false and do not change this rectangle. |
|
625 If either rectangle is empty, do nothing and return false. |
|
626 */ |
|
627 bool intersect(const SkRect& r); |
|
628 bool intersect2(const SkRect& r); |
|
629 |
|
630 /** If this rectangle intersects the rectangle specified by left, top, right, bottom, |
|
631 return true and set this rectangle to that intersection, otherwise return false |
|
632 and do not change this rectangle. |
|
633 If either rectangle is empty, do nothing and return false. |
|
634 */ |
|
635 bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom); |
|
636 |
|
637 /** |
|
638 * Return true if this rectangle is not empty, and the specified sides of |
|
639 * a rectangle are not empty, and they intersect. |
|
640 */ |
|
641 bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const { |
|
642 return // first check that both are not empty |
|
643 left < right && top < bottom && |
|
644 fLeft < fRight && fTop < fBottom && |
|
645 // now check for intersection |
|
646 fLeft < right && left < fRight && |
|
647 fTop < bottom && top < fBottom; |
|
648 } |
|
649 |
|
650 /** If rectangles a and b intersect, return true and set this rectangle to |
|
651 * that intersection, otherwise return false and do not change this |
|
652 * rectangle. If either rectangle is empty, do nothing and return false. |
|
653 */ |
|
654 bool intersect(const SkRect& a, const SkRect& b); |
|
655 |
|
656 /** |
|
657 * Return true if rectangles a and b are not empty and intersect. |
|
658 */ |
|
659 static bool Intersects(const SkRect& a, const SkRect& b) { |
|
660 return !a.isEmpty() && !b.isEmpty() && |
|
661 a.fLeft < b.fRight && b.fLeft < a.fRight && |
|
662 a.fTop < b.fBottom && b.fTop < a.fBottom; |
|
663 } |
|
664 |
|
665 /** |
|
666 * Update this rectangle to enclose itself and the specified rectangle. |
|
667 * If this rectangle is empty, just set it to the specified rectangle. |
|
668 * If the specified rectangle is empty, do nothing. |
|
669 */ |
|
670 void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom); |
|
671 |
|
672 /** Update this rectangle to enclose itself and the specified rectangle. |
|
673 If this rectangle is empty, just set it to the specified rectangle. If the specified |
|
674 rectangle is empty, do nothing. |
|
675 */ |
|
676 void join(const SkRect& r) { |
|
677 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom); |
|
678 } |
|
679 // alias for join() |
|
680 void growToInclude(const SkRect& r) { this->join(r); } |
|
681 |
|
682 /** |
|
683 * Grow the rect to include the specified (x,y). After this call, the |
|
684 * following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom. |
|
685 * |
|
686 * This is close, but not quite the same contract as contains(), since |
|
687 * contains() treats the left and top different from the right and bottom. |
|
688 * contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note |
|
689 * that contains(x,y) always returns false if the rect is empty. |
|
690 */ |
|
691 void growToInclude(SkScalar x, SkScalar y) { |
|
692 fLeft = SkMinScalar(x, fLeft); |
|
693 fRight = SkMaxScalar(x, fRight); |
|
694 fTop = SkMinScalar(y, fTop); |
|
695 fBottom = SkMaxScalar(y, fBottom); |
|
696 } |
|
697 |
|
698 /** Bulk version of growToInclude */ |
|
699 void growToInclude(const SkPoint pts[], int count) { |
|
700 this->growToInclude(pts, sizeof(SkPoint), count); |
|
701 } |
|
702 |
|
703 /** Bulk version of growToInclude with stride. */ |
|
704 void growToInclude(const SkPoint pts[], size_t stride, int count) { |
|
705 SkASSERT(count >= 0); |
|
706 SkASSERT(stride >= sizeof(SkPoint)); |
|
707 const SkPoint* end = (const SkPoint*)((intptr_t)pts + count * stride); |
|
708 for (; pts < end; pts = (const SkPoint*)((intptr_t)pts + stride)) { |
|
709 this->growToInclude(pts->fX, pts->fY); |
|
710 } |
|
711 } |
|
712 |
|
713 /** |
|
714 * Return true if this rectangle contains r, and if both rectangles are |
|
715 * not empty. |
|
716 */ |
|
717 bool contains(const SkRect& r) const { |
|
718 // todo: can we eliminate the this->isEmpty check? |
|
719 return !r.isEmpty() && !this->isEmpty() && |
|
720 fLeft <= r.fLeft && fTop <= r.fTop && |
|
721 fRight >= r.fRight && fBottom >= r.fBottom; |
|
722 } |
|
723 |
|
724 /** |
|
725 * Set the dst rectangle by rounding this rectangle's coordinates to their |
|
726 * nearest integer values using SkScalarRoundToInt. |
|
727 */ |
|
728 void round(SkIRect* dst) const { |
|
729 SkASSERT(dst); |
|
730 dst->set(SkScalarRoundToInt(fLeft), SkScalarRoundToInt(fTop), |
|
731 SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom)); |
|
732 } |
|
733 |
|
734 /** |
|
735 * Set the dst rectangle by rounding "out" this rectangle, choosing the |
|
736 * SkScalarFloor of top and left, and the SkScalarCeil of right and bottom. |
|
737 */ |
|
738 void roundOut(SkIRect* dst) const { |
|
739 SkASSERT(dst); |
|
740 dst->set(SkScalarFloorToInt(fLeft), SkScalarFloorToInt(fTop), |
|
741 SkScalarCeilToInt(fRight), SkScalarCeilToInt(fBottom)); |
|
742 } |
|
743 |
|
744 /** |
|
745 * Expand this rectangle by rounding its coordinates "out", choosing the |
|
746 * floor of top and left, and the ceil of right and bottom. If this rect |
|
747 * is already on integer coordinates, then it will be unchanged. |
|
748 */ |
|
749 void roundOut() { |
|
750 this->set(SkScalarFloorToScalar(fLeft), |
|
751 SkScalarFloorToScalar(fTop), |
|
752 SkScalarCeilToScalar(fRight), |
|
753 SkScalarCeilToScalar(fBottom)); |
|
754 } |
|
755 |
|
756 /** |
|
757 * Set the dst rectangle by rounding "in" this rectangle, choosing the |
|
758 * ceil of top and left, and the floor of right and bottom. This does *not* |
|
759 * call sort(), so it is possible that the resulting rect is inverted... |
|
760 * e.g. left >= right or top >= bottom. Call isEmpty() to detect that. |
|
761 */ |
|
762 void roundIn(SkIRect* dst) const { |
|
763 SkASSERT(dst); |
|
764 dst->set(SkScalarCeilToInt(fLeft), SkScalarCeilToInt(fTop), |
|
765 SkScalarFloorToInt(fRight), SkScalarFloorToInt(fBottom)); |
|
766 } |
|
767 |
|
768 /** |
|
769 * Return a new SkIRect which is contains the rounded coordinates of this |
|
770 * rect using SkScalarRoundToInt. |
|
771 */ |
|
772 SkIRect round() const { |
|
773 SkIRect ir; |
|
774 this->round(&ir); |
|
775 return ir; |
|
776 } |
|
777 |
|
778 /** |
|
779 * Swap top/bottom or left/right if there are flipped (i.e. if width() |
|
780 * or height() would have returned a negative value.) This should be called |
|
781 * if the edges are computed separately, and may have crossed over each |
|
782 * other. When this returns, left <= right && top <= bottom |
|
783 */ |
|
784 void sort(); |
|
785 |
|
786 /** |
|
787 * cast-safe way to treat the rect as an array of (4) SkScalars. |
|
788 */ |
|
789 const SkScalar* asScalars() const { return &fLeft; } |
|
790 }; |
|
791 |
|
792 #endif |