| OLD | NEW |
| (Empty) |
| 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/layout/style/BasicShapes.h" | |
| 32 | |
| 33 #include "core/css/BasicShapeFunctions.h" | |
| 34 #include "core/layout/style/ComputedStyle.h" | |
| 35 #include "platform/CalculationValue.h" | |
| 36 #include "platform/LengthFunctions.h" | |
| 37 #include "platform/geometry/FloatRect.h" | |
| 38 #include "platform/graphics/Path.h" | |
| 39 | |
| 40 namespace blink { | |
| 41 | |
| 42 bool BasicShape::canBlend(const BasicShape* other) const | |
| 43 { | |
| 44 // FIXME: Support animations between different shapes in the future. | |
| 45 if (!other || !isSameType(*other)) | |
| 46 return false; | |
| 47 | |
| 48 // Just polygons with same number of vertices can be animated. | |
| 49 if (type() == BasicShape::BasicShapePolygonType | |
| 50 && (toBasicShapePolygon(this)->values().size() != toBasicShapePolygon(ot
her)->values().size() | |
| 51 || toBasicShapePolygon(this)->windRule() != toBasicShapePolygon(other)->
windRule())) | |
| 52 return false; | |
| 53 | |
| 54 // Circles with keywords for radii or center coordinates cannot be animated. | |
| 55 if (type() == BasicShape::BasicShapeCircleType) { | |
| 56 if (!toBasicShapeCircle(this)->radius().canBlend(toBasicShapeCircle(othe
r)->radius())) | |
| 57 return false; | |
| 58 } | |
| 59 | |
| 60 // Ellipses with keywords for radii or center coordinates cannot be animated
. | |
| 61 if (type() != BasicShape::BasicShapeEllipseType) | |
| 62 return true; | |
| 63 | |
| 64 return (toBasicShapeEllipse(this)->radiusX().canBlend(toBasicShapeEllipse(ot
her)->radiusX()) | |
| 65 && toBasicShapeEllipse(this)->radiusY().canBlend(toBasicShapeEllipse(oth
er)->radiusY())); | |
| 66 } | |
| 67 | |
| 68 bool BasicShapeCircle::operator==(const BasicShape& o) const | |
| 69 { | |
| 70 if (!isSameType(o)) | |
| 71 return false; | |
| 72 const BasicShapeCircle& other = toBasicShapeCircle(o); | |
| 73 return m_centerX == other.m_centerX && m_centerY == other.m_centerY && m_rad
ius == other.m_radius; | |
| 74 } | |
| 75 | |
| 76 float BasicShapeCircle::floatValueForRadiusInBox(FloatSize boxSize) const | |
| 77 { | |
| 78 if (m_radius.type() == BasicShapeRadius::Value) | |
| 79 return floatValueForLength(m_radius.value(), hypotf(boxSize.width(), box
Size.height()) / sqrtf(2)); | |
| 80 | |
| 81 FloatPoint center = floatPointForCenterCoordinate(m_centerX, m_centerY, boxS
ize); | |
| 82 | |
| 83 float widthDelta = std::abs(boxSize.width() - center.x()); | |
| 84 float heightDelta = std::abs(boxSize.height() - center.y()); | |
| 85 if (m_radius.type() == BasicShapeRadius::ClosestSide) | |
| 86 return std::min(std::min(std::abs(center.x()), widthDelta), std::min(std
::abs(center.y()), heightDelta)); | |
| 87 | |
| 88 // If radius.type() == BasicShapeRadius::FarthestSide. | |
| 89 return std::max(std::max(center.x(), widthDelta), std::max(center.y(), heigh
tDelta)); | |
| 90 } | |
| 91 | |
| 92 void BasicShapeCircle::path(Path& path, const FloatRect& boundingBox) | |
| 93 { | |
| 94 ASSERT(path.isEmpty()); | |
| 95 FloatPoint center = floatPointForCenterCoordinate(m_centerX, m_centerY, boun
dingBox.size()); | |
| 96 float radius = floatValueForRadiusInBox(boundingBox.size()); | |
| 97 path.addEllipse(FloatRect( | |
| 98 center.x() - radius + boundingBox.x(), | |
| 99 center.y() - radius + boundingBox.y(), | |
| 100 radius * 2, | |
| 101 radius * 2 | |
| 102 )); | |
| 103 } | |
| 104 | |
| 105 PassRefPtr<BasicShape> BasicShapeCircle::blend(const BasicShape* other, double p
rogress) const | |
| 106 { | |
| 107 ASSERT(type() == other->type()); | |
| 108 const BasicShapeCircle* o = toBasicShapeCircle(other); | |
| 109 RefPtr<BasicShapeCircle> result = BasicShapeCircle::create(); | |
| 110 | |
| 111 result->setCenterX(m_centerX.blend(o->centerX(), progress)); | |
| 112 result->setCenterY(m_centerY.blend(o->centerY(), progress)); | |
| 113 result->setRadius(m_radius.blend(o->radius(), progress)); | |
| 114 return result.release(); | |
| 115 } | |
| 116 | |
| 117 bool BasicShapeEllipse::operator==(const BasicShape& o) const | |
| 118 { | |
| 119 if (!isSameType(o)) | |
| 120 return false; | |
| 121 const BasicShapeEllipse& other = toBasicShapeEllipse(o); | |
| 122 return m_centerX == other.m_centerX && m_centerY == other.m_centerY && m_rad
iusX == other.m_radiusX && m_radiusY == other.m_radiusY; | |
| 123 } | |
| 124 | |
| 125 float BasicShapeEllipse::floatValueForRadiusInBox(const BasicShapeRadius& radius
, float center, float boxWidthOrHeight) const | |
| 126 { | |
| 127 if (radius.type() == BasicShapeRadius::Value) | |
| 128 return floatValueForLength(radius.value(), boxWidthOrHeight); | |
| 129 | |
| 130 float widthOrHeightDelta = std::abs(boxWidthOrHeight - center); | |
| 131 if (radius.type() == BasicShapeRadius::ClosestSide) | |
| 132 return std::min(std::abs(center), widthOrHeightDelta); | |
| 133 | |
| 134 ASSERT(radius.type() == BasicShapeRadius::FarthestSide); | |
| 135 return std::max(center, widthOrHeightDelta); | |
| 136 } | |
| 137 | |
| 138 void BasicShapeEllipse::path(Path& path, const FloatRect& boundingBox) | |
| 139 { | |
| 140 ASSERT(path.isEmpty()); | |
| 141 FloatPoint center = floatPointForCenterCoordinate(m_centerX, m_centerY, boun
dingBox.size()); | |
| 142 float radiusX = floatValueForRadiusInBox(m_radiusX, center.x(), boundingBox.
width()); | |
| 143 float radiusY = floatValueForRadiusInBox(m_radiusY, center.y(), boundingBox.
height()); | |
| 144 path.addEllipse(FloatRect( | |
| 145 center.x() - radiusX + boundingBox.x(), | |
| 146 center.y() - radiusY + boundingBox.y(), | |
| 147 radiusX * 2, | |
| 148 radiusY * 2 | |
| 149 )); | |
| 150 } | |
| 151 | |
| 152 PassRefPtr<BasicShape> BasicShapeEllipse::blend(const BasicShape* other, double
progress) const | |
| 153 { | |
| 154 ASSERT(type() == other->type()); | |
| 155 const BasicShapeEllipse* o = toBasicShapeEllipse(other); | |
| 156 RefPtr<BasicShapeEllipse> result = BasicShapeEllipse::create(); | |
| 157 | |
| 158 if (m_radiusX.type() != BasicShapeRadius::Value || o->radiusX().type() != Ba
sicShapeRadius::Value | |
| 159 || m_radiusY.type() != BasicShapeRadius::Value || o->radiusY().type() !=
BasicShapeRadius::Value) { | |
| 160 result->setCenterX(o->centerX()); | |
| 161 result->setCenterY(o->centerY()); | |
| 162 result->setRadiusX(o->radiusX()); | |
| 163 result->setRadiusY(o->radiusY()); | |
| 164 return result; | |
| 165 } | |
| 166 | |
| 167 result->setCenterX(m_centerX.blend(o->centerX(), progress)); | |
| 168 result->setCenterY(m_centerY.blend(o->centerY(), progress)); | |
| 169 result->setRadiusX(m_radiusX.blend(o->radiusX(), progress)); | |
| 170 result->setRadiusY(m_radiusY.blend(o->radiusY(), progress)); | |
| 171 return result.release(); | |
| 172 } | |
| 173 | |
| 174 void BasicShapePolygon::path(Path& path, const FloatRect& boundingBox) | |
| 175 { | |
| 176 ASSERT(path.isEmpty()); | |
| 177 ASSERT(!(m_values.size() % 2)); | |
| 178 size_t length = m_values.size(); | |
| 179 | |
| 180 if (!length) | |
| 181 return; | |
| 182 | |
| 183 path.moveTo(FloatPoint(floatValueForLength(m_values.at(0), boundingBox.width
()) + boundingBox.x(), | |
| 184 floatValueForLength(m_values.at(1), boundingBox.height()) + boundingBox.
y())); | |
| 185 for (size_t i = 2; i < length; i = i + 2) { | |
| 186 path.addLineTo(FloatPoint(floatValueForLength(m_values.at(i), boundingBo
x.width()) + boundingBox.x(), | |
| 187 floatValueForLength(m_values.at(i + 1), boundingBox.height()) + boun
dingBox.y())); | |
| 188 } | |
| 189 path.closeSubpath(); | |
| 190 } | |
| 191 | |
| 192 PassRefPtr<BasicShape> BasicShapePolygon::blend(const BasicShape* other, double
progress) const | |
| 193 { | |
| 194 ASSERT(other && isSameType(*other)); | |
| 195 | |
| 196 const BasicShapePolygon* o = toBasicShapePolygon(other); | |
| 197 ASSERT(m_values.size() == o->values().size()); | |
| 198 ASSERT(!(m_values.size() % 2)); | |
| 199 | |
| 200 size_t length = m_values.size(); | |
| 201 RefPtr<BasicShapePolygon> result = BasicShapePolygon::create(); | |
| 202 if (!length) | |
| 203 return result.release(); | |
| 204 | |
| 205 result->setWindRule(o->windRule()); | |
| 206 | |
| 207 for (size_t i = 0; i < length; i = i + 2) { | |
| 208 result->appendPoint(m_values.at(i).blend(o->values().at(i), progress, Va
lueRangeAll), | |
| 209 m_values.at(i + 1).blend(o->values().at(i + 1), progress, ValueRange
All)); | |
| 210 } | |
| 211 | |
| 212 return result.release(); | |
| 213 } | |
| 214 | |
| 215 bool BasicShapePolygon::operator==(const BasicShape& o) const | |
| 216 { | |
| 217 if (!isSameType(o)) | |
| 218 return false; | |
| 219 const BasicShapePolygon& other = toBasicShapePolygon(o); | |
| 220 return m_windRule == other.m_windRule && m_values == other.m_values; | |
| 221 } | |
| 222 | |
| 223 static FloatSize floatSizeForLengthSize(const LengthSize& lengthSize, const Floa
tRect& boundingBox) | |
| 224 { | |
| 225 return FloatSize(floatValueForLength(lengthSize.width(), boundingBox.width()
), | |
| 226 floatValueForLength(lengthSize.height(), boundingBox.height())); | |
| 227 } | |
| 228 | |
| 229 void BasicShapeInset::path(Path& path, const FloatRect& boundingBox) | |
| 230 { | |
| 231 ASSERT(path.isEmpty()); | |
| 232 float left = floatValueForLength(m_left, boundingBox.width()); | |
| 233 float top = floatValueForLength(m_top, boundingBox.height()); | |
| 234 FloatRect rect(left + boundingBox.x(), top + boundingBox.y(), | |
| 235 std::max<float>(boundingBox.width() - left - floatValueForLength(m_right
, boundingBox.width()), 0), | |
| 236 std::max<float>(boundingBox.height() - top - floatValueForLength(m_botto
m, boundingBox.height()), 0)); | |
| 237 auto radii = FloatRoundedRect::Radii(floatSizeForLengthSize(m_topLeftRadius,
boundingBox), | |
| 238 floatSizeForLengthSize(m_topRightRadius, boundingBox), | |
| 239 floatSizeForLengthSize(m_bottomLeftRadius, boundingBox), | |
| 240 floatSizeForLengthSize(m_bottomRightRadius, boundingBox)); | |
| 241 | |
| 242 FloatRoundedRect finalRect(rect, radii); | |
| 243 finalRect.constrainRadii(); | |
| 244 path.addRoundedRect(finalRect); | |
| 245 } | |
| 246 | |
| 247 static inline LengthSize blendLengthSize(const LengthSize& to, const LengthSize&
from, double progress) | |
| 248 { | |
| 249 return LengthSize(to.width().blend(from.width(), progress, ValueRangeAll), | |
| 250 to.height().blend(from.height(), progress, ValueRangeAll)); | |
| 251 } | |
| 252 | |
| 253 PassRefPtr<BasicShape> BasicShapeInset::blend(const BasicShape* other, double pr
ogress) const | |
| 254 { | |
| 255 ASSERT(other && isSameType(*other)); | |
| 256 | |
| 257 const BasicShapeInset& otherInset = toBasicShapeInset(*other); | |
| 258 RefPtr<BasicShapeInset> result = BasicShapeInset::create(); | |
| 259 result->setTop(m_top.blend(otherInset.top(), progress, ValueRangeAll)); | |
| 260 result->setRight(m_right.blend(otherInset.right(), progress, ValueRangeAll))
; | |
| 261 result->setBottom(m_bottom.blend(otherInset.bottom(), progress, ValueRangeAl
l)); | |
| 262 result->setLeft(m_left.blend(otherInset.left(), progress, ValueRangeAll)); | |
| 263 | |
| 264 result->setTopLeftRadius(blendLengthSize(m_topLeftRadius, otherInset.topLeft
Radius(), progress)); | |
| 265 result->setTopRightRadius(blendLengthSize(m_topRightRadius, otherInset.topRi
ghtRadius(), progress)); | |
| 266 result->setBottomRightRadius(blendLengthSize(m_bottomRightRadius, otherInset
.bottomRightRadius(), progress)); | |
| 267 result->setBottomLeftRadius(blendLengthSize(m_bottomLeftRadius, otherInset.b
ottomLeftRadius(), progress)); | |
| 268 | |
| 269 return result.release(); | |
| 270 } | |
| 271 | |
| 272 bool BasicShapeInset::operator==(const BasicShape& o) const | |
| 273 { | |
| 274 if (!isSameType(o)) | |
| 275 return false; | |
| 276 const BasicShapeInset& other = toBasicShapeInset(o); | |
| 277 return m_right == other.m_right | |
| 278 && m_top == other.m_top | |
| 279 && m_bottom == other.m_bottom | |
| 280 && m_left == other.m_left | |
| 281 && m_topLeftRadius == other.m_topLeftRadius | |
| 282 && m_topRightRadius == other.m_topRightRadius | |
| 283 && m_bottomRightRadius == other.m_bottomRightRadius | |
| 284 && m_bottomLeftRadius == other.m_bottomLeftRadius; | |
| 285 } | |
| 286 | |
| 287 } | |
| OLD | NEW |