Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef MOZILLA_GFX_BASERECT_H_
7 #define MOZILLA_GFX_BASERECT_H_
9 #include <algorithm>
10 #include <cmath>
12 #include "mozilla/Assertions.h"
13 #include "mozilla/FloatingPoint.h"
14 #include "mozilla/TypeTraits.h"
16 namespace mozilla {
17 namespace gfx {
19 /**
20 * Rectangles have two interpretations: a set of (zero-size) points,
21 * and a rectangular area of the plane. Most rectangle operations behave
22 * the same no matter what interpretation is being used, but some operations
23 * differ:
24 * -- Equality tests behave differently. When a rectangle represents an area,
25 * all zero-width and zero-height rectangles are equal to each other since they
26 * represent the empty area. But when a rectangle represents a set of
27 * mathematical points, zero-width and zero-height rectangles can be unequal.
28 * -- The union operation can behave differently. When rectangles represent
29 * areas, taking the union of a zero-width or zero-height rectangle with
30 * another rectangle can just ignore the empty rectangle. But when rectangles
31 * represent sets of mathematical points, we may need to extend the latter
32 * rectangle to include the points of a zero-width or zero-height rectangle.
33 *
34 * To ensure that these interpretations are explicitly disambiguated, we
35 * deny access to the == and != operators and require use of IsEqualEdges and
36 * IsEqualInterior instead. Similarly we provide separate Union and UnionEdges
37 * methods.
38 *
39 * Do not use this class directly. Subclass it, pass that subclass as the
40 * Sub parameter, and only use that subclass.
41 */
42 template <class T, class Sub, class Point, class SizeT, class MarginT>
43 struct BaseRect {
44 T x, y, width, height;
46 // Constructors
47 BaseRect() : x(0), y(0), width(0), height(0) {}
48 BaseRect(const Point& aOrigin, const SizeT &aSize) :
49 x(aOrigin.x), y(aOrigin.y), width(aSize.width), height(aSize.height)
50 {
51 }
52 BaseRect(T aX, T aY, T aWidth, T aHeight) :
53 x(aX), y(aY), width(aWidth), height(aHeight)
54 {
55 }
57 // Emptiness. An empty rect is one that has no area, i.e. its height or width
58 // is <= 0
59 bool IsEmpty() const { return height <= 0 || width <= 0; }
60 void SetEmpty() { width = height = 0; }
62 // "Finite" means not inf and not NaN
63 bool IsFinite() const
64 {
65 typedef typename mozilla::Conditional<mozilla::IsSame<T, float>::value, float, double>::Type FloatType;
66 return (mozilla::IsFinite(FloatType(x)) &&
67 mozilla::IsFinite(FloatType(y)) &&
68 mozilla::IsFinite(FloatType(width)) &&
69 mozilla::IsFinite(FloatType(height)));
70 }
72 // Returns true if this rectangle contains the interior of aRect. Always
73 // returns true if aRect is empty, and always returns false is aRect is
74 // nonempty but this rect is empty.
75 bool Contains(const Sub& aRect) const
76 {
77 return aRect.IsEmpty() ||
78 (x <= aRect.x && aRect.XMost() <= XMost() &&
79 y <= aRect.y && aRect.YMost() <= YMost());
80 }
81 // Returns true if this rectangle contains the rectangle (aX,aY,1,1).
82 bool Contains(T aX, T aY) const
83 {
84 return x <= aX && aX + 1 <= XMost() &&
85 y <= aY && aY + 1 <= YMost();
86 }
87 // Returns true if this rectangle contains the rectangle (aPoint.x,aPoint.y,1,1).
88 bool Contains(const Point& aPoint) const { return Contains(aPoint.x, aPoint.y); }
90 // Intersection. Returns TRUE if the receiver's area has non-empty
91 // intersection with aRect's area, and FALSE otherwise.
92 // Always returns false if aRect is empty or 'this' is empty.
93 bool Intersects(const Sub& aRect) const
94 {
95 return x < aRect.XMost() && aRect.x < XMost() &&
96 y < aRect.YMost() && aRect.y < YMost();
97 }
98 // Returns the rectangle containing the intersection of the points
99 // (including edges) of *this and aRect. If there are no points in that
100 // intersection, returns an empty rectangle with x/y set to the std::max of the x/y
101 // of *this and aRect.
102 Sub Intersect(const Sub& aRect) const
103 {
104 Sub result;
105 result.x = std::max<T>(x, aRect.x);
106 result.y = std::max<T>(y, aRect.y);
107 result.width = std::min<T>(XMost(), aRect.XMost()) - result.x;
108 result.height = std::min<T>(YMost(), aRect.YMost()) - result.y;
109 if (result.width < 0 || result.height < 0) {
110 result.SizeTo(0, 0);
111 }
112 return result;
113 }
114 // Sets *this to be the rectangle containing the intersection of the points
115 // (including edges) of *this and aRect. If there are no points in that
116 // intersection, sets *this to be an empty rectangle with x/y set to the std::max
117 // of the x/y of *this and aRect.
118 //
119 // 'this' can be the same object as either aRect1 or aRect2
120 bool IntersectRect(const Sub& aRect1, const Sub& aRect2)
121 {
122 *static_cast<Sub*>(this) = aRect1.Intersect(aRect2);
123 return !IsEmpty();
124 }
126 // Returns the smallest rectangle that contains both the area of both
127 // this and aRect2.
128 // Thus, empty input rectangles are ignored.
129 // If both rectangles are empty, returns this.
130 Sub Union(const Sub& aRect) const
131 {
132 if (IsEmpty()) {
133 return aRect;
134 } else if (aRect.IsEmpty()) {
135 return *static_cast<const Sub*>(this);
136 } else {
137 return UnionEdges(aRect);
138 }
139 }
140 // Returns the smallest rectangle that contains both the points (including
141 // edges) of both aRect1 and aRect2.
142 // Thus, empty input rectangles are allowed to affect the result.
143 Sub UnionEdges(const Sub& aRect) const
144 {
145 Sub result;
146 result.x = std::min(x, aRect.x);
147 result.y = std::min(y, aRect.y);
148 result.width = std::max(XMost(), aRect.XMost()) - result.x;
149 result.height = std::max(YMost(), aRect.YMost()) - result.y;
150 return result;
151 }
152 // Computes the smallest rectangle that contains both the area of both
153 // aRect1 and aRect2, and fills 'this' with the result.
154 // Thus, empty input rectangles are ignored.
155 // If both rectangles are empty, sets 'this' to aRect2.
156 //
157 // 'this' can be the same object as either aRect1 or aRect2
158 void UnionRect(const Sub& aRect1, const Sub& aRect2)
159 {
160 *static_cast<Sub*>(this) = aRect1.Union(aRect2);
161 }
163 // Computes the smallest rectangle that contains both the points (including
164 // edges) of both aRect1 and aRect2.
165 // Thus, empty input rectangles are allowed to affect the result.
166 //
167 // 'this' can be the same object as either aRect1 or aRect2
168 void UnionRectEdges(const Sub& aRect1, const Sub& aRect2)
169 {
170 *static_cast<Sub*>(this) = aRect1.UnionEdges(aRect2);
171 }
173 void SetRect(T aX, T aY, T aWidth, T aHeight)
174 {
175 x = aX; y = aY; width = aWidth; height = aHeight;
176 }
177 void SetRect(const Point& aPt, const SizeT& aSize)
178 {
179 SetRect(aPt.x, aPt.y, aSize.width, aSize.height);
180 }
181 void MoveTo(T aX, T aY) { x = aX; y = aY; }
182 void MoveTo(const Point& aPoint) { x = aPoint.x; y = aPoint.y; }
183 void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; }
184 void MoveBy(const Point& aPoint) { x += aPoint.x; y += aPoint.y; }
185 void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; }
186 void SizeTo(const SizeT& aSize) { width = aSize.width; height = aSize.height; }
188 void Inflate(T aD) { Inflate(aD, aD); }
189 void Inflate(T aDx, T aDy)
190 {
191 x -= aDx;
192 y -= aDy;
193 width += 2 * aDx;
194 height += 2 * aDy;
195 }
196 void Inflate(const MarginT& aMargin)
197 {
198 x -= aMargin.left;
199 y -= aMargin.top;
200 width += aMargin.LeftRight();
201 height += aMargin.TopBottom();
202 }
203 void Inflate(const SizeT& aSize) { Inflate(aSize.width, aSize.height); }
205 void Deflate(T aD) { Deflate(aD, aD); }
206 void Deflate(T aDx, T aDy)
207 {
208 x += aDx;
209 y += aDy;
210 width = std::max(T(0), width - 2 * aDx);
211 height = std::max(T(0), height - 2 * aDy);
212 }
213 void Deflate(const MarginT& aMargin)
214 {
215 x += aMargin.left;
216 y += aMargin.top;
217 width = std::max(T(0), width - aMargin.LeftRight());
218 height = std::max(T(0), height - aMargin.TopBottom());
219 }
220 void Deflate(const SizeT& aSize) { Deflate(aSize.width, aSize.height); }
222 // Return true if the rectangles contain the same set of points, including
223 // points on the edges.
224 // Use when we care about the exact x/y/width/height values being
225 // equal (i.e. we care about differences in empty rectangles).
226 bool IsEqualEdges(const Sub& aRect) const
227 {
228 return x == aRect.x && y == aRect.y &&
229 width == aRect.width && height == aRect.height;
230 }
231 // Return true if the rectangles contain the same area of the plane.
232 // Use when we do not care about differences in empty rectangles.
233 bool IsEqualInterior(const Sub& aRect) const
234 {
235 return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty());
236 }
238 Sub operator+(const Point& aPoint) const
239 {
240 return Sub(x + aPoint.x, y + aPoint.y, width, height);
241 }
242 Sub operator-(const Point& aPoint) const
243 {
244 return Sub(x - aPoint.x, y - aPoint.y, width, height);
245 }
246 Sub& operator+=(const Point& aPoint)
247 {
248 MoveBy(aPoint);
249 return *static_cast<Sub*>(this);
250 }
251 Sub& operator-=(const Point& aPoint)
252 {
253 MoveBy(-aPoint);
254 return *static_cast<Sub*>(this);
255 }
257 // Find difference as a Margin
258 MarginT operator-(const Sub& aRect) const
259 {
260 return MarginT(aRect.y - y,
261 XMost() - aRect.XMost(),
262 YMost() - aRect.YMost(),
263 aRect.x - x);
264 }
266 // Helpers for accessing the vertices
267 Point TopLeft() const { return Point(x, y); }
268 Point TopRight() const { return Point(XMost(), y); }
269 Point BottomLeft() const { return Point(x, YMost()); }
270 Point BottomRight() const { return Point(XMost(), YMost()); }
271 Point Center() const { return Point(x, y) + Point(width, height)/2; }
272 SizeT Size() const { return SizeT(width, height); }
274 // Helper methods for computing the extents
275 T X() const { return x; }
276 T Y() const { return y; }
277 T Width() const { return width; }
278 T Height() const { return height; }
279 T XMost() const { return x + width; }
280 T YMost() const { return y + height; }
282 // Moves one edge of the rect without moving the opposite edge.
283 void SetLeftEdge(T aX) {
284 MOZ_ASSERT(aX <= XMost());
285 width = XMost() - aX;
286 x = aX;
287 }
288 void SetRightEdge(T aXMost) {
289 MOZ_ASSERT(aXMost >= x);
290 width = aXMost - x;
291 }
292 void SetTopEdge(T aY) {
293 MOZ_ASSERT(aY <= YMost());
294 height = YMost() - aY;
295 y = aY;
296 }
297 void SetBottomEdge(T aYMost) {
298 MOZ_ASSERT(aYMost >= y);
299 height = aYMost - y;
300 }
302 // Round the rectangle edges to integer coordinates, such that the rounded
303 // rectangle has the same set of pixel centers as the original rectangle.
304 // Edges at offset 0.5 round up.
305 // Suitable for most places where integral device coordinates
306 // are needed, but note that any translation should be applied first to
307 // avoid pixel rounding errors.
308 // Note that this is *not* rounding to nearest integer if the values are negative.
309 // They are always rounding as floor(n + 0.5).
310 // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
311 // If you need similar method which is using NS_round(), you should create
312 // new |RoundAwayFromZero()| method.
313 void Round()
314 {
315 T x0 = static_cast<T>(floor(T(X()) + 0.5));
316 T y0 = static_cast<T>(floor(T(Y()) + 0.5));
317 T x1 = static_cast<T>(floor(T(XMost()) + 0.5));
318 T y1 = static_cast<T>(floor(T(YMost()) + 0.5));
320 x = x0;
321 y = y0;
323 width = x1 - x0;
324 height = y1 - y0;
325 }
327 // Snap the rectangle edges to integer coordinates, such that the
328 // original rectangle contains the resulting rectangle.
329 void RoundIn()
330 {
331 T x0 = static_cast<T>(ceil(T(X())));
332 T y0 = static_cast<T>(ceil(T(Y())));
333 T x1 = static_cast<T>(floor(T(XMost())));
334 T y1 = static_cast<T>(floor(T(YMost())));
336 x = x0;
337 y = y0;
339 width = x1 - x0;
340 height = y1 - y0;
341 }
343 // Snap the rectangle edges to integer coordinates, such that the
344 // resulting rectangle contains the original rectangle.
345 void RoundOut()
346 {
347 T x0 = static_cast<T>(floor(T(X())));
348 T y0 = static_cast<T>(floor(T(Y())));
349 T x1 = static_cast<T>(ceil(T(XMost())));
350 T y1 = static_cast<T>(ceil(T(YMost())));
352 x = x0;
353 y = y0;
355 width = x1 - x0;
356 height = y1 - y0;
357 }
359 // Scale 'this' by aScale without doing any rounding.
360 void Scale(T aScale) { Scale(aScale, aScale); }
361 // Scale 'this' by aXScale and aYScale, without doing any rounding.
362 void Scale(T aXScale, T aYScale)
363 {
364 T right = XMost() * aXScale;
365 T bottom = YMost() * aYScale;
366 x = x * aXScale;
367 y = y * aYScale;
368 width = right - x;
369 height = bottom - y;
370 }
371 // Scale 'this' by aScale, converting coordinates to integers so that the result is
372 // the smallest integer-coordinate rectangle containing the unrounded result.
373 // Note: this can turn an empty rectangle into a non-empty rectangle
374 void ScaleRoundOut(double aScale) { ScaleRoundOut(aScale, aScale); }
375 // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
376 // that the result is the smallest integer-coordinate rectangle containing the
377 // unrounded result.
378 // Note: this can turn an empty rectangle into a non-empty rectangle
379 void ScaleRoundOut(double aXScale, double aYScale)
380 {
381 T right = static_cast<T>(ceil(double(XMost()) * aXScale));
382 T bottom = static_cast<T>(ceil(double(YMost()) * aYScale));
383 x = static_cast<T>(floor(double(x) * aXScale));
384 y = static_cast<T>(floor(double(y) * aYScale));
385 width = right - x;
386 height = bottom - y;
387 }
388 // Scale 'this' by aScale, converting coordinates to integers so that the result is
389 // the largest integer-coordinate rectangle contained by the unrounded result.
390 void ScaleRoundIn(double aScale) { ScaleRoundIn(aScale, aScale); }
391 // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
392 // that the result is the largest integer-coordinate rectangle contained by the
393 // unrounded result.
394 void ScaleRoundIn(double aXScale, double aYScale)
395 {
396 T right = static_cast<T>(floor(double(XMost()) * aXScale));
397 T bottom = static_cast<T>(floor(double(YMost()) * aYScale));
398 x = static_cast<T>(ceil(double(x) * aXScale));
399 y = static_cast<T>(ceil(double(y) * aYScale));
400 width = std::max<T>(0, right - x);
401 height = std::max<T>(0, bottom - y);
402 }
403 // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
404 // the smallest integer-coordinate rectangle containing the unrounded result.
405 // Note: this can turn an empty rectangle into a non-empty rectangle
406 void ScaleInverseRoundOut(double aScale) { ScaleInverseRoundOut(aScale, aScale); }
407 // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
408 // that the result is the smallest integer-coordinate rectangle containing the
409 // unrounded result.
410 // Note: this can turn an empty rectangle into a non-empty rectangle
411 void ScaleInverseRoundOut(double aXScale, double aYScale)
412 {
413 T right = static_cast<T>(ceil(double(XMost()) / aXScale));
414 T bottom = static_cast<T>(ceil(double(YMost()) / aYScale));
415 x = static_cast<T>(floor(double(x) / aXScale));
416 y = static_cast<T>(floor(double(y) / aYScale));
417 width = right - x;
418 height = bottom - y;
419 }
420 // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
421 // the largest integer-coordinate rectangle contained by the unrounded result.
422 void ScaleInverseRoundIn(double aScale) { ScaleInverseRoundIn(aScale, aScale); }
423 // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
424 // that the result is the largest integer-coordinate rectangle contained by the
425 // unrounded result.
426 void ScaleInverseRoundIn(double aXScale, double aYScale)
427 {
428 T right = static_cast<T>(floor(double(XMost()) / aXScale));
429 T bottom = static_cast<T>(floor(double(YMost()) / aYScale));
430 x = static_cast<T>(ceil(double(x) / aXScale));
431 y = static_cast<T>(ceil(double(y) / aYScale));
432 width = std::max<T>(0, right - x);
433 height = std::max<T>(0, bottom - y);
434 }
436 /**
437 * Clamp aPoint to this rectangle. It is allowed to end up on any
438 * edge of the rectangle.
439 */
440 Point ClampPoint(const Point& aPoint) const
441 {
442 return Point(std::max(x, std::min(XMost(), aPoint.x)),
443 std::max(y, std::min(YMost(), aPoint.y)));
444 }
446 /**
447 * Clamp this rectangle to be inside aRect. The function returns a copy of
448 * this rect after it is forced inside the bounds of aRect. It will attempt to
449 * retain the size but will shrink the dimensions that don't fit.
450 */
451 Sub ForceInside(const Sub& aRect) const
452 {
453 Sub rect(std::max(aRect.x, x),
454 std::max(aRect.y, y),
455 std::min(aRect.width, width),
456 std::min(aRect.height, height));
457 rect.x = std::min(rect.XMost(), aRect.XMost()) - rect.width;
458 rect.y = std::min(rect.YMost(), aRect.YMost()) - rect.height;
459 return rect;
460 }
462 private:
463 // Do not use the default operator== or operator!= !
464 // Use IsEqualEdges or IsEqualInterior explicitly.
465 bool operator==(const Sub& aRect) const { return false; }
466 bool operator!=(const Sub& aRect) const { return false; }
467 };
469 }
470 }
472 #endif /* MOZILLA_GFX_BASERECT_H_ */