DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (5b81998bb7ab)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef MOZILLA_GFX_BASERECT_H_
#define MOZILLA_GFX_BASERECT_H_

#include <cmath>
#include <mozilla/Assertions.h>

namespace mozilla {
namespace gfx {

// XXX - <algorithm> conflicts with exceptions on 10.6. Define our own gfx_min/gfx_max
// functions here. Avoid min/max to avoid conflicts with existing #defines on windows.
template<typename T>
T gfx_min(T aVal1, T aVal2)
{
  return (aVal1 < aVal2) ? aVal1 : aVal2;
}

template<typename T>
T gfx_max(T aVal1, T aVal2)
{
  return (aVal1 > aVal2) ? aVal1 : aVal2;
}

/**
 * Rectangles have two interpretations: a set of (zero-size) points,
 * and a rectangular area of the plane. Most rectangle operations behave
 * the same no matter what interpretation is being used, but some operations
 * differ:
 * -- Equality tests behave differently. When a rectangle represents an area,
 * all zero-width and zero-height rectangles are equal to each other since they
 * represent the empty area. But when a rectangle represents a set of
 * mathematical points, zero-width and zero-height rectangles can be unequal.
 * -- The union operation can behave differently. When rectangles represent
 * areas, taking the union of a zero-width or zero-height rectangle with
 * another rectangle can just ignore the empty rectangle. But when rectangles
 * represent sets of mathematical points, we may need to extend the latter
 * rectangle to include the points of a zero-width or zero-height rectangle.
 *
 * To ensure that these interpretations are explicitly disambiguated, we
 * deny access to the == and != operators and require use of IsEqualEdges and
 * IsEqualInterior instead. Similarly we provide separate Union and UnionEdges
 * methods.
 *
 * Do not use this class directly. Subclass it, pass that subclass as the
 * Sub parameter, and only use that subclass.
 */
template <class T, class Sub, class Point, class SizeT, class Margin>
struct BaseRect {
  T x, y, width, height;

  // Constructors
  BaseRect() : x(0), y(0), width(0), height(0) {}
  BaseRect(const Point& aOrigin, const SizeT &aSize) :
      x(aOrigin.x), y(aOrigin.y), width(aSize.width), height(aSize.height)
  {
  }
  BaseRect(T aX, T aY, T aWidth, T aHeight) :
      x(aX), y(aY), width(aWidth), height(aHeight)
  {
  }

  // Emptiness. An empty rect is one that has no area, i.e. its height or width
  // is <= 0
  bool IsEmpty() const { return height <= 0 || width <= 0; }
  void SetEmpty() { width = height = 0; }

  // "Finite" means not inf and not NaN
  bool IsFinite() const
  {
    return (std::isfinite(x) &&
            std::isfinite(y) &&
            std::isfinite(width) &&
            std::isfinite(height));
  }

  // Returns true if this rectangle contains the interior of aRect. Always
  // returns true if aRect is empty, and always returns false is aRect is
  // nonempty but this rect is empty.
  bool Contains(const Sub& aRect) const
  {
    return aRect.IsEmpty() ||
           (x <= aRect.x && aRect.XMost() <= XMost() &&
            y <= aRect.y && aRect.YMost() <= YMost());
  }
  // Returns true if this rectangle contains the rectangle (aX,aY,1,1).
  bool Contains(T aX, T aY) const
  {
    return x <= aX && aX + 1 <= XMost() &&
           y <= aY && aY + 1 <= YMost();
  }
  // Returns true if this rectangle contains the rectangle (aPoint.x,aPoint.y,1,1).
  bool Contains(const Point& aPoint) const { return Contains(aPoint.x, aPoint.y); }

  // Intersection. Returns TRUE if the receiver's area has non-empty
  // intersection with aRect's area, and FALSE otherwise.
  // Always returns false if aRect is empty or 'this' is empty.
  bool Intersects(const Sub& aRect) const
  {
    return x < aRect.XMost() && aRect.x < XMost() &&
           y < aRect.YMost() && aRect.y < YMost();
  }
  // Returns the rectangle containing the intersection of the points
  // (including edges) of *this and aRect. If there are no points in that
  // intersection, returns an empty rectangle with x/y set to the gfx_max of the x/y
  // of *this and aRect.
  Sub Intersect(const Sub& aRect) const
  {
    Sub result;
    result.x = gfx_max(x, aRect.x);
    result.y = gfx_max(y, aRect.y);
    result.width = gfx_min(XMost(), aRect.XMost()) - result.x;
    result.height = gfx_min(YMost(), aRect.YMost()) - result.y;
    if (result.width < 0 || result.height < 0) {
      result.SizeTo(0, 0);
    }
    return result;
  }
  // Sets *this to be the rectangle containing the intersection of the points
  // (including edges) of *this and aRect. If there are no points in that
  // intersection, sets *this to be an empty rectangle with x/y set to the gfx_max
  // of the x/y of *this and aRect.
  //
  // 'this' can be the same object as either aRect1 or aRect2
  bool IntersectRect(const Sub& aRect1, const Sub& aRect2)
  {
    *static_cast<Sub*>(this) = aRect1.Intersect(aRect2);
    return !IsEmpty();
  }

  // Returns the smallest rectangle that contains both the area of both
  // this and aRect2.
  // Thus, empty input rectangles are ignored.
  // If both rectangles are empty, returns this.
  Sub Union(const Sub& aRect) const
  {
    if (IsEmpty()) {
      return aRect;
    } else if (aRect.IsEmpty()) {
      return *static_cast<const Sub*>(this);
    } else {
      return UnionEdges(aRect);
    }
  }
  // Returns the smallest rectangle that contains both the points (including
  // edges) of both aRect1 and aRect2.
  // Thus, empty input rectangles are allowed to affect the result.
  Sub UnionEdges(const Sub& aRect) const
  {
    Sub result;
    result.x = gfx_min(x, aRect.x);
    result.y = gfx_min(y, aRect.y);
    result.width = gfx_max(XMost(), aRect.XMost()) - result.x;
    result.height = gfx_max(YMost(), aRect.YMost()) - result.y;
    return result;
  }
  // Computes the smallest rectangle that contains both the area of both
  // aRect1 and aRect2, and fills 'this' with the result.
  // Thus, empty input rectangles are ignored.
  // If both rectangles are empty, sets 'this' to aRect2.
  //
  // 'this' can be the same object as either aRect1 or aRect2
  void UnionRect(const Sub& aRect1, const Sub& aRect2)
  {
    *static_cast<Sub*>(this) = aRect1.Union(aRect2);
  }

  // Computes the smallest rectangle that contains both the points (including
  // edges) of both aRect1 and aRect2.
  // Thus, empty input rectangles are allowed to affect the result.
  //
  // 'this' can be the same object as either aRect1 or aRect2
  void UnionRectEdges(const Sub& aRect1, const Sub& aRect2)
  {
    *static_cast<Sub*>(this) = aRect1.UnionEdges(aRect2);
  }

  void SetRect(T aX, T aY, T aWidth, T aHeight)
  {
    x = aX; y = aY; width = aWidth; height = aHeight;
  }
  void SetRect(const Point& aPt, const SizeT& aSize)
  {
    SetRect(aPt.x, aPt.y, aSize.width, aSize.height);
  }
  void MoveTo(T aX, T aY) { x = aX; y = aY; }
  void MoveTo(const Point& aPoint) { x = aPoint.x; y = aPoint.y; }
  void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; }
  void MoveBy(const Point& aPoint) { x += aPoint.x; y += aPoint.y; }
  void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; }
  void SizeTo(const SizeT& aSize) { width = aSize.width; height = aSize.height; }

  void Inflate(T aD) { Inflate(aD, aD); }
  void Inflate(T aDx, T aDy)
  {
    x -= aDx;
    y -= aDy;
    width += 2 * aDx;
    height += 2 * aDy;
  }
  void Inflate(const Margin& aMargin)
  {
    x -= aMargin.left;
    y -= aMargin.top;
    width += aMargin.LeftRight();
    height += aMargin.TopBottom();
  }
  void Inflate(const SizeT& aSize) { Inflate(aSize.width, aSize.height); }

  void Deflate(T aD) { Deflate(aD, aD); }
  void Deflate(T aDx, T aDy)
  {
    x += aDx;
    y += aDy;
    width = gfx_max(T(0), width - 2 * aDx);
    height = gfx_max(T(0), height - 2 * aDy);
  }
  void Deflate(const Margin& aMargin)
  {
    x += aMargin.left;
    y += aMargin.top;
    width = gfx_max(T(0), width - aMargin.LeftRight());
    height = gfx_max(T(0), height - aMargin.TopBottom());
  }
  void Deflate(const SizeT& aSize) { Deflate(aSize.width, aSize.height); }

  // Return true if the rectangles contain the same set of points, including
  // points on the edges.
  // Use when we care about the exact x/y/width/height values being
  // equal (i.e. we care about differences in empty rectangles).
  bool IsEqualEdges(const Sub& aRect) const
  {
    return x == aRect.x && y == aRect.y &&
           width == aRect.width && height == aRect.height;
  }
  // Return true if the rectangles contain the same area of the plane.
  // Use when we do not care about differences in empty rectangles.
  bool IsEqualInterior(const Sub& aRect) const
  {
    return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty());
  }

  Sub operator+(const Point& aPoint) const
  {
    return Sub(x + aPoint.x, y + aPoint.y, width, height);
  }
  Sub operator-(const Point& aPoint) const
  {
    return Sub(x - aPoint.x, y - aPoint.y, width, height);
  }
  Sub& operator+=(const Point& aPoint)
  {
    MoveBy(aPoint);
    return *static_cast<Sub*>(this);
  }
  Sub& operator-=(const Point& aPoint)
  {
    MoveBy(-aPoint);
    return *static_cast<Sub*>(this);
  }

  // Find difference as a Margin
  Margin operator-(const Sub& aRect) const
  {
    return Margin(aRect.x - x, aRect.y - y,
                  XMost() - aRect.XMost(), YMost() - aRect.YMost());
  }

  // Helpers for accessing the vertices
  Point TopLeft() const { return Point(x, y); }
  Point TopRight() const { return Point(XMost(), y); }
  Point BottomLeft() const { return Point(x, YMost()); }
  Point BottomRight() const { return Point(XMost(), YMost()); }
  Point Center() const { return Point(x, y) + Point(width, height)/2; }
  SizeT Size() const { return SizeT(width, height); }

  // Helper methods for computing the extents
  T X() const { return x; }
  T Y() const { return y; }
  T Width() const { return width; }
  T Height() const { return height; }
  T XMost() const { return x + width; }
  T YMost() const { return y + height; }

  // Moves one edge of the rect without moving the opposite edge.
  void SetLeftEdge(T aX) {
    MOZ_ASSERT(aX <= XMost());
    width = XMost() - aX;
    x = aX;
  }
  void SetRightEdge(T aXMost) { 
    MOZ_ASSERT(aXMost >= x);
    width = aXMost - x; 
  }
  void SetTopEdge(T aY) {
    MOZ_ASSERT(aY <= YMost());
    height = YMost() - aY;
    y = aY;
  }
  void SetBottomEdge(T aYMost) { 
    MOZ_ASSERT(aYMost >= y);
    height = aYMost - y; 
  }

  // Round the rectangle edges to integer coordinates, such that the rounded
  // rectangle has the same set of pixel centers as the original rectangle.
  // Edges at offset 0.5 round up.
  // Suitable for most places where integral device coordinates
  // are needed, but note that any translation should be applied first to
  // avoid pixel rounding errors.
  // Note that this is *not* rounding to nearest integer if the values are negative.
  // They are always rounding as floor(n + 0.5).
  // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
  // If you need similar method which is using NS_round(), you should create
  // new |RoundAwayFromZero()| method.
  void Round()
  {
    T x0 = static_cast<T>(floor(T(X()) + 0.5));
    T y0 = static_cast<T>(floor(T(Y()) + 0.5));
    T x1 = static_cast<T>(floor(T(XMost()) + 0.5));
    T y1 = static_cast<T>(floor(T(YMost()) + 0.5));

    x = x0;
    y = y0;

    width = x1 - x0;
    height = y1 - y0;
  }

  // Snap the rectangle edges to integer coordinates, such that the
  // original rectangle contains the resulting rectangle.
  void RoundIn()
  {
    T x0 = static_cast<T>(ceil(T(X())));
    T y0 = static_cast<T>(ceil(T(Y())));
    T x1 = static_cast<T>(floor(T(XMost())));
    T y1 = static_cast<T>(floor(T(YMost())));

    x = x0;
    y = y0;

    width = x1 - x0;
    height = y1 - y0;
  }

  // Snap the rectangle edges to integer coordinates, such that the
  // resulting rectangle contains the original rectangle.
  void RoundOut()
  {
    T x0 = static_cast<T>(floor(T(X())));
    T y0 = static_cast<T>(floor(T(Y())));
    T x1 = static_cast<T>(ceil(T(XMost())));
    T y1 = static_cast<T>(ceil(T(YMost())));

    x = x0;
    y = y0;

    width = x1 - x0;
    height = y1 - y0;
  }

  // Scale 'this' by aScale, converting coordinates to integers so that the result is
  // the smallest integer-coordinate rectangle containing the unrounded result.
  // Note: this can turn an empty rectangle into a non-empty rectangle
  void ScaleRoundOut(double aScale) { ScaleRoundOut(aScale, aScale); }
  // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
  // that the result is the smallest integer-coordinate rectangle containing the
  // unrounded result.
  // Note: this can turn an empty rectangle into a non-empty rectangle
  void ScaleRoundOut(double aXScale, double aYScale)
  {
    T right = static_cast<T>(ceil(double(XMost()) * aXScale));
    T bottom = static_cast<T>(ceil(double(YMost()) * aYScale));
    x = static_cast<T>(floor(double(x) * aXScale));
    y = static_cast<T>(floor(double(y) * aYScale));
    width = right - x;
    height = bottom - y;
  }
  // Scale 'this' by aScale, converting coordinates to integers so that the result is
  // the largest integer-coordinate rectangle contained by the unrounded result.
  void ScaleRoundIn(double aScale) { ScaleRoundIn(aScale, aScale); }
  // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
  // that the result is the largest integer-coordinate rectangle contained by the
  // unrounded result.
  void ScaleRoundIn(double aXScale, double aYScale)
  {
    T right = static_cast<T>(floor(double(XMost()) * aXScale));
    T bottom = static_cast<T>(floor(double(YMost()) * aYScale));
    x = static_cast<T>(ceil(double(x) * aXScale));
    y = static_cast<T>(ceil(double(y) * aYScale));
    width = gfx_max<T>(0, right - x);
    height = gfx_max<T>(0, bottom - y);
  }
  // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
  // the smallest integer-coordinate rectangle containing the unrounded result.
  // Note: this can turn an empty rectangle into a non-empty rectangle
  void ScaleInverseRoundOut(double aScale) { ScaleInverseRoundOut(aScale, aScale); }
  // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
  // that the result is the smallest integer-coordinate rectangle containing the
  // unrounded result.
  // Note: this can turn an empty rectangle into a non-empty rectangle
  void ScaleInverseRoundOut(double aXScale, double aYScale)
  {
    T right = static_cast<T>(ceil(double(XMost()) / aXScale));
    T bottom = static_cast<T>(ceil(double(YMost()) / aYScale));
    x = static_cast<T>(floor(double(x) / aXScale));
    y = static_cast<T>(floor(double(y) / aYScale));
    width = right - x;
    height = bottom - y;
  }

  /**
   * Clamp aPoint to this rectangle. It is allowed to end up on any
   * edge of the rectangle.
   */
  Point ClampPoint(const Point& aPoint) const
  {
    return Point(NS_MAX(x, NS_MIN(XMost(), aPoint.x)),
                 NS_MAX(y, NS_MIN(YMost(), aPoint.y)));
  }

private:
  // Do not use the default operator== or operator!= !
  // Use IsEqualEdges or IsEqualInterior explicitly.
  bool operator==(const Sub& aRect) const { return false; }
  bool operator!=(const Sub& aRect) const { return false; }
};

}
}

#endif /* MOZILLA_GFX_BASERECT_H_ */