Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2012 Google, Inc.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
     18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include "config.h"
     28 
     29 #include "core/rendering/svg/RenderSVGEllipse.h"
     30 
     31 #include "core/svg/SVGCircleElement.h"
     32 #include "core/svg/SVGEllipseElement.h"
     33 #include "platform/graphics/GraphicsContext.h"
     34 
     35 namespace blink {
     36 
     37 RenderSVGEllipse::RenderSVGEllipse(SVGGraphicsElement* node)
     38     : RenderSVGShape(node)
     39     , m_usePathFallback(false)
     40 {
     41 }
     42 
     43 RenderSVGEllipse::~RenderSVGEllipse()
     44 {
     45 }
     46 
     47 void RenderSVGEllipse::updateShapeFromElement()
     48 {
     49     // Before creating a new object we need to clear the cached bounding box
     50     // to avoid using garbage.
     51     m_fillBoundingBox = FloatRect();
     52     m_strokeBoundingBox = FloatRect();
     53     m_center = FloatPoint();
     54     m_radii = FloatSize();
     55     m_usePathFallback = false;
     56 
     57     calculateRadiiAndCenter();
     58 
     59     // Spec: "A negative value is an error. A value of zero disables rendering of the element."
     60     if (m_radii.width() < 0 || m_radii.height() < 0)
     61         return;
     62 
     63     if (!m_radii.isEmpty()) {
     64         // Fallback to RenderSVGShape if shape has a non-scaling stroke.
     65         if (hasNonScalingStroke()) {
     66             RenderSVGShape::updateShapeFromElement();
     67             m_usePathFallback = true;
     68             return;
     69         }
     70     }
     71 
     72     m_fillBoundingBox = FloatRect(m_center.x() - m_radii.width(), m_center.y() - m_radii.height(), 2 * m_radii.width(), 2 * m_radii.height());
     73     m_strokeBoundingBox = m_fillBoundingBox;
     74     if (style()->svgStyle().hasStroke())
     75         m_strokeBoundingBox.inflate(strokeWidth() / 2);
     76 }
     77 
     78 void RenderSVGEllipse::calculateRadiiAndCenter()
     79 {
     80     ASSERT(element());
     81     if (isSVGCircleElement(*element())) {
     82         SVGCircleElement& circle = toSVGCircleElement(*element());
     83 
     84         SVGLengthContext lengthContext(&circle);
     85         float radius = circle.r()->currentValue()->value(lengthContext);
     86         m_radii = FloatSize(radius, radius);
     87         m_center = FloatPoint(circle.cx()->currentValue()->value(lengthContext), circle.cy()->currentValue()->value(lengthContext));
     88         return;
     89     }
     90 
     91     SVGEllipseElement& ellipse = toSVGEllipseElement(*element());
     92 
     93     SVGLengthContext lengthContext(&ellipse);
     94     m_radii = FloatSize(ellipse.rx()->currentValue()->value(lengthContext), ellipse.ry()->currentValue()->value(lengthContext));
     95     m_center = FloatPoint(ellipse.cx()->currentValue()->value(lengthContext), ellipse.cy()->currentValue()->value(lengthContext));
     96 }
     97 
     98 void RenderSVGEllipse::fillShape(GraphicsContext* context) const
     99 {
    100     if (m_usePathFallback) {
    101         RenderSVGShape::fillShape(context);
    102         return;
    103     }
    104     context->fillEllipse(m_fillBoundingBox);
    105 }
    106 
    107 void RenderSVGEllipse::strokeShape(GraphicsContext* context) const
    108 {
    109     if (!style()->svgStyle().hasVisibleStroke())
    110         return;
    111     if (m_usePathFallback) {
    112         RenderSVGShape::strokeShape(context);
    113         return;
    114     }
    115     context->strokeEllipse(m_fillBoundingBox);
    116 }
    117 
    118 bool RenderSVGEllipse::shapeDependentStrokeContains(const FloatPoint& point)
    119 {
    120     // The optimized contains code below does not support non-smooth strokes so we need
    121     // to fall back to RenderSVGShape::shapeDependentStrokeContains in these cases.
    122     if (m_usePathFallback || !hasSmoothStroke()) {
    123         if (!hasPath())
    124             RenderSVGShape::updateShapeFromElement();
    125         return RenderSVGShape::shapeDependentStrokeContains(point);
    126     }
    127 
    128     float halfStrokeWidth = strokeWidth() / 2;
    129     FloatPoint center = FloatPoint(m_center.x() - point.x(), m_center.y() - point.y());
    130 
    131     // This works by checking if the point satisfies the ellipse equation,
    132     // (x/rX)^2 + (y/rY)^2 <= 1, for the outer but not the inner stroke.
    133     float xrXOuter = center.x() / (m_radii.width() + halfStrokeWidth);
    134     float yrYOuter = center.y() / (m_radii.height() + halfStrokeWidth);
    135     if (xrXOuter * xrXOuter + yrYOuter * yrYOuter > 1.0)
    136         return false;
    137 
    138     float xrXInner = center.x() / (m_radii.width() - halfStrokeWidth);
    139     float yrYInner = center.y() / (m_radii.height() - halfStrokeWidth);
    140     return xrXInner * xrXInner + yrYInner * yrYInner >= 1.0;
    141 }
    142 
    143 bool RenderSVGEllipse::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
    144 {
    145     if (m_usePathFallback)
    146         return RenderSVGShape::shapeDependentFillContains(point, fillRule);
    147 
    148     FloatPoint center = FloatPoint(m_center.x() - point.x(), m_center.y() - point.y());
    149 
    150     // This works by checking if the point satisfies the ellipse equation.
    151     // (x/rX)^2 + (y/rY)^2 <= 1
    152     float xrX = center.x() / m_radii.width();
    153     float yrY = center.y() / m_radii.height();
    154     return xrX * xrX + yrY * yrY <= 1.0;
    155 }
    156 
    157 }
    158