blob: e0359b324a7b9b448b445409d4801174c94b96db [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "config.h"
#include "core/paint/SVGShapePainter.h"
#include "core/paint/ObjectPainter.h"
#include "core/paint/SVGContainerPainter.h"
#include "core/rendering/GraphicsContextAnnotator.h"
#include "core/rendering/PaintInfo.h"
#include "core/rendering/svg/RenderSVGPath.h"
#include "core/rendering/svg/RenderSVGResourceMarker.h"
#include "core/rendering/svg/RenderSVGShape.h"
#include "core/rendering/svg/SVGMarkerData.h"
#include "core/rendering/svg/SVGRenderSupport.h"
#include "core/rendering/svg/SVGRenderingContext.h"
#include "core/rendering/svg/SVGResources.h"
#include "core/rendering/svg/SVGResourcesCache.h"
namespace blink {
static bool setupNonScalingStrokeContext(AffineTransform& strokeTransform, GraphicsContextStateSaver& stateSaver)
{
if (!strokeTransform.isInvertible())
return false;
stateSaver.save();
stateSaver.context()->concatCTM(strokeTransform.inverse());
return true;
}
static void useStrokeStyleToFill(GraphicsContext* context)
{
if (Gradient* gradient = context->strokeGradient())
context->setFillGradient(gradient);
else if (Pattern* pattern = context->strokePattern())
context->setFillPattern(pattern);
else
context->setFillColor(context->strokeColor());
}
void SVGShapePainter::paint(PaintInfo& paintInfo)
{
ANNOTATE_GRAPHICS_CONTEXT(paintInfo, &m_renderSVGShape);
if (paintInfo.phase != PaintPhaseForeground
|| m_renderSVGShape.style()->visibility() == HIDDEN
|| m_renderSVGShape.isShapeEmpty())
return;
FloatRect boundingBox = m_renderSVGShape.paintInvalidationRectInLocalCoordinates();
if (!SVGRenderSupport::paintInfoIntersectsPaintInvalidationRect(boundingBox, m_renderSVGShape.localTransform(), paintInfo))
return;
PaintInfo childPaintInfo(paintInfo);
GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
childPaintInfo.applyTransform(m_renderSVGShape.localTransform());
SVGRenderingContext renderingContext(&m_renderSVGShape, childPaintInfo);
if (renderingContext.isRenderingPrepared()) {
const SVGRenderStyle& svgStyle = m_renderSVGShape.style()->svgStyle();
if (svgStyle.shapeRendering() == SR_CRISPEDGES)
childPaintInfo.context->setShouldAntialias(false);
for (int i = 0; i < 3; i++) {
switch (svgStyle.paintOrderType(i)) {
case PT_FILL: {
GraphicsContextStateSaver stateSaver(*childPaintInfo.context, false);
if (!SVGRenderSupport::updateGraphicsContext(stateSaver, m_renderSVGShape.style(), m_renderSVGShape, ApplyToFillMode))
break;
fillShape(childPaintInfo.context);
break;
}
case PT_STROKE:
if (svgStyle.hasVisibleStroke()) {
GraphicsContextStateSaver stateSaver(*childPaintInfo.context, false);
AffineTransform nonScalingTransform;
const AffineTransform* additionalPaintServerTransform = 0;
if (m_renderSVGShape.hasNonScalingStroke()) {
nonScalingTransform = m_renderSVGShape.nonScalingStrokeTransform();
if (!setupNonScalingStrokeContext(nonScalingTransform, stateSaver))
return;
// Non-scaling stroke needs to reset the transform back to the host transform.
additionalPaintServerTransform = &nonScalingTransform;
}
if (!SVGRenderSupport::updateGraphicsContext(stateSaver, m_renderSVGShape.style(), m_renderSVGShape, ApplyToStrokeMode, additionalPaintServerTransform))
break;
strokeShape(childPaintInfo.context);
}
break;
case PT_MARKERS:
paintMarkers(childPaintInfo);
break;
default:
ASSERT_NOT_REACHED();
break;
}
}
}
if (m_renderSVGShape.style()->outlineWidth())
ObjectPainter(m_renderSVGShape).paintOutline(childPaintInfo, IntRect(boundingBox));
}
void SVGShapePainter::fillShape(GraphicsContext* context)
{
switch (m_renderSVGShape.geometryCodePath()) {
case RectGeometryFastPath:
context->fillRect(m_renderSVGShape.objectBoundingBox());
break;
case EllipseGeometryFastPath:
context->fillEllipse(m_renderSVGShape.objectBoundingBox());
break;
default:
context->fillPath(m_renderSVGShape.path());
}
}
void SVGShapePainter::strokeShape(GraphicsContext* context)
{
if (!m_renderSVGShape.style()->svgStyle().hasVisibleStroke())
return;
switch (m_renderSVGShape.geometryCodePath()) {
case RectGeometryFastPath:
context->strokeRect(m_renderSVGShape.objectBoundingBox(), m_renderSVGShape.strokeWidth());
break;
case EllipseGeometryFastPath:
context->strokeEllipse(m_renderSVGShape.objectBoundingBox());
break;
default:
ASSERT(m_renderSVGShape.hasPath());
Path* usePath = &m_renderSVGShape.path();
if (m_renderSVGShape.hasNonScalingStroke())
usePath = m_renderSVGShape.nonScalingStrokePath(usePath, m_renderSVGShape.nonScalingStrokeTransform());
context->strokePath(*usePath);
strokeZeroLengthLineCaps(context);
}
}
void SVGShapePainter::paintMarkers(PaintInfo& paintInfo)
{
const Vector<MarkerPosition>* markerPositions = m_renderSVGShape.markerPositions();
if (!markerPositions || markerPositions->isEmpty())
return;
SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(&m_renderSVGShape);
if (!resources)
return;
RenderSVGResourceMarker* markerStart = resources->markerStart();
RenderSVGResourceMarker* markerMid = resources->markerMid();
RenderSVGResourceMarker* markerEnd = resources->markerEnd();
if (!markerStart && !markerMid && !markerEnd)
return;
float strokeWidth = m_renderSVGShape.strokeWidth();
unsigned size = markerPositions->size();
for (unsigned i = 0; i < size; ++i) {
if (RenderSVGResourceMarker* marker = SVGMarkerData::markerForType((*markerPositions)[i].type, markerStart, markerMid, markerEnd))
paintMarker(paintInfo, *marker, (*markerPositions)[i], strokeWidth);
}
}
void SVGShapePainter::paintMarker(PaintInfo& paintInfo, RenderSVGResourceMarker& marker, const MarkerPosition& position, float strokeWidth)
{
// An empty viewBox disables rendering.
SVGMarkerElement* markerElement = toSVGMarkerElement(marker.element());
ASSERT(markerElement);
if (markerElement->hasAttribute(SVGNames::viewBoxAttr) && markerElement->viewBox()->currentValue()->isValid() && markerElement->viewBox()->currentValue()->value().isEmpty())
return;
PaintInfo info(paintInfo);
GraphicsContextStateSaver stateSaver(*info.context, false);
info.applyTransform(marker.markerTransformation(position.origin, position.angle, strokeWidth), &stateSaver);
if (SVGRenderSupport::isOverflowHidden(&marker)) {
stateSaver.saveIfNeeded();
info.context->clip(marker.viewport());
}
SVGContainerPainter(marker).paint(info);
}
void SVGShapePainter::strokeZeroLengthLineCaps(GraphicsContext* context)
{
const Vector<FloatPoint>* zeroLengthLineCaps = m_renderSVGShape.zeroLengthLineCaps();
if (!zeroLengthLineCaps || zeroLengthLineCaps->isEmpty())
return;
Path* usePath;
AffineTransform nonScalingTransform;
if (m_renderSVGShape.hasNonScalingStroke())
nonScalingTransform = m_renderSVGShape.nonScalingStrokeTransform();
GraphicsContextStateSaver stateSaver(*context, true);
useStrokeStyleToFill(context);
for (size_t i = 0; i < zeroLengthLineCaps->size(); ++i) {
usePath = zeroLengthLinecapPath((*zeroLengthLineCaps)[i]);
if (m_renderSVGShape.hasNonScalingStroke())
usePath = m_renderSVGShape.nonScalingStrokePath(usePath, nonScalingTransform);
context->fillPath(*usePath);
}
}
Path* SVGShapePainter::zeroLengthLinecapPath(const FloatPoint& linecapPosition) const
{
DEFINE_STATIC_LOCAL(Path, tempPath, ());
tempPath.clear();
if (m_renderSVGShape.style()->svgStyle().capStyle() == SquareCap)
tempPath.addRect(RenderSVGPath::zeroLengthSubpathRect(linecapPosition, m_renderSVGShape.strokeWidth()));
else
tempPath.addEllipse(RenderSVGPath::zeroLengthSubpathRect(linecapPosition, m_renderSVGShape.strokeWidth()));
return &tempPath;
}
} // namespace blink