1/*
2 * Copyright (c) 2012, Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#ifndef LayoutRect_h
32#define LayoutRect_h
33
34#include "FloatRect.h"
35#include "IntRect.h"
36#include "LayoutBoxExtent.h"
37#include "LayoutPoint.h"
38#include <wtf/Vector.h>
39
40namespace WebCore {
41
42class LayoutRect {
43public:
44    LayoutRect() { }
45    LayoutRect(const LayoutPoint& location, const LayoutSize& size)
46        : m_location(location), m_size(size) { }
47    LayoutRect(LayoutUnit x, LayoutUnit y, LayoutUnit width, LayoutUnit height)
48        : m_location(LayoutPoint(x, y)), m_size(LayoutSize(width, height)) { }
49    LayoutRect(const FloatPoint& location, const FloatSize& size)
50        : m_location(location), m_size(size) { }
51    LayoutRect(const IntRect& rect) : m_location(rect.location()), m_size(rect.size()) { }
52
53    explicit LayoutRect(const FloatRect&); // don't do this implicitly since it's lossy
54
55    LayoutPoint location() const { return m_location; }
56    LayoutSize size() const { return m_size; }
57
58    IntPoint pixelSnappedLocation() const { return roundedIntPoint(m_location); }
59    IntSize pixelSnappedSize() const { return IntSize(snapSizeToPixel(m_size.width(), m_location.x()), snapSizeToPixel(m_size.height(), m_location.y())); }
60
61    void setLocation(const LayoutPoint& location) { m_location = location; }
62    void setSize(const LayoutSize& size) { m_size = size; }
63
64    LayoutUnit x() const { return m_location.x(); }
65    LayoutUnit y() const { return m_location.y(); }
66    LayoutUnit maxX() const { return x() + width(); }
67    LayoutUnit maxY() const { return y() + height(); }
68    LayoutUnit width() const { return m_size.width(); }
69    LayoutUnit height() const { return m_size.height(); }
70
71    int pixelSnappedX() const { return x().round(); }
72    int pixelSnappedY() const { return y().round(); }
73    int pixelSnappedWidth() const { return snapSizeToPixel(width(), x()); }
74    int pixelSnappedHeight() const { return snapSizeToPixel(height(), y()); }
75    int pixelSnappedMaxX() const { return (m_location.x() + m_size.width()).round(); }
76    int pixelSnappedMaxY() const { return (m_location.y() + m_size.height()).round(); }
77
78    void setX(LayoutUnit x) { m_location.setX(x); }
79    void setY(LayoutUnit y) { m_location.setY(y); }
80    void setWidth(LayoutUnit width) { m_size.setWidth(width); }
81    void setHeight(LayoutUnit height) { m_size.setHeight(height); }
82
83    bool isEmpty() const { return m_size.isEmpty(); }
84
85    // NOTE: The result is rounded to integer values, and thus may be not the exact
86    // center point.
87    LayoutPoint center() const { return LayoutPoint(x() + width() / 2, y() + height() / 2); }
88
89    void move(const LayoutSize& size) { m_location += size; }
90    void moveBy(const LayoutPoint& offset) { m_location.move(offset.x(), offset.y()); }
91    void move(LayoutUnit dx, LayoutUnit dy) { m_location.move(dx, dy); }
92
93    void expand(const LayoutSize& size) { m_size += size; }
94    void expand(const LayoutBoxExtent& box)
95    {
96        m_location.move(-box.left(), -box.top());
97        m_size.expand(box.left() + box.right(), box.top() + box.bottom());
98    }
99    void expand(LayoutUnit dw, LayoutUnit dh) { m_size.expand(dw, dh); }
100    void contract(const LayoutSize& size) { m_size -= size; }
101    void contract(const LayoutBoxExtent& box)
102    {
103        m_location.move(box.left(), box.top());
104        m_size.shrink(box.left() + box.right(), box.top() + box.bottom());
105    }
106    void contract(LayoutUnit dw, LayoutUnit dh) { m_size.expand(-dw, -dh); }
107
108    void shiftXEdgeTo(LayoutUnit edge)
109    {
110        LayoutUnit delta = edge - x();
111        setX(edge);
112        setWidth(std::max<LayoutUnit>(0, width() - delta));
113    }
114    void shiftMaxXEdgeTo(LayoutUnit edge)
115    {
116        LayoutUnit delta = edge - maxX();
117        setWidth(std::max<LayoutUnit>(0, width() + delta));
118    }
119    void shiftYEdgeTo(LayoutUnit edge)
120    {
121        LayoutUnit delta = edge - y();
122        setY(edge);
123        setHeight(std::max<LayoutUnit>(0, height() - delta));
124    }
125    void shiftMaxYEdgeTo(LayoutUnit edge)
126    {
127        LayoutUnit delta = edge - maxY();
128        setHeight(std::max<LayoutUnit>(0, height() + delta));
129    }
130
131    LayoutPoint minXMinYCorner() const { return m_location; } // typically topLeft
132    LayoutPoint maxXMinYCorner() const { return LayoutPoint(m_location.x() + m_size.width(), m_location.y()); } // typically topRight
133    LayoutPoint minXMaxYCorner() const { return LayoutPoint(m_location.x(), m_location.y() + m_size.height()); } // typically bottomLeft
134    LayoutPoint maxXMaxYCorner() const { return LayoutPoint(m_location.x() + m_size.width(), m_location.y() + m_size.height()); } // typically bottomRight
135
136    bool intersects(const LayoutRect&) const;
137    bool contains(const LayoutRect&) const;
138
139    // This checks to see if the rect contains x,y in the traditional sense.
140    // Equivalent to checking if the rect contains a 1x1 rect below and to the right of (px,py).
141    bool contains(LayoutUnit px, LayoutUnit py) const
142        { return px >= x() && px < maxX() && py >= y() && py < maxY(); }
143    bool contains(const LayoutPoint& point) const { return contains(point.x(), point.y()); }
144
145    void intersect(const LayoutRect&);
146    void unite(const LayoutRect&);
147    void uniteIfNonZero(const LayoutRect&);
148
149    void inflateX(LayoutUnit dx)
150    {
151        m_location.setX(m_location.x() - dx);
152        m_size.setWidth(m_size.width() + dx + dx);
153    }
154    void inflateY(LayoutUnit dy)
155    {
156        m_location.setY(m_location.y() - dy);
157        m_size.setHeight(m_size.height() + dy + dy);
158    }
159    void inflate(LayoutUnit d) { inflateX(d); inflateY(d); }
160    void scale(float s);
161    void scale(float xScale, float yScale);
162
163    LayoutRect transposedRect() const { return LayoutRect(m_location.transposedPoint(), m_size.transposedSize()); }
164
165    static LayoutRect infiniteRect()
166    {
167        // Return a rect that is slightly smaller than the true max rect to allow pixelSnapping to round up to the nearest IntRect without overflowing.
168        return LayoutRect(LayoutUnit::nearlyMin() / 2, LayoutUnit::nearlyMin() / 2, LayoutUnit::nearlyMax(), LayoutUnit::nearlyMax());
169    }
170
171    operator FloatRect() const { return FloatRect(m_location, m_size); }
172
173private:
174    LayoutPoint m_location;
175    LayoutSize m_size;
176};
177
178inline LayoutRect intersection(const LayoutRect& a, const LayoutRect& b)
179{
180    LayoutRect c = a;
181    c.intersect(b);
182    return c;
183}
184
185inline LayoutRect unionRect(const LayoutRect& a, const LayoutRect& b)
186{
187    LayoutRect c = a;
188    c.unite(b);
189    return c;
190}
191
192LayoutRect unionRect(const Vector<LayoutRect>&);
193
194inline bool operator==(const LayoutRect& a, const LayoutRect& b)
195{
196    return a.location() == b.location() && a.size() == b.size();
197}
198
199inline bool operator!=(const LayoutRect& a, const LayoutRect& b)
200{
201    return a.location() != b.location() || a.size() != b.size();
202}
203
204inline IntRect pixelSnappedIntRect(const LayoutRect& rect)
205{
206#if ENABLE(SUBPIXEL_LAYOUT)
207    return IntRect(roundedIntPoint(rect.location()), IntSize(snapSizeToPixel(rect.width(), rect.x()),
208                                                             snapSizeToPixel(rect.height(), rect.y())));
209
210#else
211    return IntRect(rect);
212#endif
213}
214
215IntRect enclosingIntRect(const LayoutRect&);
216LayoutRect enclosingLayoutRect(const FloatRect&);
217FloatRect enclosingRectForPainting(const LayoutRect&, float pixelSnappingFactor);
218
219inline IntRect pixelSnappedIntRect(LayoutUnit left, LayoutUnit top, LayoutUnit width, LayoutUnit height)
220{
221    return IntRect(left.round(), top.round(), snapSizeToPixel(width, left), snapSizeToPixel(height, top));
222}
223
224inline IntRect pixelSnappedIntRectFromEdges(LayoutUnit left, LayoutUnit top, LayoutUnit right, LayoutUnit bottom)
225{
226    return IntRect(left.round(), top.round(), snapSizeToPixel(right - left, left), snapSizeToPixel(bottom - top, top));
227}
228
229inline IntRect pixelSnappedIntRect(LayoutPoint location, LayoutSize size)
230{
231    return IntRect(roundedIntPoint(location), pixelSnappedIntSize(size, location));
232}
233
234inline FloatRect pixelSnappedForPainting(const LayoutRect& rect, float pixelSnappingFactor)
235{
236#if ENABLE(SUBPIXEL_LAYOUT)
237    return FloatRect(roundToDevicePixel(rect.x(), pixelSnappingFactor), roundToDevicePixel(rect.y(), pixelSnappingFactor),
238        snapSizeToDevicePixel(rect.width(), rect.x(), pixelSnappingFactor), snapSizeToDevicePixel(rect.height(), rect.y(), pixelSnappingFactor));
239#else
240    UNUSED_PARAM(pixelSnappingFactor);
241    return FloatRect(rect);
242#endif
243}
244
245inline FloatRect pixelSnappedForPainting(LayoutUnit x, LayoutUnit y, LayoutUnit width, LayoutUnit height, float pixelSnappingFactor)
246{
247    return pixelSnappedForPainting(LayoutRect(x, y, width, height), pixelSnappingFactor);
248}
249
250// FIXME: This needs to take vertical centering into account too.
251inline FloatRect directionalPixelSnappedForPainting(const LayoutRect& rect, float deviceScaleFactor, bool ltr)
252{
253    if (!ltr) {
254        FloatPoint snappedTopRight = roundedForPainting(rect.maxXMinYCorner(), deviceScaleFactor, ltr);
255        float snappedWidth = snapSizeToDevicePixel(rect.width(), rect.maxX(), deviceScaleFactor);
256        float snappedHeight = snapSizeToDevicePixel(rect.height(), rect.y(), deviceScaleFactor);
257        return FloatRect(snappedTopRight.x() - snappedWidth, snappedTopRight.y(), snappedWidth, snappedHeight);
258    }
259    return pixelSnappedForPainting(rect, deviceScaleFactor);
260}
261
262} // namespace WebCore
263
264#endif // LayoutRect_h
265