1/* 2 * Copyright (C) 2012 Adobe Systems Incorporated. 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 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above 9 * copyright notice, this list of conditions and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following 13 * disclaimer in the documentation and/or other materials 14 * provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 20 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 27 * OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include "config.h" 31#include "RectangleShape.h" 32 33#include <wtf/MathExtras.h> 34 35namespace WebCore { 36 37static inline float ellipseXIntercept(float y, float rx, float ry) 38{ 39 ASSERT(ry > 0); 40 return rx * sqrt(1 - (y * y) / (ry * ry)); 41} 42 43static inline float ellipseYIntercept(float x, float rx, float ry) 44{ 45 ASSERT(rx > 0); 46 return ry * sqrt(1 - (x * x) / (rx * rx)); 47} 48 49FloatRoundedRect FloatRoundedRect::paddingBounds(float padding) const 50{ 51 ASSERT(padding >= 0); 52 if (!padding || isEmpty()) 53 return *this; 54 55 float boundsX = x() + std::min(width() / 2, padding); 56 float boundsY = y() + std::min(height() / 2, padding); 57 float boundsWidth = std::max(0.0f, width() - padding * 2); 58 float boundsHeight = std::max(0.0f, height() - padding * 2); 59 float boundsRadiusX = std::max(0.0f, rx() - padding); 60 float boundsRadiusY = std::max(0.0f, ry() - padding); 61 return FloatRoundedRect(FloatRect(boundsX, boundsY, boundsWidth, boundsHeight), FloatSize(boundsRadiusX, boundsRadiusY)); 62} 63 64FloatRoundedRect FloatRoundedRect::marginBounds(float margin) const 65{ 66 ASSERT(margin >= 0); 67 if (!margin) 68 return *this; 69 70 float boundsX = x() - margin; 71 float boundsY = y() - margin; 72 float boundsWidth = width() + margin * 2; 73 float boundsHeight = height() + margin * 2; 74 float boundsRadiusX = rx() + margin; 75 float boundsRadiusY = ry() + margin; 76 return FloatRoundedRect(FloatRect(boundsX, boundsY, boundsWidth, boundsHeight), FloatSize(boundsRadiusX, boundsRadiusY)); 77} 78 79FloatPoint FloatRoundedRect::cornerInterceptForWidth(float widthAtIntercept) const 80{ 81 float xi = (width() - widthAtIntercept) / 2; 82 float yi = ry() - ellipseYIntercept(rx() - xi, rx(), ry()); 83 return FloatPoint(xi, yi); 84} 85 86FloatRoundedRect RectangleShape::shapePaddingBounds() const 87{ 88 if (!m_haveInitializedPaddingBounds) { 89 m_haveInitializedPaddingBounds = true; 90 m_paddingBounds = m_bounds.paddingBounds(shapePadding()); 91 } 92 return m_paddingBounds; 93} 94 95FloatRoundedRect RectangleShape::shapeMarginBounds() const 96{ 97 if (!m_haveInitializedMarginBounds) { 98 m_haveInitializedMarginBounds = true; 99 m_marginBounds = m_bounds.marginBounds(shapeMargin()); 100 } 101 return m_marginBounds; 102} 103 104void RectangleShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const 105{ 106 const FloatRoundedRect& bounds = shapeMarginBounds(); 107 if (bounds.isEmpty()) 108 return; 109 110 float y1 = logicalTop; 111 float y2 = logicalTop + logicalHeight; 112 113 if (y2 < bounds.y() || y1 >= bounds.maxY()) 114 return; 115 116 float x1 = bounds.x(); 117 float x2 = bounds.maxX(); 118 119 if (bounds.ry() > 0) { 120 if (y2 < bounds.y() + bounds.ry()) { 121 float yi = y2 - bounds.y() - bounds.ry(); 122 float xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry()); 123 x1 = bounds.x() + bounds.rx() - xi; 124 x2 = bounds.maxX() - bounds.rx() + xi; 125 } else if (y1 > bounds.maxY() - bounds.ry()) { 126 float yi = y1 - (bounds.maxY() - bounds.ry()); 127 float xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry()); 128 x1 = bounds.x() + bounds.rx() - xi; 129 x2 = bounds.maxX() - bounds.rx() + xi; 130 } 131 } 132 133 result.append(LineSegment(x1, x2)); 134} 135 136void RectangleShape::getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const 137{ 138 const FloatRoundedRect& bounds = shapePaddingBounds(); 139 if (bounds.isEmpty()) 140 return; 141 142 float y1 = logicalTop; 143 float y2 = logicalTop + logicalHeight; 144 145 if (y1 < bounds.y() || y2 > bounds.maxY()) 146 return; 147 148 float x1 = bounds.x(); 149 float x2 = bounds.maxX(); 150 151 if (bounds.ry() > 0) { 152 bool y1InterceptsCorner = y1 < bounds.y() + bounds.ry(); 153 bool y2InterceptsCorner = y2 > bounds.maxY() - bounds.ry(); 154 float xi = 0; 155 156 if (y1InterceptsCorner && y2InterceptsCorner) { 157 if (y1 < bounds.height() + 2 * bounds.y() - y2) { 158 float yi = y1 - bounds.y() - bounds.ry(); 159 xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry()); 160 } else { 161 float yi = y2 - (bounds.maxY() - bounds.ry()); 162 xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry()); 163 } 164 } else if (y1InterceptsCorner) { 165 float yi = y1 - bounds.y() - bounds.ry(); 166 xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry()); 167 } else if (y2InterceptsCorner) { 168 float yi = y2 - (bounds.maxY() - bounds.ry()); 169 xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry()); 170 } 171 172 if (y1InterceptsCorner || y2InterceptsCorner) { 173 x1 = bounds.x() + bounds.rx() - xi; 174 x2 = bounds.maxX() - bounds.rx() + xi; 175 } 176 } 177 178 result.append(LineSegment(x1, x2)); 179} 180 181bool RectangleShape::firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const LayoutSize& minLogicalIntervalSize, LayoutUnit& result) const 182{ 183 float minIntervalTop = minLogicalIntervalTop; 184 float minIntervalHeight = minLogicalIntervalSize.height(); 185 float minIntervalWidth = minLogicalIntervalSize.width(); 186 187 const FloatRoundedRect& bounds = shapePaddingBounds(); 188 if (bounds.isEmpty() || minIntervalWidth > bounds.width()) 189 return false; 190 191 float minY = std::max(bounds.y(), minIntervalTop); 192 float maxY = minY + minIntervalHeight; 193 194 if (maxY > bounds.maxY()) 195 return false; 196 197 bool intervalOverlapsMinCorner = minY < bounds.y() + bounds.ry(); 198 bool intervalOverlapsMaxCorner = maxY > bounds.maxY() - bounds.ry(); 199 200 if (!intervalOverlapsMinCorner && !intervalOverlapsMaxCorner) { 201 result = minY; 202 return true; 203 } 204 205 float centerY = bounds.y() + bounds.height() / 2; 206 bool minCornerDefinesX = fabs(centerY - minY) > fabs(centerY - maxY); 207 bool intervalFitsWithinCorners = minIntervalWidth + 2 * bounds.rx() <= bounds.width(); 208 FloatPoint cornerIntercept = bounds.cornerInterceptForWidth(minIntervalWidth); 209 210 if (intervalOverlapsMinCorner && (!intervalOverlapsMaxCorner || minCornerDefinesX)) { 211 if (intervalFitsWithinCorners || bounds.y() + cornerIntercept.y() < minY) { 212 result = minY; 213 return true; 214 } 215 if (minIntervalHeight < bounds.height() - (2 * cornerIntercept.y())) { 216 result = ceiledLayoutUnit(bounds.y() + cornerIntercept.y()); 217 return true; 218 } 219 } 220 221 if (intervalOverlapsMaxCorner && (!intervalOverlapsMinCorner || !minCornerDefinesX)) { 222 if (intervalFitsWithinCorners || minY <= bounds.maxY() - cornerIntercept.y() - minIntervalHeight) { 223 result = minY; 224 return true; 225 } 226 } 227 228 return false; 229} 230 231} // namespace WebCore 232