1/* 2 * Copyright (C) 2013 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 "FloatRoundedRect.h" 32 33#include <algorithm> 34 35namespace WebCore { 36 37FloatRoundedRect::FloatRoundedRect(const RoundedRect& rect) 38 : m_rect(rect.rect()) 39 , m_radii(rect.radii()) 40{ 41} 42 43FloatRoundedRect::FloatRoundedRect(float x, float y, float width, float height) 44 : m_rect(x, y, width, height) 45{ 46} 47 48FloatRoundedRect::FloatRoundedRect(const FloatRect& rect, const Radii& radii) 49 : m_rect(rect) 50 , m_radii(radii) 51{ 52} 53 54FloatRoundedRect::FloatRoundedRect(const FloatRect& rect, const FloatSize& topLeft, const FloatSize& topRight, const FloatSize& bottomLeft, const FloatSize& bottomRight) 55 : m_rect(rect) 56 , m_radii(topLeft, topRight, bottomLeft, bottomRight) 57{ 58} 59 60bool FloatRoundedRect::Radii::isZero() const 61{ 62 return m_topLeft.isZero() && m_topRight.isZero() && m_bottomLeft.isZero() && m_bottomRight.isZero(); 63} 64 65void FloatRoundedRect::Radii::scale(float factor) 66{ 67 scale(factor, factor); 68} 69 70void FloatRoundedRect::Radii::scale(float horizontalFactor, float verticalFactor) 71{ 72 if (horizontalFactor == 1 && verticalFactor == 1) 73 return; 74 75 // If either radius on a corner becomes zero, reset both radii on that corner. 76 m_topLeft.scale(horizontalFactor, verticalFactor); 77 if (!m_topLeft.width() || !m_topLeft.height()) 78 m_topLeft = FloatSize(); 79 m_topRight.scale(horizontalFactor, verticalFactor); 80 if (!m_topRight.width() || !m_topRight.height()) 81 m_topRight = FloatSize(); 82 m_bottomLeft.scale(horizontalFactor, verticalFactor); 83 if (!m_bottomLeft.width() || !m_bottomLeft.height()) 84 m_bottomLeft = FloatSize(); 85 m_bottomRight.scale(horizontalFactor, verticalFactor); 86 if (!m_bottomRight.width() || !m_bottomRight.height()) 87 m_bottomRight = FloatSize(); 88} 89 90void FloatRoundedRect::Radii::expand(float topWidth, float bottomWidth, float leftWidth, float rightWidth) 91{ 92 if (m_topLeft.width() > 0 && m_topLeft.height() > 0) { 93 m_topLeft.setWidth(std::max<float>(0, m_topLeft.width() + leftWidth)); 94 m_topLeft.setHeight(std::max<float>(0, m_topLeft.height() + topWidth)); 95 } 96 if (m_topRight.width() > 0 && m_topRight.height() > 0) { 97 m_topRight.setWidth(std::max<float>(0, m_topRight.width() + rightWidth)); 98 m_topRight.setHeight(std::max<float>(0, m_topRight.height() + topWidth)); 99 } 100 if (m_bottomLeft.width() > 0 && m_bottomLeft.height() > 0) { 101 m_bottomLeft.setWidth(std::max<float>(0, m_bottomLeft.width() + leftWidth)); 102 m_bottomLeft.setHeight(std::max<float>(0, m_bottomLeft.height() + bottomWidth)); 103 } 104 if (m_bottomRight.width() > 0 && m_bottomRight.height() > 0) { 105 m_bottomRight.setWidth(std::max<float>(0, m_bottomRight.width() + rightWidth)); 106 m_bottomRight.setHeight(std::max<float>(0, m_bottomRight.height() + bottomWidth)); 107 } 108} 109 110static inline float cornerRectIntercept(float y, const FloatRect& cornerRect) 111{ 112 ASSERT(cornerRect.height() > 0); 113 return cornerRect.width() * sqrt(1 - (y * y) / (cornerRect.height() * cornerRect.height())); 114} 115 116bool FloatRoundedRect::xInterceptsAtY(float y, float& minXIntercept, float& maxXIntercept) const 117{ 118 if (y < rect().y() || y > rect().maxY()) 119 return false; 120 121 if (!isRounded()) { 122 minXIntercept = rect().x(); 123 maxXIntercept = rect().maxX(); 124 return true; 125 } 126 127 const FloatRect& topLeftRect = topLeftCorner(); 128 const FloatRect& bottomLeftRect = bottomLeftCorner(); 129 130 if (!topLeftRect.isEmpty() && y >= topLeftRect.y() && y < topLeftRect.maxY()) 131 minXIntercept = topLeftRect.maxX() - cornerRectIntercept(topLeftRect.maxY() - y, topLeftRect); 132 else if (!bottomLeftRect.isEmpty() && y >= bottomLeftRect.y() && y <= bottomLeftRect.maxY()) 133 minXIntercept = bottomLeftRect.maxX() - cornerRectIntercept(y - bottomLeftRect.y(), bottomLeftRect); 134 else 135 minXIntercept = m_rect.x(); 136 137 const FloatRect& topRightRect = topRightCorner(); 138 const FloatRect& bottomRightRect = bottomRightCorner(); 139 140 if (!topRightRect.isEmpty() && y >= topRightRect.y() && y <= topRightRect.maxY()) 141 maxXIntercept = topRightRect.x() + cornerRectIntercept(topRightRect.maxY() - y, topRightRect); 142 else if (!bottomRightRect.isEmpty() && y >= bottomRightRect.y() && y <= bottomRightRect.maxY()) 143 maxXIntercept = bottomRightRect.x() + cornerRectIntercept(y - bottomRightRect.y(), bottomRightRect); 144 else 145 maxXIntercept = m_rect.maxX(); 146 147 return true; 148} 149 150bool FloatRoundedRect::isRenderable() const 151{ 152 return m_radii.topLeft().width() >= 0 && m_radii.topLeft().height() >= 0 153 && m_radii.bottomLeft().width() >= 0 && m_radii.bottomLeft().height() >= 0 154 && m_radii.topRight().width() >= 0 && m_radii.topRight().height() >= 0 155 && m_radii.bottomRight().width() >= 0 && m_radii.bottomRight().height() >= 0 156 && m_radii.topLeft().width() + m_radii.topRight().width() <= m_rect.width() 157 && m_radii.bottomLeft().width() + m_radii.bottomRight().width() <= m_rect.width() 158 && m_radii.topLeft().height() + m_radii.bottomLeft().height() <= m_rect.height() 159 && m_radii.topRight().height() + m_radii.bottomRight().height() <= m_rect.height(); 160} 161 162void FloatRoundedRect::inflateWithRadii(float size) 163{ 164 FloatRect old = m_rect; 165 166 m_rect.inflate(size); 167 // Considering the inflation factor of shorter size to scale the radii seems appropriate here 168 float factor; 169 if (m_rect.width() < m_rect.height()) 170 factor = old.width() ? m_rect.width() / old.width() : 0; 171 else 172 factor = old.height() ? m_rect.height() / old.height() : 0; 173 174 m_radii.scale(factor); 175} 176 177void FloatRoundedRect::adjustRadii() 178{ 179 float maxRadiusWidth = std::max(m_radii.topLeft().width() + m_radii.topRight().width(), m_radii.bottomLeft().width() + m_radii.bottomRight().width()); 180 float maxRadiusHeight = std::max(m_radii.topLeft().height() + m_radii.bottomLeft().height(), m_radii.topRight().height() + m_radii.bottomRight().height()); 181 182 if (maxRadiusWidth <= 0 || maxRadiusHeight <= 0) { 183 m_radii.scale(0.0f); 184 return; 185 } 186 float widthRatio = m_rect.width() / maxRadiusWidth; 187 float heightRatio = m_rect.height() / maxRadiusHeight; 188 m_radii.scale(widthRatio < heightRatio ? widthRatio : heightRatio); 189} 190 191 192} // namespace WebCore 193