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