Coverage Report

Created: 2018-09-25 14:53

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