Home | History | Annotate | Download | only in graphics
      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 "core/platform/graphics/RoundedRect.h"
     30 #include "wtf/Assertions.h"
     31 
     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