blob: 7481d3e4cbbb5ca4add2b1e16a0a6f183e27071e [file] [log] [blame]
/*
* Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "core/style/BasicShapes.h"
#include "core/css/BasicShapeFunctions.h"
#include "platform/CalculationValue.h"
#include "platform/LengthFunctions.h"
#include "platform/geometry/FloatRect.h"
#include "platform/graphics/Path.h"
namespace blink {
bool BasicShape::CanBlend(const BasicShape* other) const {
// FIXME: Support animations between different shapes in the future.
if (!other || !IsSameType(*other))
return false;
// Just polygons with same number of vertices can be animated.
if (GetType() == BasicShape::kBasicShapePolygonType &&
(ToBasicShapePolygon(this)->Values().size() !=
ToBasicShapePolygon(other)->Values().size() ||
ToBasicShapePolygon(this)->GetWindRule() !=
ToBasicShapePolygon(other)->GetWindRule()))
return false;
// Circles with keywords for radii or center coordinates cannot be animated.
if (GetType() == BasicShape::kBasicShapeCircleType) {
if (!ToBasicShapeCircle(this)->Radius().CanBlend(
ToBasicShapeCircle(other)->Radius()))
return false;
}
// Ellipses with keywords for radii or center coordinates cannot be animated.
if (GetType() != BasicShape::kBasicShapeEllipseType)
return true;
return (ToBasicShapeEllipse(this)->RadiusX().CanBlend(
ToBasicShapeEllipse(other)->RadiusX()) &&
ToBasicShapeEllipse(this)->RadiusY().CanBlend(
ToBasicShapeEllipse(other)->RadiusY()));
}
bool BasicShapeCircle::operator==(const BasicShape& o) const {
if (!IsSameType(o))
return false;
const BasicShapeCircle& other = ToBasicShapeCircle(o);
return center_x_ == other.center_x_ && center_y_ == other.center_y_ &&
radius_ == other.radius_;
}
float BasicShapeCircle::FloatValueForRadiusInBox(FloatSize box_size) const {
if (radius_.GetType() == BasicShapeRadius::kValue)
return FloatValueForLength(
radius_.Value(),
hypotf(box_size.Width(), box_size.Height()) / sqrtf(2));
FloatPoint center =
FloatPointForCenterCoordinate(center_x_, center_y_, box_size);
float width_delta = std::abs(box_size.Width() - center.X());
float height_delta = std::abs(box_size.Height() - center.Y());
if (radius_.GetType() == BasicShapeRadius::kClosestSide)
return std::min(std::min(std::abs(center.X()), width_delta),
std::min(std::abs(center.Y()), height_delta));
// If radius.type() == BasicShapeRadius::kFarthestSide.
return std::max(std::max(center.X(), width_delta),
std::max(center.Y(), height_delta));
}
void BasicShapeCircle::GetPath(Path& path, const FloatRect& bounding_box) {
DCHECK(path.IsEmpty());
FloatPoint center =
FloatPointForCenterCoordinate(center_x_, center_y_, bounding_box.Size());
float radius = FloatValueForRadiusInBox(bounding_box.Size());
path.AddEllipse(FloatRect(center.X() - radius + bounding_box.X(),
center.Y() - radius + bounding_box.Y(), radius * 2,
radius * 2));
}
PassRefPtr<BasicShape> BasicShapeCircle::Blend(const BasicShape* other,
double progress) const {
DCHECK_EQ(GetType(), other->GetType());
const BasicShapeCircle* o = ToBasicShapeCircle(other);
RefPtr<BasicShapeCircle> result = BasicShapeCircle::Create();
result->SetCenterX(center_x_.Blend(o->CenterX(), progress));
result->SetCenterY(center_y_.Blend(o->CenterY(), progress));
result->SetRadius(radius_.Blend(o->Radius(), progress));
return result;
}
bool BasicShapeEllipse::operator==(const BasicShape& o) const {
if (!IsSameType(o))
return false;
const BasicShapeEllipse& other = ToBasicShapeEllipse(o);
return center_x_ == other.center_x_ && center_y_ == other.center_y_ &&
radius_x_ == other.radius_x_ && radius_y_ == other.radius_y_;
}
float BasicShapeEllipse::FloatValueForRadiusInBox(
const BasicShapeRadius& radius,
float center,
float box_width_or_height) const {
if (radius.GetType() == BasicShapeRadius::kValue)
return FloatValueForLength(radius.Value(), box_width_or_height);
float width_or_height_delta = std::abs(box_width_or_height - center);
if (radius.GetType() == BasicShapeRadius::kClosestSide)
return std::min(std::abs(center), width_or_height_delta);
DCHECK_EQ(radius.GetType(), BasicShapeRadius::kFarthestSide);
return std::max(center, width_or_height_delta);
}
void BasicShapeEllipse::GetPath(Path& path, const FloatRect& bounding_box) {
DCHECK(path.IsEmpty());
FloatPoint center =
FloatPointForCenterCoordinate(center_x_, center_y_, bounding_box.Size());
float radius_x =
FloatValueForRadiusInBox(radius_x_, center.X(), bounding_box.Width());
float radius_y =
FloatValueForRadiusInBox(radius_y_, center.Y(), bounding_box.Height());
path.AddEllipse(FloatRect(center.X() - radius_x + bounding_box.X(),
center.Y() - radius_y + bounding_box.Y(),
radius_x * 2, radius_y * 2));
}
PassRefPtr<BasicShape> BasicShapeEllipse::Blend(const BasicShape* other,
double progress) const {
DCHECK_EQ(GetType(), other->GetType());
const BasicShapeEllipse* o = ToBasicShapeEllipse(other);
RefPtr<BasicShapeEllipse> result = BasicShapeEllipse::Create();
if (radius_x_.GetType() != BasicShapeRadius::kValue ||
o->RadiusX().GetType() != BasicShapeRadius::kValue ||
radius_y_.GetType() != BasicShapeRadius::kValue ||
o->RadiusY().GetType() != BasicShapeRadius::kValue) {
result->SetCenterX(o->CenterX());
result->SetCenterY(o->CenterY());
result->SetRadiusX(o->RadiusX());
result->SetRadiusY(o->RadiusY());
return result;
}
result->SetCenterX(center_x_.Blend(o->CenterX(), progress));
result->SetCenterY(center_y_.Blend(o->CenterY(), progress));
result->SetRadiusX(radius_x_.Blend(o->RadiusX(), progress));
result->SetRadiusY(radius_y_.Blend(o->RadiusY(), progress));
return result;
}
void BasicShapePolygon::GetPath(Path& path, const FloatRect& bounding_box) {
DCHECK(path.IsEmpty());
DCHECK(!(values_.size() % 2));
size_t length = values_.size();
if (!length)
return;
path.MoveTo(
FloatPoint(FloatValueForLength(values_.at(0), bounding_box.Width()) +
bounding_box.X(),
FloatValueForLength(values_.at(1), bounding_box.Height()) +
bounding_box.Y()));
for (size_t i = 2; i < length; i = i + 2) {
path.AddLineTo(FloatPoint(
FloatValueForLength(values_.at(i), bounding_box.Width()) +
bounding_box.X(),
FloatValueForLength(values_.at(i + 1), bounding_box.Height()) +
bounding_box.Y()));
}
path.CloseSubpath();
}
PassRefPtr<BasicShape> BasicShapePolygon::Blend(const BasicShape* other,
double progress) const {
DCHECK(other);
DCHECK(IsSameType(*other));
const BasicShapePolygon* o = ToBasicShapePolygon(other);
DCHECK_EQ(values_.size(), o->Values().size());
DCHECK(!(values_.size() % 2));
size_t length = values_.size();
RefPtr<BasicShapePolygon> result = BasicShapePolygon::Create();
if (!length)
return result;
result->SetWindRule(o->GetWindRule());
for (size_t i = 0; i < length; i = i + 2) {
result->AppendPoint(
values_.at(i).Blend(o->Values().at(i), progress, kValueRangeAll),
values_.at(i + 1).Blend(o->Values().at(i + 1), progress,
kValueRangeAll));
}
return result;
}
bool BasicShapePolygon::operator==(const BasicShape& o) const {
if (!IsSameType(o))
return false;
const BasicShapePolygon& other = ToBasicShapePolygon(o);
return wind_rule_ == other.wind_rule_ && values_ == other.values_;
}
static FloatSize FloatSizeForLengthSize(const LengthSize& length_size,
const FloatRect& bounding_box) {
return FloatSize(
FloatValueForLength(length_size.Width(), bounding_box.Width()),
FloatValueForLength(length_size.Height(), bounding_box.Height()));
}
void BasicShapeInset::GetPath(Path& path, const FloatRect& bounding_box) {
DCHECK(path.IsEmpty());
float left = FloatValueForLength(left_, bounding_box.Width());
float top = FloatValueForLength(top_, bounding_box.Height());
FloatRect rect(
left + bounding_box.X(), top + bounding_box.Y(),
std::max<float>(bounding_box.Width() - left -
FloatValueForLength(right_, bounding_box.Width()),
0),
std::max<float>(bounding_box.Height() - top -
FloatValueForLength(bottom_, bounding_box.Height()),
0));
auto radii = FloatRoundedRect::Radii(
FloatSizeForLengthSize(top_left_radius_, bounding_box),
FloatSizeForLengthSize(top_right_radius_, bounding_box),
FloatSizeForLengthSize(bottom_left_radius_, bounding_box),
FloatSizeForLengthSize(bottom_right_radius_, bounding_box));
FloatRoundedRect final_rect(rect, radii);
final_rect.ConstrainRadii();
path.AddRoundedRect(final_rect);
}
static inline LengthSize BlendLengthSize(const LengthSize& to,
const LengthSize& from,
double progress) {
return LengthSize(
to.Width().Blend(from.Width(), progress, kValueRangeNonNegative),
to.Height().Blend(from.Height(), progress, kValueRangeNonNegative));
}
PassRefPtr<BasicShape> BasicShapeInset::Blend(const BasicShape* other,
double progress) const {
DCHECK(other);
DCHECK(IsSameType(*other));
const BasicShapeInset& other_inset = ToBasicShapeInset(*other);
RefPtr<BasicShapeInset> result = BasicShapeInset::Create();
result->SetTop(top_.Blend(other_inset.Top(), progress, kValueRangeAll));
result->SetRight(right_.Blend(other_inset.Right(), progress, kValueRangeAll));
result->SetBottom(
bottom_.Blend(other_inset.Bottom(), progress, kValueRangeAll));
result->SetLeft(left_.Blend(other_inset.Left(), progress, kValueRangeAll));
result->SetTopLeftRadius(
BlendLengthSize(top_left_radius_, other_inset.TopLeftRadius(), progress));
result->SetTopRightRadius(BlendLengthSize(
top_right_radius_, other_inset.TopRightRadius(), progress));
result->SetBottomRightRadius(BlendLengthSize(
bottom_right_radius_, other_inset.BottomRightRadius(), progress));
result->SetBottomLeftRadius(BlendLengthSize(
bottom_left_radius_, other_inset.BottomLeftRadius(), progress));
return result;
}
bool BasicShapeInset::operator==(const BasicShape& o) const {
if (!IsSameType(o))
return false;
const BasicShapeInset& other = ToBasicShapeInset(o);
return right_ == other.right_ && top_ == other.top_ &&
bottom_ == other.bottom_ && left_ == other.left_ &&
top_left_radius_ == other.top_left_radius_ &&
top_right_radius_ == other.top_right_radius_ &&
bottom_right_radius_ == other.bottom_right_radius_ &&
bottom_left_radius_ == other.bottom_left_radius_;
}
} // namespace blink