Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#ifndef MOZILLA_GFX_BASERECT_H_
8
#define MOZILLA_GFX_BASERECT_H_
9
10
#include <algorithm>
11
#include <cmath>
12
#include <ostream>
13
14
#include "mozilla/Assertions.h"
15
#include "mozilla/FloatingPoint.h"
16
#include "mozilla/TypeTraits.h"
17
#include "Types.h"
18
19
namespace mozilla {
20
namespace gfx {
21
22
/**
23
* Rectangles have two interpretations: a set of (zero-size) points,
24
* and a rectangular area of the plane. Most rectangle operations behave
25
* the same no matter what interpretation is being used, but some operations
26
* differ:
27
* -- Equality tests behave differently. When a rectangle represents an area,
28
* all zero-width and zero-height rectangles are equal to each other since they
29
* represent the empty area. But when a rectangle represents a set of
30
* mathematical points, zero-width and zero-height rectangles can be unequal.
31
* -- The union operation can behave differently. When rectangles represent
32
* areas, taking the union of a zero-width or zero-height rectangle with
33
* another rectangle can just ignore the empty rectangle. But when rectangles
34
* represent sets of mathematical points, we may need to extend the latter
35
* rectangle to include the points of a zero-width or zero-height rectangle.
36
*
37
* To ensure that these interpretations are explicitly disambiguated, we
38
* deny access to the == and != operators and require use of IsEqualEdges and
39
* IsEqualInterior instead. Similarly we provide separate Union and UnionEdges
40
* methods.
41
*
42
* Do not use this class directly. Subclass it, pass that subclass as the
43
* Sub parameter, and only use that subclass.
44
*/
45
template <class T, class Sub, class Point, class SizeT, class MarginT>
46
struct BaseRect {
47
T x, y, width, height;
48
49
// Constructors
50
BaseRect() : x(0), y(0), width(0), height(0) {}
51
BaseRect(const Point& aOrigin, const SizeT& aSize)
52
: x(aOrigin.x), y(aOrigin.y), width(aSize.width), height(aSize.height) {}
53
BaseRect(T aX, T aY, T aWidth, T aHeight)
54
: x(aX), y(aY), width(aWidth), height(aHeight) {}
55
56
// Emptiness. An empty rect is one that has no area, i.e. its height or width
57
// is <= 0. Zero rect is the one with height and width set to zero. Note
58
// that SetEmpty() may change a rectangle that identified as IsEmpty().
59
MOZ_ALWAYS_INLINE bool IsZeroArea() const {
60
return height == 0 || width == 0;
61
}
62
MOZ_ALWAYS_INLINE bool IsEmpty() const { return height <= 0 || width <= 0; }
63
void SetEmpty() { width = height = 0; }
64
65
// "Finite" means not inf and not NaN
66
bool IsFinite() const {
67
typedef typename mozilla::Conditional<mozilla::IsSame<T, float>::value,
68
float, double>::Type FloatType;
69
return (mozilla::IsFinite(FloatType(x)) &&
70
mozilla::IsFinite(FloatType(y)) &&
71
mozilla::IsFinite(FloatType(width)) &&
72
mozilla::IsFinite(FloatType(height)));
73
}
74
75
// Returns true if this rectangle contains the interior of aRect. Always
76
// returns true if aRect is empty, and always returns false is aRect is
77
// nonempty but this rect is empty.
78
bool Contains(const Sub& aRect) const {
79
return aRect.IsEmpty() || (x <= aRect.x && aRect.XMost() <= XMost() &&
80
y <= aRect.y && aRect.YMost() <= YMost());
81
}
82
// Returns true if this rectangle contains the point. Points are considered
83
// in the rectangle if they are on the left or top edge, but outside if they
84
// are on the right or bottom edge.
85
MOZ_ALWAYS_INLINE bool Contains(T aX, T aY) const {
86
return x <= aX && aX < XMost() && y <= aY && aY < YMost();
87
}
88
MOZ_ALWAYS_INLINE bool ContainsX(T aX) const {
89
return x <= aX && aX < XMost();
90
}
91
MOZ_ALWAYS_INLINE bool ContainsY(T aY) const {
92
return y <= aY && aY < YMost();
93
}
94
// Returns true if this rectangle contains the point. Points are considered
95
// in the rectangle if they are on the left or top edge, but outside if they
96
// are on the right or bottom edge.
97
bool Contains(const Point& aPoint) const {
98
return Contains(aPoint.x, aPoint.y);
99
}
100
101
// Intersection. Returns TRUE if the receiver's area has non-empty
102
// intersection with aRect's area, and FALSE otherwise.
103
// Always returns false if aRect is empty or 'this' is empty.
104
bool Intersects(const Sub& aRect) const {
105
return !IsEmpty() && !aRect.IsEmpty() && x < aRect.XMost() &&
106
aRect.x < XMost() && y < aRect.YMost() && aRect.y < YMost();
107
}
108
// Returns the rectangle containing the intersection of the points
109
// (including edges) of *this and aRect. If there are no points in that
110
// intersection, returns an empty rectangle with x/y set to the std::max of
111
// the x/y of *this and aRect.
112
MOZ_MUST_USE Sub Intersect(const Sub& aRect) const {
113
Sub result;
114
result.x = std::max<T>(x, aRect.x);
115
result.y = std::max<T>(y, aRect.y);
116
result.width =
117
std::min<T>(x - result.x + width, aRect.x - result.x + aRect.width);
118
result.height =
119
std::min<T>(y - result.y + height, aRect.y - result.y + aRect.height);
120
// See bug 1457110, this function expects to -only- size to 0,0 if the
121
// width/height is explicitly negative.
122
if (result.width < 0 || result.height < 0) {
123
result.SizeTo(0, 0);
124
}
125
return result;
126
}
127
// Sets *this to be the rectangle containing the intersection of the points
128
// (including edges) of *this and aRect. If there are no points in that
129
// intersection, sets *this to be an empty rectangle with x/y set to the
130
// std::max of the x/y of *this and aRect.
131
//
132
// 'this' can be the same object as either aRect1 or aRect2
133
bool IntersectRect(const Sub& aRect1, const Sub& aRect2) {
134
T newX = std::max<T>(aRect1.x, aRect2.x);
135
T newY = std::max<T>(aRect1.y, aRect2.y);
136
width = std::min<T>(aRect1.x - newX + aRect1.width,
137
aRect2.x - newX + aRect2.width);
138
height = std::min<T>(aRect1.y - newY + aRect1.height,
139
aRect2.y - newY + aRect2.height);
140
x = newX;
141
y = newY;
142
if (width <= 0 || height <= 0) {
143
SizeTo(0, 0);
144
return false;
145
}
146
return true;
147
}
148
149
// Returns the smallest rectangle that contains both the area of both
150
// this and aRect2.
151
// Thus, empty input rectangles are ignored.
152
// If both rectangles are empty, returns this.
153
// WARNING! This is not safe against overflow, prefer using SafeUnion instead
154
// when dealing with int-based rects.
155
MOZ_MUST_USE Sub Union(const Sub& aRect) const {
156
if (IsEmpty()) {
157
return aRect;
158
} else if (aRect.IsEmpty()) {
159
return *static_cast<const Sub*>(this);
160
} else {
161
return UnionEdges(aRect);
162
}
163
}
164
// Returns the smallest rectangle that contains both the points (including
165
// edges) of both aRect1 and aRect2.
166
// Thus, empty input rectangles are allowed to affect the result.
167
// WARNING! This is not safe against overflow, prefer using SafeUnionEdges
168
// instead when dealing with int-based rects.
169
MOZ_MUST_USE Sub UnionEdges(const Sub& aRect) const {
170
Sub result;
171
result.x = std::min(x, aRect.x);
172
result.y = std::min(y, aRect.y);
173
result.width = std::max(XMost(), aRect.XMost()) - result.x;
174
result.height = std::max(YMost(), aRect.YMost()) - result.y;
175
return result;
176
}
177
// Computes the smallest rectangle that contains both the area of both
178
// aRect1 and aRect2, and fills 'this' with the result.
179
// Thus, empty input rectangles are ignored.
180
// If both rectangles are empty, sets 'this' to aRect2.
181
//
182
// 'this' can be the same object as either aRect1 or aRect2
183
void UnionRect(const Sub& aRect1, const Sub& aRect2) {
184
*static_cast<Sub*>(this) = aRect1.Union(aRect2);
185
}
186
187
void OrWith(const Sub& aRect1) {
188
UnionRect(*static_cast<Sub*>(this), aRect1);
189
}
190
191
// Computes the smallest rectangle that contains both the points (including
192
// edges) of both aRect1 and aRect2.
193
// Thus, empty input rectangles are allowed to affect the result.
194
//
195
// 'this' can be the same object as either aRect1 or aRect2
196
void UnionRectEdges(const Sub& aRect1, const Sub& aRect2) {
197
*static_cast<Sub*>(this) = aRect1.UnionEdges(aRect2);
198
}
199
200
// Expands the rect to include the point
201
void ExpandToEnclose(const Point& aPoint) {
202
if (aPoint.x < x) {
203
width = XMost() - aPoint.x;
204
x = aPoint.x;
205
} else if (aPoint.x > XMost()) {
206
width = aPoint.x - x;
207
}
208
if (aPoint.y < y) {
209
height = YMost() - aPoint.y;
210
y = aPoint.y;
211
} else if (aPoint.y > YMost()) {
212
height = aPoint.y - y;
213
}
214
}
215
216
MOZ_ALWAYS_INLINE void SetRect(T aX, T aY, T aWidth, T aHeight) {
217
x = aX;
218
y = aY;
219
width = aWidth;
220
height = aHeight;
221
}
222
MOZ_ALWAYS_INLINE void SetRectX(T aX, T aWidth) {
223
x = aX;
224
width = aWidth;
225
}
226
MOZ_ALWAYS_INLINE void SetRectY(T aY, T aHeight) {
227
y = aY;
228
height = aHeight;
229
}
230
MOZ_ALWAYS_INLINE void SetBox(T aX, T aY, T aXMost, T aYMost) {
231
x = aX;
232
y = aY;
233
width = aXMost - aX;
234
height = aYMost - aY;
235
}
236
MOZ_ALWAYS_INLINE void SetNonEmptyBox(T aX, T aY, T aXMost, T aYMost) {
237
x = aX;
238
y = aY;
239
width = std::max(0, aXMost - aX);
240
height = std::max(0, aYMost - aY);
241
}
242
MOZ_ALWAYS_INLINE void SetBoxX(T aX, T aXMost) {
243
x = aX;
244
width = aXMost - aX;
245
}
246
MOZ_ALWAYS_INLINE void SetBoxY(T aY, T aYMost) {
247
y = aY;
248
height = aYMost - aY;
249
}
250
void SetRect(const Point& aPt, const SizeT& aSize) {
251
SetRect(aPt.x, aPt.y, aSize.width, aSize.height);
252
}
253
MOZ_ALWAYS_INLINE void GetRect(T* aX, T* aY, T* aWidth, T* aHeight) const {
254
*aX = x;
255
*aY = y;
256
*aWidth = width;
257
*aHeight = height;
258
}
259
260
MOZ_ALWAYS_INLINE void MoveTo(T aX, T aY) {
261
x = aX;
262
y = aY;
263
}
264
MOZ_ALWAYS_INLINE void MoveToX(T aX) { x = aX; }
265
MOZ_ALWAYS_INLINE void MoveToY(T aY) { y = aY; }
266
MOZ_ALWAYS_INLINE void MoveTo(const Point& aPoint) {
267
x = aPoint.x;
268
y = aPoint.y;
269
}
270
MOZ_ALWAYS_INLINE void MoveBy(T aDx, T aDy) {
271
x += aDx;
272
y += aDy;
273
}
274
MOZ_ALWAYS_INLINE void MoveByX(T aDx) { x += aDx; }
275
MOZ_ALWAYS_INLINE void MoveByY(T aDy) { y += aDy; }
276
MOZ_ALWAYS_INLINE void MoveBy(const Point& aPoint) {
277
x += aPoint.x;
278
y += aPoint.y;
279
}
280
MOZ_ALWAYS_INLINE void SizeTo(T aWidth, T aHeight) {
281
width = aWidth;
282
height = aHeight;
283
}
284
MOZ_ALWAYS_INLINE void SizeTo(const SizeT& aSize) {
285
width = aSize.width;
286
height = aSize.height;
287
}
288
289
void Inflate(T aD) { Inflate(aD, aD); }
290
void Inflate(T aDx, T aDy) {
291
x -= aDx;
292
y -= aDy;
293
width += 2 * aDx;
294
height += 2 * aDy;
295
}
296
void Inflate(const MarginT& aMargin) {
297
x -= aMargin.left;
298
y -= aMargin.top;
299
width += aMargin.LeftRight();
300
height += aMargin.TopBottom();
301
}
302
void Inflate(const SizeT& aSize) { Inflate(aSize.width, aSize.height); }
303
304
void Deflate(T aD) { Deflate(aD, aD); }
305
void Deflate(T aDx, T aDy) {
306
x += aDx;
307
y += aDy;
308
width = std::max(T(0), width - 2 * aDx);
309
height = std::max(T(0), height - 2 * aDy);
310
}
311
void Deflate(const MarginT& aMargin) {
312
x += aMargin.left;
313
y += aMargin.top;
314
width = std::max(T(0), width - aMargin.LeftRight());
315
height = std::max(T(0), height - aMargin.TopBottom());
316
}
317
void Deflate(const SizeT& aSize) { Deflate(aSize.width, aSize.height); }
318
319
// Return true if the rectangles contain the same set of points, including
320
// points on the edges.
321
// Use when we care about the exact x/y/width/height values being
322
// equal (i.e. we care about differences in empty rectangles).
323
bool IsEqualEdges(const Sub& aRect) const {
324
return x == aRect.x && y == aRect.y && width == aRect.width &&
325
height == aRect.height;
326
}
327
MOZ_ALWAYS_INLINE bool IsEqualRect(T aX, T aY, T aW, T aH) {
328
return x == aX && y == aY && width == aW && height == aH;
329
}
330
MOZ_ALWAYS_INLINE bool IsEqualXY(T aX, T aY) { return x == aX && y == aY; }
331
332
MOZ_ALWAYS_INLINE bool IsEqualSize(T aW, T aH) {
333
return width == aW && height == aH;
334
}
335
336
// Return true if the rectangles contain the same area of the plane.
337
// Use when we do not care about differences in empty rectangles.
338
bool IsEqualInterior(const Sub& aRect) const {
339
return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty());
340
}
341
342
friend Sub operator+(Sub aSub, const Point& aPoint) {
343
aSub += aPoint;
344
return aSub;
345
}
346
friend Sub operator-(Sub aSub, const Point& aPoint) {
347
aSub -= aPoint;
348
return aSub;
349
}
350
friend Sub operator+(Sub aSub, const SizeT& aSize) {
351
aSub += aSize;
352
return aSub;
353
}
354
friend Sub operator-(Sub aSub, const SizeT& aSize) {
355
aSub -= aSize;
356
return aSub;
357
}
358
Sub& operator+=(const Point& aPoint) {
359
MoveBy(aPoint);
360
return *static_cast<Sub*>(this);
361
}
362
Sub& operator-=(const Point& aPoint) {
363
MoveBy(-aPoint);
364
return *static_cast<Sub*>(this);
365
}
366
Sub& operator+=(const SizeT& aSize) {
367
width += aSize.width;
368
height += aSize.height;
369
return *static_cast<Sub*>(this);
370
}
371
Sub& operator-=(const SizeT& aSize) {
372
width -= aSize.width;
373
height -= aSize.height;
374
return *static_cast<Sub*>(this);
375
}
376
// Find difference as a Margin
377
MarginT operator-(const Sub& aRect) const {
378
return MarginT(aRect.y - y, XMost() - aRect.XMost(),
379
YMost() - aRect.YMost(), aRect.x - x);
380
}
381
382
// Helpers for accessing the vertices
383
Point TopLeft() const { return Point(x, y); }
384
Point TopRight() const { return Point(XMost(), y); }
385
Point BottomLeft() const { return Point(x, YMost()); }
386
Point BottomRight() const { return Point(XMost(), YMost()); }
387
Point AtCorner(Corner aCorner) const {
388
switch (aCorner) {
389
case eCornerTopLeft:
390
return TopLeft();
391
case eCornerTopRight:
392
return TopRight();
393
case eCornerBottomRight:
394
return BottomRight();
395
case eCornerBottomLeft:
396
return BottomLeft();
397
}
398
MOZ_CRASH("GFX: Incomplete switch");
399
}
400
Point CCWCorner(mozilla::Side side) const {
401
switch (side) {
402
case eSideTop:
403
return TopLeft();
404
case eSideRight:
405
return TopRight();
406
case eSideBottom:
407
return BottomRight();
408
case eSideLeft:
409
return BottomLeft();
410
}
411
MOZ_CRASH("GFX: Incomplete switch");
412
}
413
Point CWCorner(mozilla::Side side) const {
414
switch (side) {
415
case eSideTop:
416
return TopRight();
417
case eSideRight:
418
return BottomRight();
419
case eSideBottom:
420
return BottomLeft();
421
case eSideLeft:
422
return TopLeft();
423
}
424
MOZ_CRASH("GFX: Incomplete switch");
425
}
426
Point Center() const { return Point(x, y) + Point(width, height) / 2; }
427
SizeT Size() const { return SizeT(width, height); }
428
429
T Area() const { return width * height; }
430
431
// Helper methods for computing the extents
432
MOZ_ALWAYS_INLINE T X() const { return x; }
433
MOZ_ALWAYS_INLINE T Y() const { return y; }
434
MOZ_ALWAYS_INLINE T Width() const { return width; }
435
MOZ_ALWAYS_INLINE T Height() const { return height; }
436
MOZ_ALWAYS_INLINE T XMost() const { return x + width; }
437
MOZ_ALWAYS_INLINE T YMost() const { return y + height; }
438
439
// Set width and height. SizeTo() sets them together.
440
MOZ_ALWAYS_INLINE void SetWidth(T aWidth) { width = aWidth; }
441
MOZ_ALWAYS_INLINE void SetHeight(T aHeight) { height = aHeight; }
442
443
// Get the coordinate of the edge on the given side.
444
T Edge(mozilla::Side aSide) const {
445
switch (aSide) {
446
case eSideTop:
447
return Y();
448
case eSideRight:
449
return XMost();
450
case eSideBottom:
451
return YMost();
452
case eSideLeft:
453
return X();
454
}
455
MOZ_CRASH("GFX: Incomplete switch");
456
}
457
458
// Moves one edge of the rect without moving the opposite edge.
459
void SetLeftEdge(T aX) {
460
width = XMost() - aX;
461
x = aX;
462
}
463
void SetRightEdge(T aXMost) { width = aXMost - x; }
464
void SetTopEdge(T aY) {
465
height = YMost() - aY;
466
y = aY;
467
}
468
void SetBottomEdge(T aYMost) { height = aYMost - y; }
469
void Swap() {
470
std::swap(x, y);
471
std::swap(width, height);
472
}
473
474
// Round the rectangle edges to integer coordinates, such that the rounded
475
// rectangle has the same set of pixel centers as the original rectangle.
476
// Edges at offset 0.5 round up.
477
// Suitable for most places where integral device coordinates
478
// are needed, but note that any translation should be applied first to
479
// avoid pixel rounding errors.
480
// Note that this is *not* rounding to nearest integer if the values are
481
// negative. They are always rounding as floor(n + 0.5). See
483
// method which is using NS_round(), you should create new
484
// |RoundAwayFromZero()| method.
485
void Round() {
486
T x0 = static_cast<T>(std::floor(T(X()) + 0.5f));
487
T y0 = static_cast<T>(std::floor(T(Y()) + 0.5f));
488
T x1 = static_cast<T>(std::floor(T(XMost()) + 0.5f));
489
T y1 = static_cast<T>(std::floor(T(YMost()) + 0.5f));
490
491
x = x0;
492
y = y0;
493
494
width = x1 - x0;
495
height = y1 - y0;
496
}
497
498
// Snap the rectangle edges to integer coordinates, such that the
499
// original rectangle contains the resulting rectangle.
500
void RoundIn() {
501
T x0 = static_cast<T>(std::ceil(T(X())));
502
T y0 = static_cast<T>(std::ceil(T(Y())));
503
T x1 = static_cast<T>(std::floor(T(XMost())));
504
T y1 = static_cast<T>(std::floor(T(YMost())));
505
506
x = x0;
507
y = y0;
508
509
width = x1 - x0;
510
height = y1 - y0;
511
}
512
513
// Snap the rectangle edges to integer coordinates, such that the
514
// resulting rectangle contains the original rectangle.
515
void RoundOut() {
516
T x0 = static_cast<T>(std::floor(T(X())));
517
T y0 = static_cast<T>(std::floor(T(Y())));
518
T x1 = static_cast<T>(std::ceil(T(XMost())));
519
T y1 = static_cast<T>(std::ceil(T(YMost())));
520
521
x = x0;
522
y = y0;
523
524
width = x1 - x0;
525
height = y1 - y0;
526
}
527
528
// Scale 'this' by aScale without doing any rounding.
529
void Scale(T aScale) { Scale(aScale, aScale); }
530
// Scale 'this' by aXScale and aYScale, without doing any rounding.
531
void Scale(T aXScale, T aYScale) {
532
T right = XMost() * aXScale;
533
T bottom = YMost() * aYScale;
534
x = x * aXScale;
535
y = y * aYScale;
536
width = right - x;
537
height = bottom - y;
538
}
539
// Scale 'this' by aScale, converting coordinates to integers so that the
540
// result is the smallest integer-coordinate rectangle containing the
541
// unrounded result. Note: this can turn an empty rectangle into a non-empty
542
// rectangle
543
void ScaleRoundOut(double aScale) { ScaleRoundOut(aScale, aScale); }
544
// Scale 'this' by aXScale and aYScale, converting coordinates to integers so
545
// that the result is the smallest integer-coordinate rectangle containing the
546
// unrounded result.
547
// Note: this can turn an empty rectangle into a non-empty rectangle
548
void ScaleRoundOut(double aXScale, double aYScale) {
549
T right = static_cast<T>(ceil(double(XMost()) * aXScale));
550
T bottom = static_cast<T>(ceil(double(YMost()) * aYScale));
551
x = static_cast<T>(floor(double(x) * aXScale));
552
y = static_cast<T>(floor(double(y) * aYScale));
553
width = right - x;
554
height = bottom - y;
555
}
556
// Scale 'this' by aScale, converting coordinates to integers so that the
557
// result is the largest integer-coordinate rectangle contained by the
558
// unrounded result.
559
void ScaleRoundIn(double aScale) { ScaleRoundIn(aScale, aScale); }
560
// Scale 'this' by aXScale and aYScale, converting coordinates to integers so
561
// that the result is the largest integer-coordinate rectangle contained by
562
// the unrounded result.
563
void ScaleRoundIn(double aXScale, double aYScale) {
564
T right = static_cast<T>(floor(double(XMost()) * aXScale));
565
T bottom = static_cast<T>(floor(double(YMost()) * aYScale));
566
x = static_cast<T>(ceil(double(x) * aXScale));
567
y = static_cast<T>(ceil(double(y) * aYScale));
568
width = std::max<T>(0, right - x);
569
height = std::max<T>(0, bottom - y);
570
}
571
// Scale 'this' by 1/aScale, converting coordinates to integers so that the
572
// result is the smallest integer-coordinate rectangle containing the
573
// unrounded result. Note: this can turn an empty rectangle into a non-empty
574
// rectangle
575
void ScaleInverseRoundOut(double aScale) {
576
ScaleInverseRoundOut(aScale, aScale);
577
}
578
// Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers
579
// so that the result is the smallest integer-coordinate rectangle containing
580
// the unrounded result. Note: this can turn an empty rectangle into a
581
// non-empty rectangle
582
void ScaleInverseRoundOut(double aXScale, double aYScale) {
583
T right = static_cast<T>(ceil(double(XMost()) / aXScale));
584
T bottom = static_cast<T>(ceil(double(YMost()) / aYScale));
585
x = static_cast<T>(floor(double(x) / aXScale));
586
y = static_cast<T>(floor(double(y) / aYScale));
587
width = right - x;
588
height = bottom - y;
589
}
590
// Scale 'this' by 1/aScale, converting coordinates to integers so that the
591
// result is the largest integer-coordinate rectangle contained by the
592
// unrounded result.
593
void ScaleInverseRoundIn(double aScale) {
594
ScaleInverseRoundIn(aScale, aScale);
595
}
596
// Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers
597
// so that the result is the largest integer-coordinate rectangle contained by
598
// the unrounded result.
599
void ScaleInverseRoundIn(double aXScale, double aYScale) {
600
T right = static_cast<T>(floor(double(XMost()) / aXScale));
601
T bottom = static_cast<T>(floor(double(YMost()) / aYScale));
602
x = static_cast<T>(ceil(double(x) / aXScale));
603
y = static_cast<T>(ceil(double(y) / aYScale));
604
width = std::max<T>(0, right - x);
605
height = std::max<T>(0, bottom - y);
606
}
607
608
/**
609
* Clamp aPoint to this rectangle. It is allowed to end up on any
610
* edge of the rectangle.
611
*/
612
MOZ_MUST_USE Point ClampPoint(const Point& aPoint) const {
613
return Point(std::max(x, std::min(XMost(), aPoint.x)),
614
std::max(y, std::min(YMost(), aPoint.y)));
615
}
616
617
/**
618
* Translate this rectangle to be inside aRect. If it doesn't fit inside
619
* aRect then the dimensions that don't fit will be shrunk so that they
620
* do fit. The resulting rect is returned.
621
*/
622
MOZ_MUST_USE Sub MoveInsideAndClamp(const Sub& aRect) const {
623
Sub rect(std::max(aRect.x, x), std::max(aRect.y, y),
624
std::min(aRect.width, width), std::min(aRect.height, height));
625
rect.x = std::min(rect.XMost(), aRect.XMost()) - rect.width;
626
rect.y = std::min(rect.YMost(), aRect.YMost()) - rect.height;
627
return rect;
628
}
629
630
// Returns the largest rectangle that can be represented with 32-bit
631
// signed integers, centered around a point at 0,0. As BaseRect's represent
632
// the dimensions as a top-left point with a width and height, the width
633
// and height will be the largest positive 32-bit value. The top-left
634
// position coordinate is divided by two to center the rectangle around a
635
// point at 0,0.
636
static Sub MaxIntRect() {
637
return Sub(static_cast<T>(-std::numeric_limits<int32_t>::max() * 0.5),
638
static_cast<T>(-std::numeric_limits<int32_t>::max() * 0.5),
639
static_cast<T>(std::numeric_limits<int32_t>::max()),
640
static_cast<T>(std::numeric_limits<int32_t>::max()));
641
};
642
643
// Returns a point representing the distance, along each dimension, of the
644
// given point from this rectangle. The distance along a dimension is defined
645
// as zero if the point is within the bounds of the rectangle in that
646
// dimension; otherwise, it's the distance to the closer endpoint of the
647
// rectangle in that dimension.
648
Point DistanceTo(const Point& aPoint) const {
649
return {DistanceFromInterval(aPoint.x, x, XMost()),
650
DistanceFromInterval(aPoint.y, y, YMost())};
651
}
652
653
friend std::ostream& operator<<(
654
std::ostream& stream,
655
const BaseRect<T, Sub, Point, SizeT, MarginT>& aRect) {
656
return stream << '(' << aRect.x << ',' << aRect.y << ',' << aRect.width
657
<< ',' << aRect.height << ')';
658
}
659
660
private:
661
// Do not use the default operator== or operator!= !
662
// Use IsEqualEdges or IsEqualInterior explicitly.
663
bool operator==(const Sub& aRect) const { return false; }
664
bool operator!=(const Sub& aRect) const { return false; }
665
666
// Helper function for DistanceTo() that computes the distance of a
667
// coordinate along one dimension from an interval in that dimension.
668
static T DistanceFromInterval(T aCoord, T aIntervalStart, T aIntervalEnd) {
669
if (aCoord < aIntervalStart) {
670
return aIntervalStart - aCoord;
671
}
672
if (aCoord > aIntervalEnd) {
673
return aCoord - aIntervalEnd;
674
}
675
return 0;
676
}
677
};
678
679
} // namespace gfx
680
} // namespace mozilla
681
682
#endif /* MOZILLA_GFX_BASERECT_H_ */