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 namespace blink { 35 36 bool RoundedRect::Radii::isZero() const 37 { 38 return m_topLeft.isZero() && m_topRight.isZero() && m_bottomLeft.isZero() && m_bottomRight.isZero(); 39 } 40 41 void RoundedRect::Radii::scale(float factor) 42 { 43 if (factor == 1) 44 return; 45 46 // If either radius on a corner becomes zero, reset both radii on that corner. 47 m_topLeft.scale(factor); 48 if (!m_topLeft.width() || !m_topLeft.height()) 49 m_topLeft = IntSize(); 50 m_topRight.scale(factor); 51 if (!m_topRight.width() || !m_topRight.height()) 52 m_topRight = IntSize(); 53 m_bottomLeft.scale(factor); 54 if (!m_bottomLeft.width() || !m_bottomLeft.height()) 55 m_bottomLeft = IntSize(); 56 m_bottomRight.scale(factor); 57 if (!m_bottomRight.width() || !m_bottomRight.height()) 58 m_bottomRight = IntSize(); 59 60 } 61 62 void RoundedRect::Radii::expand(int topWidth, int bottomWidth, int leftWidth, int rightWidth) 63 { 64 if (m_topLeft.width() > 0 && m_topLeft.height() > 0) { 65 m_topLeft.setWidth(std::max<int>(0, m_topLeft.width() + leftWidth)); 66 m_topLeft.setHeight(std::max<int>(0, m_topLeft.height() + topWidth)); 67 } 68 if (m_topRight.width() > 0 && m_topRight.height() > 0) { 69 m_topRight.setWidth(std::max<int>(0, m_topRight.width() + rightWidth)); 70 m_topRight.setHeight(std::max<int>(0, m_topRight.height() + topWidth)); 71 } 72 if (m_bottomLeft.width() > 0 && m_bottomLeft.height() > 0) { 73 m_bottomLeft.setWidth(std::max<int>(0, m_bottomLeft.width() + leftWidth)); 74 m_bottomLeft.setHeight(std::max<int>(0, m_bottomLeft.height() + bottomWidth)); 75 } 76 if (m_bottomRight.width() > 0 && m_bottomRight.height() > 0) { 77 m_bottomRight.setWidth(std::max<int>(0, m_bottomRight.width() + rightWidth)); 78 m_bottomRight.setHeight(std::max<int>(0, m_bottomRight.height() + bottomWidth)); 79 } 80 } 81 82 void RoundedRect::inflateWithRadii(int size) 83 { 84 IntRect old = m_rect; 85 86 m_rect.inflate(size); 87 // Considering the inflation factor of shorter size to scale the radii seems appropriate here 88 float factor; 89 if (m_rect.width() < m_rect.height()) 90 factor = old.width() ? (float)m_rect.width() / old.width() : int(0); 91 else 92 factor = old.height() ? (float)m_rect.height() / old.height() : int(0); 93 94 m_radii.scale(factor); 95 } 96 97 void RoundedRect::Radii::includeLogicalEdges(const RoundedRect::Radii& edges, bool isHorizontal, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) 98 { 99 if (includeLogicalLeftEdge) { 100 if (isHorizontal) 101 m_bottomLeft = edges.bottomLeft(); 102 else 103 m_topRight = edges.topRight(); 104 m_topLeft = edges.topLeft(); 105 } 106 107 if (includeLogicalRightEdge) { 108 if (isHorizontal) 109 m_topRight = edges.topRight(); 110 else 111 m_bottomLeft = edges.bottomLeft(); 112 m_bottomRight = edges.bottomRight(); 113 } 114 } 115 116 void RoundedRect::Radii::excludeLogicalEdges(bool isHorizontal, bool excludeLogicalLeftEdge, bool excludeLogicalRightEdge) 117 { 118 if (excludeLogicalLeftEdge) { 119 if (isHorizontal) 120 m_bottomLeft = IntSize(); 121 else 122 m_topRight = IntSize(); 123 m_topLeft = IntSize(); 124 } 125 126 if (excludeLogicalRightEdge) { 127 if (isHorizontal) 128 m_topRight = IntSize(); 129 else 130 m_bottomLeft = IntSize(); 131 m_bottomRight = IntSize(); 132 } 133 } 134 135 RoundedRect::RoundedRect(int x, int y, int width, int height) 136 : m_rect(x, y, width, height) 137 { 138 } 139 140 RoundedRect::RoundedRect(const IntRect& rect, const Radii& radii) 141 : m_rect(rect) 142 , m_radii(radii) 143 { 144 } 145 146 RoundedRect::RoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight) 147 : m_rect(rect) 148 , m_radii(topLeft, topRight, bottomLeft, bottomRight) 149 { 150 } 151 152 IntRect RoundedRect::radiusCenterRect() const 153 { 154 ASSERT(isRenderable()); 155 int minX = m_rect.x() + std::max(m_radii.topLeft().width(), m_radii.bottomLeft().width()); 156 int minY = m_rect.y() + std::max(m_radii.topLeft().height(), m_radii.topRight().height()); 157 int maxX = m_rect.maxX() - std::max(m_radii.topRight().width(), m_radii.bottomRight().width()); 158 int maxY = m_rect.maxY() - std::max(m_radii.bottomLeft().height(), m_radii.bottomRight().height()); 159 return IntRect(minX, minY, maxX - minX, maxY - minY); 160 } 161 162 void RoundedRect::includeLogicalEdges(const Radii& edges, bool isHorizontal, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) 163 { 164 m_radii.includeLogicalEdges(edges, isHorizontal, includeLogicalLeftEdge, includeLogicalRightEdge); 165 } 166 167 void RoundedRect::excludeLogicalEdges(bool isHorizontal, bool excludeLogicalLeftEdge, bool excludeLogicalRightEdge) 168 { 169 m_radii.excludeLogicalEdges(isHorizontal, excludeLogicalLeftEdge, excludeLogicalRightEdge); 170 } 171 172 bool RoundedRect::isRenderable() const 173 { 174 return m_radii.topLeft().width() + m_radii.topRight().width() <= m_rect.width() 175 && m_radii.bottomLeft().width() + m_radii.bottomRight().width() <= m_rect.width() 176 && m_radii.topLeft().height() + m_radii.bottomLeft().height() <= m_rect.height() 177 && m_radii.topRight().height() + m_radii.bottomRight().height() <= m_rect.height(); 178 } 179 180 void RoundedRect::adjustRadii() 181 { 182 int maxRadiusWidth = std::max(m_radii.topLeft().width() + m_radii.topRight().width(), m_radii.bottomLeft().width() + m_radii.bottomRight().width()); 183 int maxRadiusHeight = std::max(m_radii.topLeft().height() + m_radii.bottomLeft().height(), m_radii.topRight().height() + m_radii.bottomRight().height()); 184 185 if (maxRadiusWidth <= 0 || maxRadiusHeight <= 0) { 186 m_radii.scale(0.0f); 187 return; 188 } 189 float widthRatio = static_cast<float>(m_rect.width()) / maxRadiusWidth; 190 float heightRatio = static_cast<float>(m_rect.height()) / maxRadiusHeight; 191 m_radii.scale(widthRatio < heightRatio ? widthRatio : heightRatio); 192 } 193 194 bool RoundedRect::intersectsQuad(const FloatQuad& quad) const 195 { 196 FloatRect rect(m_rect); 197 if (!quad.intersectsRect(rect)) 198 return false; 199 200 const IntSize& topLeft = m_radii.topLeft(); 201 if (!topLeft.isEmpty()) { 202 FloatRect rect(m_rect.x(), m_rect.y(), topLeft.width(), topLeft.height()); 203 if (quad.intersectsRect(rect)) { 204 FloatPoint center(m_rect.x() + topLeft.width(), m_rect.y() + topLeft.height()); 205 FloatSize size(topLeft.width(), topLeft.height()); 206 if (!quad.intersectsEllipse(center, size)) 207 return false; 208 } 209 } 210 211 const IntSize& topRight = m_radii.topRight(); 212 if (!topRight.isEmpty()) { 213 FloatRect rect(m_rect.maxX() - topRight.width(), m_rect.y(), topRight.width(), topRight.height()); 214 if (quad.intersectsRect(rect)) { 215 FloatPoint center(m_rect.maxX() - topRight.width(), m_rect.y() + topRight.height()); 216 FloatSize size(topRight.width(), topRight.height()); 217 if (!quad.intersectsEllipse(center, size)) 218 return false; 219 } 220 } 221 222 const IntSize& bottomLeft = m_radii.bottomLeft(); 223 if (!bottomLeft.isEmpty()) { 224 FloatRect rect(m_rect.x(), m_rect.maxY() - bottomLeft.height(), bottomLeft.width(), bottomLeft.height()); 225 if (quad.intersectsRect(rect)) { 226 FloatPoint center(m_rect.x() + bottomLeft.width(), m_rect.maxY() - bottomLeft.height()); 227 FloatSize size(bottomLeft.width(), bottomLeft.height()); 228 if (!quad.intersectsEllipse(center, size)) 229 return false; 230 } 231 } 232 233 const IntSize& bottomRight = m_radii.bottomRight(); 234 if (!bottomRight.isEmpty()) { 235 FloatRect rect(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height(), bottomRight.width(), bottomRight.height()); 236 if (quad.intersectsRect(rect)) { 237 FloatPoint center(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height()); 238 FloatSize size(bottomRight.width(), bottomRight.height()); 239 if (!quad.intersectsEllipse(center, size)) 240 return false; 241 } 242 } 243 244 return true; 245 } 246 247 } // namespace blink 248