15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/*
25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Copyright (C) 2012 Google, Inc.
35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * All rights reserved.
45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Redistribution and use in source and binary forms, with or without
65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * modification, are permitted provided that the following conditions
75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * are met:
85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 1. Redistributions of source code must retain the above copyright
95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *    notice, this list of conditions and the following disclaimer.
105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 2. Redistributions in binary form must reproduce the above copyright
115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *    notice, this list of conditions and the following disclaimer in the
125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *    documentation and/or other materials provided with the distribution.
135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */
265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "config.h"
285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/rendering/svg/RenderSVGEllipse.h"
305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/svg/SVGCircleElement.h"
3253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/svg/SVGEllipseElement.h"
335d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)#include "platform/graphics/GraphicsContext.h"
345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
35c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)namespace blink {
365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
37591b958dee2cf159d33a0b931e6231072eaf38d5Ben MurdochRenderSVGEllipse::RenderSVGEllipse(SVGGraphicsElement* node)
385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    : RenderSVGShape(node)
395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_usePathFallback(false)
405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)RenderSVGEllipse::~RenderSVGEllipse()
445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void RenderSVGEllipse::updateShapeFromElement()
485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // Before creating a new object we need to clear the cached bounding box
505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // to avoid using garbage.
515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_fillBoundingBox = FloatRect();
525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_strokeBoundingBox = FloatRect();
535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_center = FloatPoint();
545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_radii = FloatSize();
557242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    m_usePathFallback = false;
565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    calculateRadiiAndCenter();
585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5907a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch    // Spec: "A negative value is an error. A value of zero disables rendering of the element."
60f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    if (m_radii.width() < 0 || m_radii.height() < 0)
615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
63f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    if (!m_radii.isEmpty()) {
64f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        // Fallback to RenderSVGShape if shape has a non-scaling stroke.
65f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        if (hasNonScalingStroke()) {
66f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            RenderSVGShape::updateShapeFromElement();
67f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            m_usePathFallback = true;
68f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            return;
69f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        }
70f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    }
71f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_fillBoundingBox = FloatRect(m_center.x() - m_radii.width(), m_center.y() - m_radii.height(), 2 * m_radii.width(), 2 * m_radii.height());
735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_strokeBoundingBox = m_fillBoundingBox;
74197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    if (style()->svgStyle().hasStroke())
755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_strokeBoundingBox.inflate(strokeWidth() / 2);
765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void RenderSVGEllipse::calculateRadiiAndCenter()
795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
808abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)    ASSERT(element());
81d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    if (isSVGCircleElement(*element())) {
82d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        SVGCircleElement& circle = toSVGCircleElement(*element());
835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
84d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        SVGLengthContext lengthContext(&circle);
85d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        float radius = circle.r()->currentValue()->value(lengthContext);
865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_radii = FloatSize(radius, radius);
87d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        m_center = FloatPoint(circle.cx()->currentValue()->value(lengthContext), circle.cy()->currentValue()->value(lengthContext));
885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
91d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    SVGEllipseElement& ellipse = toSVGEllipseElement(*element());
925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
93d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    SVGLengthContext lengthContext(&ellipse);
94d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    m_radii = FloatSize(ellipse.rx()->currentValue()->value(lengthContext), ellipse.ry()->currentValue()->value(lengthContext));
95d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    m_center = FloatPoint(ellipse.cx()->currentValue()->value(lengthContext), ellipse.cy()->currentValue()->value(lengthContext));
965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void RenderSVGEllipse::fillShape(GraphicsContext* context) const
995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_usePathFallback) {
1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        RenderSVGShape::fillShape(context);
1025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    context->fillEllipse(m_fillBoundingBox);
1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void RenderSVGEllipse::strokeShape(GraphicsContext* context) const
1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
109197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    if (!style()->svgStyle().hasVisibleStroke())
1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
1115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_usePathFallback) {
1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        RenderSVGShape::strokeShape(context);
1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    context->strokeEllipse(m_fillBoundingBox);
1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)bool RenderSVGEllipse::shapeDependentStrokeContains(const FloatPoint& point)
1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // The optimized contains code below does not support non-smooth strokes so we need
1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // to fall back to RenderSVGShape::shapeDependentStrokeContains in these cases.
1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_usePathFallback || !hasSmoothStroke()) {
1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (!hasPath())
1245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            RenderSVGShape::updateShapeFromElement();
1255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return RenderSVGShape::shapeDependentStrokeContains(point);
1265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    float halfStrokeWidth = strokeWidth() / 2;
1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    FloatPoint center = FloatPoint(m_center.x() - point.x(), m_center.y() - point.y());
1305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // This works by checking if the point satisfies the ellipse equation,
1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // (x/rX)^2 + (y/rY)^2 <= 1, for the outer but not the inner stroke.
1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    float xrXOuter = center.x() / (m_radii.width() + halfStrokeWidth);
1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    float yrYOuter = center.y() / (m_radii.height() + halfStrokeWidth);
1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (xrXOuter * xrXOuter + yrYOuter * yrYOuter > 1.0)
1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return false;
1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    float xrXInner = center.x() / (m_radii.width() - halfStrokeWidth);
1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    float yrYInner = center.y() / (m_radii.height() - halfStrokeWidth);
1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return xrXInner * xrXInner + yrYInner * yrYInner >= 1.0;
1415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)bool RenderSVGEllipse::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
1445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_usePathFallback)
1465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return RenderSVGShape::shapeDependentFillContains(point, fillRule);
1475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    FloatPoint center = FloatPoint(m_center.x() - point.x(), m_center.y() - point.y());
1495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // This works by checking if the point satisfies the ellipse equation.
1515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // (x/rX)^2 + (y/rY)^2 <= 1
1525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    float xrX = center.x() / m_radii.width();
1535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    float yrY = center.y() / m_radii.height();
1545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return xrX * xrX + yrY * yrY <= 1.0;
1555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
158