Home | History | Annotate | Download | only in style
      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 HOLDER "AS IS" AND ANY
     17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
     20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
     21  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
     25  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
     26  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     27  * SUCH DAMAGE.
     28  */
     29 
     30 #include "config.h"
     31 #include "core/rendering/style/BasicShapes.h"
     32 
     33 #include "core/css/BasicShapeFunctions.h"
     34 #include "platform/CalculationValue.h"
     35 #include "platform/LengthFunctions.h"
     36 #include "platform/geometry/FloatRect.h"
     37 #include "platform/graphics/Path.h"
     38 
     39 namespace WebCore {
     40 
     41 void BasicShapeCenterCoordinate::updateComputedLength()
     42 {
     43     if (m_direction == TopLeft) {
     44         m_computedLength = m_length.isUndefined() ? Length(0, Fixed) : m_length;
     45         return;
     46     }
     47     if (m_length.isUndefined()) {
     48         m_computedLength = Length(100, Percent);
     49         return;
     50     }
     51 
     52     m_computedLength = m_length.subtractFromOneHundredPercent();
     53 }
     54 
     55 bool BasicShape::canBlend(const BasicShape* other) const
     56 {
     57     // FIXME: Support animations between different shapes in the future.
     58     if (!other || !isSameType(*other))
     59         return false;
     60 
     61     // Just polygons with same number of vertices can be animated.
     62     if (type() == BasicShape::BasicShapePolygonType
     63         && (static_cast<const BasicShapePolygon*>(this)->values().size() != static_cast<const BasicShapePolygon*>(other)->values().size()
     64         || static_cast<const BasicShapePolygon*>(this)->windRule() != static_cast<const BasicShapePolygon*>(other)->windRule()))
     65         return false;
     66 
     67     // Circles with keywords for radii or center coordinates cannot be animated.
     68     if (type() == BasicShape::BasicShapeCircleType) {
     69         const BasicShapeCircle* thisCircle = static_cast<const BasicShapeCircle*>(this);
     70         const BasicShapeCircle* otherCircle = static_cast<const BasicShapeCircle*>(other);
     71         if (!thisCircle->radius().canBlend(otherCircle->radius()))
     72             return false;
     73     }
     74 
     75     // Ellipses with keywords for radii or center coordinates cannot be animated.
     76     if (type() != BasicShape::BasicShapeEllipseType)
     77         return true;
     78 
     79     const BasicShapeEllipse* thisEllipse = static_cast<const BasicShapeEllipse*>(this);
     80     const BasicShapeEllipse* otherEllipse = static_cast<const BasicShapeEllipse*>(other);
     81     return (thisEllipse->radiusX().canBlend(otherEllipse->radiusX())
     82         && thisEllipse->radiusY().canBlend(otherEllipse->radiusY()));
     83 }
     84 
     85 bool BasicShapeCircle::operator==(const BasicShape& o) const
     86 {
     87     if (!isSameType(o))
     88         return false;
     89     const BasicShapeCircle& other = toBasicShapeCircle(o);
     90     return m_centerX == other.m_centerX && m_centerY == other.m_centerY && m_radius == other.m_radius;
     91 }
     92 
     93 float BasicShapeCircle::floatValueForRadiusInBox(FloatSize boxSize) const
     94 {
     95     if (m_radius.type() == BasicShapeRadius::Value)
     96         return floatValueForLength(m_radius.value(), hypotf(boxSize.width(), boxSize.height()) / sqrtf(2));
     97 
     98     FloatPoint center = floatPointForCenterCoordinate(m_centerX, m_centerY, boxSize);
     99 
    100     if (m_radius.type() == BasicShapeRadius::ClosestSide)
    101         return std::min(std::min(center.x(), boxSize.width() - center.x()), std::min(center.y(), boxSize.height() - center.y()));
    102 
    103     // If radius.type() == BasicShapeRadius::FarthestSide.
    104     return std::max(std::max(center.x(), boxSize.width() - center.x()), std::max(center.y(), boxSize.height() - center.y()));
    105 }
    106 
    107 void BasicShapeCircle::path(Path& path, const FloatRect& boundingBox)
    108 {
    109     ASSERT(path.isEmpty());
    110     FloatPoint center = floatPointForCenterCoordinate(m_centerX, m_centerY, boundingBox.size());
    111     float radius = floatValueForRadiusInBox(boundingBox.size());
    112     path.addEllipse(FloatRect(
    113         center.x() - radius + boundingBox.x(),
    114         center.y() - radius + boundingBox.y(),
    115         radius * 2,
    116         radius * 2
    117     ));
    118 }
    119 
    120 PassRefPtr<BasicShape> BasicShapeCircle::blend(const BasicShape* other, double progress) const
    121 {
    122     ASSERT(type() == other->type());
    123     const BasicShapeCircle* o = static_cast<const BasicShapeCircle*>(other);
    124     RefPtr<BasicShapeCircle> result =  BasicShapeCircle::create();
    125 
    126     result->setCenterX(m_centerX.blend(o->centerX(), progress));
    127     result->setCenterY(m_centerY.blend(o->centerY(), progress));
    128     result->setRadius(m_radius.blend(o->radius(), progress));
    129     return result.release();
    130 }
    131 
    132 bool BasicShapeEllipse::operator==(const BasicShape& o) const
    133 {
    134     if (!isSameType(o))
    135         return false;
    136     const BasicShapeEllipse& other = toBasicShapeEllipse(o);
    137     return m_centerX == other.m_centerX && m_centerY == other.m_centerY && m_radiusX == other.m_radiusX && m_radiusY == other.m_radiusY;
    138 }
    139 
    140 float BasicShapeEllipse::floatValueForRadiusInBox(const BasicShapeRadius& radius, float center, float boxWidthOrHeight) const
    141 {
    142     if (radius.type() == BasicShapeRadius::Value)
    143         return floatValueForLength(radius.value(), boxWidthOrHeight);
    144 
    145     if (radius.type() == BasicShapeRadius::ClosestSide)
    146         return std::min(center, boxWidthOrHeight - center);
    147 
    148     ASSERT(radius.type() == BasicShapeRadius::FarthestSide);
    149     return std::max(center, boxWidthOrHeight - center);
    150 }
    151 
    152 void BasicShapeEllipse::path(Path& path, const FloatRect& boundingBox)
    153 {
    154     ASSERT(path.isEmpty());
    155     FloatPoint center = floatPointForCenterCoordinate(m_centerX, m_centerY, boundingBox.size());
    156     float radiusX = floatValueForRadiusInBox(m_radiusX, center.x(), boundingBox.width());
    157     float radiusY = floatValueForRadiusInBox(m_radiusY, center.y(), boundingBox.height());
    158     path.addEllipse(FloatRect(
    159         center.x() - radiusX + boundingBox.x(),
    160         center.y() - radiusY + boundingBox.y(),
    161         radiusX * 2,
    162         radiusY * 2
    163     ));
    164 }
    165 
    166 PassRefPtr<BasicShape> BasicShapeEllipse::blend(const BasicShape* other, double progress) const
    167 {
    168     ASSERT(type() == other->type());
    169     const BasicShapeEllipse* o = static_cast<const BasicShapeEllipse*>(other);
    170     RefPtr<BasicShapeEllipse> result =  BasicShapeEllipse::create();
    171 
    172     if (m_radiusX.type() != BasicShapeRadius::Value || o->radiusX().type() != BasicShapeRadius::Value
    173         || m_radiusY.type() != BasicShapeRadius::Value || o->radiusY().type() != BasicShapeRadius::Value) {
    174         result->setCenterX(o->centerX());
    175         result->setCenterY(o->centerY());
    176         result->setRadiusX(o->radiusX());
    177         result->setRadiusY(o->radiusY());
    178         return result;
    179     }
    180 
    181     result->setCenterX(m_centerX.blend(o->centerX(), progress));
    182     result->setCenterY(m_centerY.blend(o->centerY(), progress));
    183     result->setRadiusX(m_radiusX.blend(o->radiusX(), progress));
    184     result->setRadiusY(m_radiusY.blend(o->radiusY(), progress));
    185     return result.release();
    186 }
    187 
    188 void BasicShapePolygon::path(Path& path, const FloatRect& boundingBox)
    189 {
    190     ASSERT(path.isEmpty());
    191     ASSERT(!(m_values.size() % 2));
    192     size_t length = m_values.size();
    193 
    194     if (!length)
    195         return;
    196 
    197     path.moveTo(FloatPoint(floatValueForLength(m_values.at(0), boundingBox.width()) + boundingBox.x(),
    198         floatValueForLength(m_values.at(1), boundingBox.height()) + boundingBox.y()));
    199     for (size_t i = 2; i < length; i = i + 2) {
    200         path.addLineTo(FloatPoint(floatValueForLength(m_values.at(i), boundingBox.width()) + boundingBox.x(),
    201             floatValueForLength(m_values.at(i + 1), boundingBox.height()) + boundingBox.y()));
    202     }
    203     path.closeSubpath();
    204 }
    205 
    206 PassRefPtr<BasicShape> BasicShapePolygon::blend(const BasicShape* other, double progress) const
    207 {
    208     ASSERT(other && isSameType(*other));
    209 
    210     const BasicShapePolygon* o = static_cast<const BasicShapePolygon*>(other);
    211     ASSERT(m_values.size() == o->values().size());
    212     ASSERT(!(m_values.size() % 2));
    213 
    214     size_t length = m_values.size();
    215     RefPtr<BasicShapePolygon> result = BasicShapePolygon::create();
    216     if (!length)
    217         return result.release();
    218 
    219     result->setWindRule(o->windRule());
    220 
    221     for (size_t i = 0; i < length; i = i + 2) {
    222         result->appendPoint(m_values.at(i).blend(o->values().at(i), progress, ValueRangeAll),
    223             m_values.at(i + 1).blend(o->values().at(i + 1), progress, ValueRangeAll));
    224     }
    225 
    226     return result.release();
    227 }
    228 
    229 bool BasicShapePolygon::operator==(const BasicShape& o) const
    230 {
    231     if (!isSameType(o))
    232         return false;
    233     const BasicShapePolygon& other = toBasicShapePolygon(o);
    234     return m_windRule == other.m_windRule && m_values == other.m_values;
    235 }
    236 
    237 static FloatSize floatSizeForLengthSize(const LengthSize& lengthSize, const FloatRect& boundingBox)
    238 {
    239     return FloatSize(floatValueForLength(lengthSize.width(), boundingBox.width()),
    240         floatValueForLength(lengthSize.height(), boundingBox.height()));
    241 }
    242 
    243 void BasicShapeInset::path(Path& path, const FloatRect& boundingBox)
    244 {
    245     ASSERT(path.isEmpty());
    246     float left = floatValueForLength(m_left, boundingBox.width());
    247     float top = floatValueForLength(m_top, boundingBox.height());
    248     path.addRoundedRect(
    249         FloatRect(
    250             left + boundingBox.x(),
    251             top + boundingBox.y(),
    252             std::max<float>(boundingBox.width() - left - floatValueForLength(m_right, boundingBox.width()), 0),
    253             std::max<float>(boundingBox.height() - top - floatValueForLength(m_bottom, boundingBox.height()), 0)
    254         ),
    255         floatSizeForLengthSize(m_topLeftRadius, boundingBox),
    256         floatSizeForLengthSize(m_topRightRadius, boundingBox),
    257         floatSizeForLengthSize(m_bottomLeftRadius, boundingBox),
    258         floatSizeForLengthSize(m_bottomRightRadius, boundingBox)
    259     );
    260 }
    261 
    262 PassRefPtr<BasicShape> BasicShapeInset::blend(const BasicShape* other, double) const
    263 {
    264     ASSERT(type() == other->type());
    265     // FIXME: Implement blend for BasicShapeInset.
    266     return nullptr;
    267 }
    268 
    269 bool BasicShapeInset::operator==(const BasicShape& o) const
    270 {
    271     if (!isSameType(o))
    272         return false;
    273     const BasicShapeInset& other = toBasicShapeInset(o);
    274     return m_right == other.m_right
    275         && m_top == other.m_top
    276         && m_bottom == other.m_bottom
    277         && m_left == other.m_left
    278         && m_topLeftRadius == other.m_topLeftRadius
    279         && m_topRightRadius == other.m_topRightRadius
    280         && m_bottomRightRadius == other.m_bottomRightRadius
    281         && m_bottomLeftRadius == other.m_bottomLeftRadius;
    282 }
    283 
    284 }
    285