// Copyright 2014 PDFium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #ifndef CORE_FXCRT_FX_COORDINATES_H_ #define CORE_FXCRT_FX_COORDINATES_H_ #include <algorithm> #include <tuple> #include "core/fxcrt/fx_system.h" #include "third_party/base/numerics/safe_math.h" class CFX_Matrix; template <class BaseType> class CFX_PTemplate { public: CFX_PTemplate() : x(0), y(0) {} CFX_PTemplate(BaseType new_x, BaseType new_y) : x(new_x), y(new_y) {} CFX_PTemplate(const CFX_PTemplate& other) : x(other.x), y(other.y) {} CFX_PTemplate operator=(const CFX_PTemplate& other) { if (this != &other) { x = other.x; y = other.y; } return *this; } bool operator==(const CFX_PTemplate& other) const { return x == other.x && y == other.y; } bool operator!=(const CFX_PTemplate& other) const { return !(*this == other); } CFX_PTemplate& operator+=(const CFX_PTemplate<BaseType>& obj) { x += obj.x; y += obj.y; return *this; } CFX_PTemplate& operator-=(const CFX_PTemplate<BaseType>& obj) { x -= obj.x; y -= obj.y; return *this; } CFX_PTemplate operator+(const CFX_PTemplate& other) const { return CFX_PTemplate(x + other.x, y + other.y); } CFX_PTemplate operator-(const CFX_PTemplate& other) const { return CFX_PTemplate(x - other.x, y - other.y); } BaseType x; BaseType y; }; using CFX_Point = CFX_PTemplate<int32_t>; using CFX_PointF = CFX_PTemplate<float>; template <class BaseType> class CFX_STemplate { public: CFX_STemplate() : width(0), height(0) {} CFX_STemplate(BaseType new_width, BaseType new_height) : width(new_width), height(new_height) {} CFX_STemplate(const CFX_STemplate& other) : width(other.width), height(other.height) {} template <typename OtherType> CFX_STemplate<OtherType> As() const { return CFX_STemplate<OtherType>(static_cast<OtherType>(width), static_cast<OtherType>(height)); } void clear() { width = 0; height = 0; } CFX_STemplate operator=(const CFX_STemplate& other) { if (this != &other) { width = other.width; height = other.height; } return *this; } bool operator==(const CFX_STemplate& other) const { return width == other.width && height == other.height; } bool operator!=(const CFX_STemplate& other) const { return !(*this == other); } CFX_STemplate& operator+=(const CFX_STemplate<BaseType>& obj) { width += obj.width; height += obj.height; return *this; } CFX_STemplate& operator-=(const CFX_STemplate<BaseType>& obj) { width -= obj.width; height -= obj.height; return *this; } CFX_STemplate& operator*=(BaseType factor) { width *= factor; height *= factor; return *this; } CFX_STemplate& operator/=(BaseType divisor) { width /= divisor; height /= divisor; return *this; } CFX_STemplate operator+(const CFX_STemplate& other) const { return CFX_STemplate(width + other.width, height + other.height); } CFX_STemplate operator-(const CFX_STemplate& other) const { return CFX_STemplate(width - other.width, height - other.height); } CFX_STemplate operator*(BaseType factor) const { return CFX_STemplate(width * factor, height * factor); } CFX_STemplate operator/(BaseType divisor) const { return CFX_STemplate(width / divisor, height / divisor); } BaseType width; BaseType height; }; using CFX_Size = CFX_STemplate<int32_t>; using CFX_SizeF = CFX_STemplate<float>; template <class BaseType> class CFX_VTemplate : public CFX_PTemplate<BaseType> { public: using CFX_PTemplate<BaseType>::x; using CFX_PTemplate<BaseType>::y; CFX_VTemplate() : CFX_PTemplate<BaseType>() {} CFX_VTemplate(BaseType new_x, BaseType new_y) : CFX_PTemplate<BaseType>(new_x, new_y) {} CFX_VTemplate(const CFX_VTemplate& other) : CFX_PTemplate<BaseType>(other) {} CFX_VTemplate(const CFX_PTemplate<BaseType>& point1, const CFX_PTemplate<BaseType>& point2) : CFX_PTemplate<BaseType>(point2.x - point1.x, point2.y - point1.y) {} float Length() const { return sqrt(x * x + y * y); } void Normalize() { float fLen = Length(); if (fLen < 0.0001f) return; x /= fLen; y /= fLen; } void Translate(BaseType dx, BaseType dy) { x += dx; y += dy; } void Scale(BaseType sx, BaseType sy) { x *= sx; y *= sy; } void Rotate(float fRadian) { float cosValue = cos(fRadian); float sinValue = sin(fRadian); x = x * cosValue - y * sinValue; y = x * sinValue + y * cosValue; } }; using CFX_Vector = CFX_VTemplate<int32_t>; using CFX_VectorF = CFX_VTemplate<float>; // Rectangles. // TODO(tsepez): Consolidate all these different rectangle classes. // LTRB rectangles (y-axis runs downwards). struct FX_RECT { FX_RECT() : left(0), top(0), right(0), bottom(0) {} FX_RECT(int l, int t, int r, int b) : left(l), top(t), right(r), bottom(b) {} int Width() const { return right - left; } int Height() const { return bottom - top; } bool IsEmpty() const { return right <= left || bottom <= top; } bool Valid() const { pdfium::base::CheckedNumeric<int> w = right; pdfium::base::CheckedNumeric<int> h = bottom; w -= left; h -= top; return w.IsValid() && h.IsValid(); } void Normalize(); void Intersect(const FX_RECT& src); void Intersect(int l, int t, int r, int b) { Intersect(FX_RECT(l, t, r, b)); } void Offset(int dx, int dy) { left += dx; right += dx; top += dy; bottom += dy; } bool operator==(const FX_RECT& src) const { return left == src.left && right == src.right && top == src.top && bottom == src.bottom; } bool Contains(int x, int y) const { return x >= left && x < right && y >= top && y < bottom; } int32_t left; int32_t top; int32_t right; int32_t bottom; }; // LTRB rectangles (y-axis runs upwards). class CFX_FloatRect { public: CFX_FloatRect() : CFX_FloatRect(0.0f, 0.0f, 0.0f, 0.0f) {} CFX_FloatRect(float l, float b, float r, float t) : left(l), bottom(b), right(r), top(t) {} explicit CFX_FloatRect(const float* pArray) : CFX_FloatRect(pArray[0], pArray[1], pArray[2], pArray[3]) {} explicit CFX_FloatRect(const FX_RECT& rect); static CFX_FloatRect GetBBox(const CFX_PointF* pPoints, int nPoints); void Normalize(); void Reset(); bool IsEmpty() const { return left >= right || bottom >= top; } bool Contains(const CFX_PointF& point) const; bool Contains(const CFX_FloatRect& other_rect) const; void Intersect(const CFX_FloatRect& other_rect); void Union(const CFX_FloatRect& other_rect); // These may be better at rounding than ToFxRect() and friends. // // Returned rect has bounds rounded up/down such that it is contained in the // original. FX_RECT GetInnerRect() const; // Returned rect has bounds rounded up/down such that the original is // contained in it. FX_RECT GetOuterRect() const; // Returned rect has bounds rounded up/down such that the dimensions are // rounded up and the sum of the error in the bounds is minimized. FX_RECT GetClosestRect() const; CFX_FloatRect GetCenterSquare() const; void InitRect(const CFX_PointF& point) { left = point.x; right = point.x; bottom = point.y; top = point.y; } void UpdateRect(const CFX_PointF& point); float Width() const { return right - left; } float Height() const { return top - bottom; } void Inflate(float x, float y) { Normalize(); left -= x; right += x; bottom -= y; top += y; } void Inflate(float other_left, float other_bottom, float other_right, float other_top) { Normalize(); left -= other_left; bottom -= other_bottom; right += other_right; top += other_top; } void Inflate(const CFX_FloatRect& rt) { Inflate(rt.left, rt.bottom, rt.right, rt.top); } void Deflate(float x, float y) { Normalize(); left += x; right -= x; bottom += y; top -= y; } void Deflate(float other_left, float other_bottom, float other_right, float other_top) { Normalize(); left += other_left; bottom += other_bottom; right -= other_right; top -= other_top; } void Deflate(const CFX_FloatRect& rt) { Deflate(rt.left, rt.bottom, rt.right, rt.top); } CFX_FloatRect GetDeflated(float x, float y) const { if (IsEmpty()) return CFX_FloatRect(); CFX_FloatRect that = *this; that.Deflate(x, y); that.Normalize(); return that; } void Translate(float e, float f) { left += e; right += e; top += f; bottom += f; } void Scale(float fScale); void ScaleFromCenterPoint(float fScale); // GetInnerRect() and friends may be better at rounding than these methods. // Unlike the methods above, these two blindly floor / round the LBRT values. // Doing so may introduce rounding errors that are visible to users as // off-by-one pixels/lines. // // Floors LBRT values. FX_RECT ToFxRect() const; // Rounds LBRT values. FX_RECT ToRoundedFxRect() const; float left; float bottom; float right; float top; }; #ifndef NDEBUG std::ostream& operator<<(std::ostream& os, const CFX_FloatRect& rect); #endif // LTWH rectangles (y-axis runs downwards). template <class BaseType> class CFX_RTemplate { public: using PointType = CFX_PTemplate<BaseType>; using SizeType = CFX_STemplate<BaseType>; using VectorType = CFX_VTemplate<BaseType>; using RectType = CFX_RTemplate<BaseType>; CFX_RTemplate() : left(0), top(0), width(0), height(0) {} CFX_RTemplate(BaseType dst_left, BaseType dst_top, BaseType dst_width, BaseType dst_height) : left(dst_left), top(dst_top), width(dst_width), height(dst_height) {} CFX_RTemplate(BaseType dst_left, BaseType dst_top, const SizeType& dst_size) : left(dst_left), top(dst_top), width(dst_size.width), height(dst_size.height) {} CFX_RTemplate(const PointType& p, BaseType dst_width, BaseType dst_height) : left(p.x), top(p.y), width(dst_width), height(dst_height) {} CFX_RTemplate(const PointType& p1, const SizeType& s2) : left(p1.x), top(p1.y), width(s2.width), height(s2.height) {} CFX_RTemplate(const PointType& p1, const PointType& p2) : left(p1.x), top(p1.y), width(p2.width - p1.width), height(p2.height - p1.height) { Normalize(); } CFX_RTemplate(const PointType& p, const VectorType& v) : left(p.x), top(p.y), width(v.x), height(v.y) { Normalize(); } explicit CFX_RTemplate(const CFX_FloatRect& r) : left(static_cast<BaseType>(r.left)), top(static_cast<BaseType>(r.top)), width(static_cast<BaseType>(r.Width())), height(static_cast<BaseType>(r.Height())) {} // NOLINTNEXTLINE(runtime/explicit) CFX_RTemplate(const RectType& other) : left(other.left), top(other.top), width(other.width), height(other.height) {} template <typename OtherType> CFX_RTemplate<OtherType> As() const { return CFX_RTemplate<OtherType>( static_cast<OtherType>(left), static_cast<OtherType>(top), static_cast<OtherType>(width), static_cast<OtherType>(height)); } void Reset() { left = 0; top = 0; width = 0; height = 0; } RectType& operator+=(const PointType& p) { left += p.x; top += p.y; return *this; } RectType& operator-=(const PointType& p) { left -= p.x; top -= p.y; return *this; } BaseType right() const { return left + width; } BaseType bottom() const { return top + height; } void Normalize() { if (width < 0) { left += width; width = -width; } if (height < 0) { top += height; height = -height; } } void Offset(BaseType dx, BaseType dy) { left += dx; top += dy; } void Inflate(BaseType x, BaseType y) { left -= x; width += x * 2; top -= y; height += y * 2; } void Inflate(const PointType& p) { Inflate(p.x, p.y); } void Inflate(BaseType off_left, BaseType off_top, BaseType off_right, BaseType off_bottom) { left -= off_left; top -= off_top; width += off_left + off_right; height += off_top + off_bottom; } void Inflate(const RectType& rt) { Inflate(rt.left, rt.top, rt.left + rt.width, rt.top + rt.height); } void Deflate(BaseType x, BaseType y) { left += x; width -= x * 2; top += y; height -= y * 2; } void Deflate(const PointType& p) { Deflate(p.x, p.y); } void Deflate(BaseType off_left, BaseType off_top, BaseType off_right, BaseType off_bottom) { left += off_left; top += off_top; width -= off_left + off_right; height -= off_top + off_bottom; } void Deflate(const RectType& rt) { Deflate(rt.left, rt.top, rt.top + rt.width, rt.top + rt.height); } bool IsEmpty() const { return width <= 0 || height <= 0; } bool IsEmpty(float fEpsilon) const { return width <= fEpsilon || height <= fEpsilon; } void Empty() { width = height = 0; } bool Contains(const PointType& p) const { return p.x >= left && p.x < left + width && p.y >= top && p.y < top + height; } bool Contains(const RectType& rt) const { return rt.left >= left && rt.right() <= right() && rt.top >= top && rt.bottom() <= bottom(); } BaseType Width() const { return width; } BaseType Height() const { return height; } SizeType Size() const { return SizeType(width, height); } PointType TopLeft() const { return PointType(left, top); } PointType TopRight() const { return PointType(left + width, top); } PointType BottomLeft() const { return PointType(left, top + height); } PointType BottomRight() const { return PointType(left + width, top + height); } PointType Center() const { return PointType(left + width / 2, top + height / 2); } void Union(BaseType x, BaseType y) { BaseType r = right(); BaseType b = bottom(); left = std::min(left, x); top = std::min(top, y); r = std::max(r, x); b = std::max(b, y); width = r - left; height = b - top; } void Union(const PointType& p) { Union(p.x, p.y); } void Union(const RectType& rt) { BaseType r = right(); BaseType b = bottom(); left = std::min(left, rt.left); top = std::min(top, rt.top); r = std::max(r, rt.right()); b = std::max(b, rt.bottom()); width = r - left; height = b - top; } void Intersect(const RectType& rt) { BaseType r = right(); BaseType b = bottom(); left = std::max(left, rt.left); top = std::max(top, rt.top); r = std::min(r, rt.right()); b = std::min(b, rt.bottom()); width = r - left; height = b - top; } bool IntersectWith(const RectType& rt) const { RectType rect = rt; rect.Intersect(*this); return !rect.IsEmpty(); } bool IntersectWith(const RectType& rt, float fEpsilon) const { RectType rect = rt; rect.Intersect(*this); return !rect.IsEmpty(fEpsilon); } friend bool operator==(const RectType& rc1, const RectType& rc2) { return rc1.left == rc2.left && rc1.top == rc2.top && rc1.width == rc2.width && rc1.height == rc2.height; } friend bool operator!=(const RectType& rc1, const RectType& rc2) { return !(rc1 == rc2); } CFX_FloatRect ToFloatRect() const { // Note, we flip top/bottom here because the CFX_FloatRect has the // y-axis running in the opposite direction. return CFX_FloatRect(left, top, right(), bottom()); } BaseType left; BaseType top; BaseType width; BaseType height; }; using CFX_Rect = CFX_RTemplate<int32_t>; using CFX_RectF = CFX_RTemplate<float>; // The matrix is of the form: // | a b 0 | // | c d 0 | // | e f 1 | // See PDF spec 1.7 Section 4.2.3. // class CFX_Matrix { public: CFX_Matrix() { SetIdentity(); } explicit CFX_Matrix(const float n[6]) : a(n[0]), b(n[1]), c(n[2]), d(n[3]), e(n[4]), f(n[5]) {} CFX_Matrix(const CFX_Matrix& other) = default; CFX_Matrix(float a1, float b1, float c1, float d1, float e1, float f1) : a(a1), b(b1), c(c1), d(d1), e(e1), f(f1) {} void operator=(const CFX_Matrix& other) { a = other.a; b = other.b; c = other.c; d = other.d; e = other.e; f = other.f; } void SetIdentity() { a = 1; b = 0; c = 0; d = 1; e = 0; f = 0; } CFX_Matrix GetInverse() const; void Concat(const CFX_Matrix& m, bool bPrepended = false); void ConcatInverse(const CFX_Matrix& m, bool bPrepended = false); bool IsIdentity() const { return a == 1 && b == 0 && c == 0 && d == 1 && e == 0 && f == 0; } bool Is90Rotated() const; bool IsScaled() const; bool WillScale() const { return a != 1.0f || b != 0 || c != 0 || d != 1.0f; } void Translate(float x, float y, bool bPrepended = false); void Translate(int32_t x, int32_t y, bool bPrepended = false) { Translate(static_cast<float>(x), static_cast<float>(y), bPrepended); } void Scale(float sx, float sy, bool bPrepended = false); void Rotate(float fRadian, bool bPrepended = false); void RotateAt(float fRadian, float x, float y, bool bPrepended = false); void Shear(float fAlphaRadian, float fBetaRadian, bool bPrepended = false); void MatchRect(const CFX_FloatRect& dest, const CFX_FloatRect& src); float GetXUnit() const; float GetYUnit() const; CFX_FloatRect GetUnitRect() const; float TransformXDistance(float dx) const; float TransformDistance(float distance) const; CFX_PointF Transform(const CFX_PointF& point) const; std::tuple<float, float, float, float> TransformRect( const float& left, const float& right, const float& top, const float& bottom) const; CFX_RectF TransformRect(const CFX_RectF& rect) const; CFX_FloatRect TransformRect(const CFX_FloatRect& rect) const; float a; float b; float c; float d; float e; float f; private: void ConcatInternal(const CFX_Matrix& other, bool prepend); }; #endif // CORE_FXCRT_FX_COORDINATES_H_