1 /* 2 * Copyright (C) 2003, 2006, 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2010 Google Inc. All rights reserved. 4 * Copyright (C) 2013 Xidorn Quan (quanxunzhen (at) gmail.com) 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "config.h" 29 #include "platform/geometry/RoundedRect.h" 30 31 #include "wtf/Assertions.h" 32 #include <algorithm> 33 34 using namespace std; 35 36 namespace WebCore { 37 38 bool RoundedRect::Radii::isZero() const 39 { 40 return m_topLeft.isZero() && m_topRight.isZero() && m_bottomLeft.isZero() && m_bottomRight.isZero(); 41 } 42 43 void RoundedRect::Radii::scale(float factor) 44 { 45 if (factor == 1) 46 return; 47 48 // If either radius on a corner becomes zero, reset both radii on that corner. 49 m_topLeft.scale(factor); 50 if (!m_topLeft.width() || !m_topLeft.height()) 51 m_topLeft = IntSize(); 52 m_topRight.scale(factor); 53 if (!m_topRight.width() || !m_topRight.height()) 54 m_topRight = IntSize(); 55 m_bottomLeft.scale(factor); 56 if (!m_bottomLeft.width() || !m_bottomLeft.height()) 57 m_bottomLeft = IntSize(); 58 m_bottomRight.scale(factor); 59 if (!m_bottomRight.width() || !m_bottomRight.height()) 60 m_bottomRight = IntSize(); 61 62 } 63 64 void RoundedRect::Radii::expand(int topWidth, int bottomWidth, int leftWidth, int rightWidth) 65 { 66 if (m_topLeft.width() > 0 && m_topLeft.height() > 0) { 67 m_topLeft.setWidth(max<int>(0, m_topLeft.width() + leftWidth)); 68 m_topLeft.setHeight(max<int>(0, m_topLeft.height() + topWidth)); 69 } 70 if (m_topRight.width() > 0 && m_topRight.height() > 0) { 71 m_topRight.setWidth(max<int>(0, m_topRight.width() + rightWidth)); 72 m_topRight.setHeight(max<int>(0, m_topRight.height() + topWidth)); 73 } 74 if (m_bottomLeft.width() > 0 && m_bottomLeft.height() > 0) { 75 m_bottomLeft.setWidth(max<int>(0, m_bottomLeft.width() + leftWidth)); 76 m_bottomLeft.setHeight(max<int>(0, m_bottomLeft.height() + bottomWidth)); 77 } 78 if (m_bottomRight.width() > 0 && m_bottomRight.height() > 0) { 79 m_bottomRight.setWidth(max<int>(0, m_bottomRight.width() + rightWidth)); 80 m_bottomRight.setHeight(max<int>(0, m_bottomRight.height() + bottomWidth)); 81 } 82 } 83 84 void RoundedRect::inflateWithRadii(int size) 85 { 86 IntRect old = m_rect; 87 88 m_rect.inflate(size); 89 // Considering the inflation factor of shorter size to scale the radii seems appropriate here 90 float factor; 91 if (m_rect.width() < m_rect.height()) 92 factor = old.width() ? (float)m_rect.width() / old.width() : int(0); 93 else 94 factor = old.height() ? (float)m_rect.height() / old.height() : int(0); 95 96 m_radii.scale(factor); 97 } 98 99 void RoundedRect::Radii::includeLogicalEdges(const RoundedRect::Radii& edges, bool isHorizontal, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) 100 { 101 if (includeLogicalLeftEdge) { 102 if (isHorizontal) 103 m_bottomLeft = edges.bottomLeft(); 104 else 105 m_topRight = edges.topRight(); 106 m_topLeft = edges.topLeft(); 107 } 108 109 if (includeLogicalRightEdge) { 110 if (isHorizontal) 111 m_topRight = edges.topRight(); 112 else 113 m_bottomLeft = edges.bottomLeft(); 114 m_bottomRight = edges.bottomRight(); 115 } 116 } 117 118 void RoundedRect::Radii::excludeLogicalEdges(bool isHorizontal, bool excludeLogicalLeftEdge, bool excludeLogicalRightEdge) 119 { 120 if (excludeLogicalLeftEdge) { 121 if (isHorizontal) 122 m_bottomLeft = IntSize(); 123 else 124 m_topRight = IntSize(); 125 m_topLeft = IntSize(); 126 } 127 128 if (excludeLogicalRightEdge) { 129 if (isHorizontal) 130 m_topRight = IntSize(); 131 else 132 m_bottomLeft = IntSize(); 133 m_bottomRight = IntSize(); 134 } 135 } 136 137 RoundedRect::RoundedRect(int x, int y, int width, int height) 138 : m_rect(x, y, width, height) 139 { 140 } 141 142 RoundedRect::RoundedRect(const IntRect& rect, const Radii& radii) 143 : m_rect(rect) 144 , m_radii(radii) 145 { 146 } 147 148 RoundedRect::RoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight) 149 : m_rect(rect) 150 , m_radii(topLeft, topRight, bottomLeft, bottomRight) 151 { 152 } 153 154 IntRect RoundedRect::radiusCenterRect() const 155 { 156 ASSERT(isRenderable()); 157 int minX = m_rect.x() + max(m_radii.topLeft().width(), m_radii.bottomLeft().width()); 158 int minY = m_rect.y() + max(m_radii.topLeft().height(), m_radii.topRight().height()); 159 int maxX = m_rect.maxX() - max(m_radii.topRight().width(), m_radii.bottomRight().width()); 160 int maxY = m_rect.maxY() - max(m_radii.bottomLeft().height(), m_radii.bottomRight().height()); 161 return IntRect(minX, minY, maxX - minX, maxY - minY); 162 } 163 164 void RoundedRect::includeLogicalEdges(const Radii& edges, bool isHorizontal, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) 165 { 166 m_radii.includeLogicalEdges(edges, isHorizontal, includeLogicalLeftEdge, includeLogicalRightEdge); 167 } 168 169 void RoundedRect::excludeLogicalEdges(bool isHorizontal, bool excludeLogicalLeftEdge, bool excludeLogicalRightEdge) 170 { 171 m_radii.excludeLogicalEdges(isHorizontal, excludeLogicalLeftEdge, excludeLogicalRightEdge); 172 } 173 174 bool RoundedRect::isRenderable() const 175 { 176 return m_radii.topLeft().width() + m_radii.topRight().width() <= m_rect.width() 177 && m_radii.bottomLeft().width() + m_radii.bottomRight().width() <= m_rect.width() 178 && m_radii.topLeft().height() + m_radii.bottomLeft().height() <= m_rect.height() 179 && m_radii.topRight().height() + m_radii.bottomRight().height() <= m_rect.height(); 180 } 181 182 void RoundedRect::adjustRadii() 183 { 184 int maxRadiusWidth = std::max(m_radii.topLeft().width() + m_radii.topRight().width(), m_radii.bottomLeft().width() + m_radii.bottomRight().width()); 185 int maxRadiusHeight = std::max(m_radii.topLeft().height() + m_radii.bottomLeft().height(), m_radii.topRight().height() + m_radii.bottomRight().height()); 186 187 if (maxRadiusWidth <= 0 || maxRadiusHeight <= 0) { 188 m_radii.scale(0.0f); 189 return; 190 } 191 float widthRatio = static_cast<float>(m_rect.width()) / maxRadiusWidth; 192 float heightRatio = static_cast<float>(m_rect.height()) / maxRadiusHeight; 193 m_radii.scale(widthRatio < heightRatio ? widthRatio : heightRatio); 194 } 195 196 bool RoundedRect::intersectsQuad(const FloatQuad& quad) const 197 { 198 FloatRect rect(m_rect); 199 if (!quad.intersectsRect(rect)) 200 return false; 201 202 const IntSize& topLeft = m_radii.topLeft(); 203 if (!topLeft.isEmpty()) { 204 FloatRect rect(m_rect.x(), m_rect.y(), topLeft.width(), topLeft.height()); 205 if (quad.intersectsRect(rect)) { 206 FloatPoint center(m_rect.x() + topLeft.width(), m_rect.y() + topLeft.height()); 207 FloatSize size(topLeft.width(), topLeft.height()); 208 if (!quad.intersectsEllipse(center, size)) 209 return false; 210 } 211 } 212 213 const IntSize& topRight = m_radii.topRight(); 214 if (!topRight.isEmpty()) { 215 FloatRect rect(m_rect.maxX() - topRight.width(), m_rect.y(), topRight.width(), topRight.height()); 216 if (quad.intersectsRect(rect)) { 217 FloatPoint center(m_rect.maxX() - topRight.width(), m_rect.y() + topRight.height()); 218 FloatSize size(topRight.width(), topRight.height()); 219 if (!quad.intersectsEllipse(center, size)) 220 return false; 221 } 222 } 223 224 const IntSize& bottomLeft = m_radii.bottomLeft(); 225 if (!bottomLeft.isEmpty()) { 226 FloatRect rect(m_rect.x(), m_rect.maxY() - bottomLeft.height(), bottomLeft.width(), bottomLeft.height()); 227 if (quad.intersectsRect(rect)) { 228 FloatPoint center(m_rect.x() + bottomLeft.width(), m_rect.maxY() - bottomLeft.height()); 229 FloatSize size(bottomLeft.width(), bottomLeft.height()); 230 if (!quad.intersectsEllipse(center, size)) 231 return false; 232 } 233 } 234 235 const IntSize& bottomRight = m_radii.bottomRight(); 236 if (!bottomRight.isEmpty()) { 237 FloatRect rect(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height(), bottomRight.width(), bottomRight.height()); 238 if (quad.intersectsRect(rect)) { 239 FloatPoint center(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height()); 240 FloatSize size(bottomRight.width(), bottomRight.height()); 241 if (!quad.intersectsEllipse(center, size)) 242 return false; 243 } 244 } 245 246 return true; 247 } 248 249 } // namespace WebCore 250