blob: 0e78210def83ae33647ac2345e664ccc92e5060b [file] [log] [blame]
/*
* Copyright (C) 2012 Google, Inc.
* 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED OR
* CONTRIBUTORS 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 "third_party/blink/renderer/core/layout/svg/layout_svg_ellipse.h"
#include <cmath>
#include "third_party/blink/renderer/core/svg/svg_circle_element.h"
#include "third_party/blink/renderer/core/svg/svg_ellipse_element.h"
namespace blink {
LayoutSVGEllipse::LayoutSVGEllipse(SVGGeometryElement* node)
: LayoutSVGShape(node, kSimple), use_path_fallback_(false) {}
LayoutSVGEllipse::~LayoutSVGEllipse() = default;
void LayoutSVGEllipse::UpdateShapeFromElement() {
// Before creating a new object we need to clear the cached bounding box
// to avoid using garbage.
fill_bounding_box_ = FloatRect();
stroke_bounding_box_ = FloatRect();
center_ = FloatPoint();
radii_ = FloatSize();
use_path_fallback_ = false;
CalculateRadiiAndCenter();
// Spec: "A negative value is an error. A value of zero disables rendering of
// the element."
if (radii_.Width() < 0 || radii_.Height() < 0)
return;
if (!radii_.IsEmpty()) {
// Fall back to LayoutSVGShape and path-based hit detection if the ellipse
// has a non-scaling or discontinuous stroke.
// However, only use LayoutSVGShape bounding-box calculations for the
// non-scaling stroke case, since the computation below should be accurate
// for the other cases.
if (HasNonScalingStroke()) {
LayoutSVGShape::UpdateShapeFromElement();
use_path_fallback_ = true;
return;
}
if (!HasContinuousStroke()) {
CreatePath();
use_path_fallback_ = true;
}
}
if (!use_path_fallback_)
ClearPath();
fill_bounding_box_ = FloatRect(center_ - radii_, radii_.ScaledBy(2));
stroke_bounding_box_ = CalculateStrokeBoundingBox();
}
void LayoutSVGEllipse::CalculateRadiiAndCenter() {
DCHECK(GetElement());
SVGLengthContext length_context(GetElement());
const ComputedStyle& style = StyleRef();
const SVGComputedStyle& svg_style = style.SvgStyle();
center_ =
length_context.ResolveLengthPair(svg_style.Cx(), svg_style.Cy(), style);
if (IsSVGCircleElement(*GetElement())) {
float radius = length_context.ValueForLength(svg_style.R(), style,
SVGLengthMode::kOther);
radii_ = FloatSize(radius, radius);
} else {
radii_ = ToFloatSize(length_context.ResolveLengthPair(
svg_style.Rx(), svg_style.Ry(), style));
if (svg_style.Rx().IsAuto())
radii_.SetWidth(radii_.Height());
else if (svg_style.Ry().IsAuto())
radii_.SetHeight(radii_.Width());
}
}
bool LayoutSVGEllipse::ShapeDependentStrokeContains(const FloatPoint& point) {
if (radii_.Width() < 0 || radii_.Height() < 0)
return false;
// The optimized check below for circles does not support non-circular and
// the cases that we set use_path_fallback_ in UpdateShapeFromElement().
if (use_path_fallback_ || radii_.Width() != radii_.Height())
return LayoutSVGShape::ShapeDependentStrokeContains(point);
const FloatPoint center =
FloatPoint(center_.X() - point.X(), center_.Y() - point.Y());
const float half_stroke_width = StrokeWidth() / 2;
const float r = radii_.Width();
return std::abs(center.length() - r) <= half_stroke_width;
}
bool LayoutSVGEllipse::ShapeDependentFillContains(
const FloatPoint& point,
const WindRule fill_rule) const {
const FloatPoint center =
FloatPoint(center_.X() - point.X(), center_.Y() - point.Y());
// This works by checking if the point satisfies the ellipse equation.
// (x/rX)^2 + (y/rY)^2 <= 1
const float xr_x = center.X() / radii_.Width();
const float yr_y = center.Y() / radii_.Height();
return xr_x * xr_x + yr_y * yr_y <= 1.0;
}
bool LayoutSVGEllipse::HasContinuousStroke() const {
const SVGComputedStyle& svg_style = StyleRef().SvgStyle();
return svg_style.StrokeDashArray()->IsEmpty();
}
} // namespace blink