Index: Source/core/paint/SVGShapePainter.cpp |
diff --git a/Source/core/paint/SVGShapePainter.cpp b/Source/core/paint/SVGShapePainter.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..442aa49ee84a8e8be543040dbfedcd8d1f0448c9 |
--- /dev/null |
+++ b/Source/core/paint/SVGShapePainter.cpp |
@@ -0,0 +1,208 @@ |
+// 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/SVGMarkerPainter.h" |
+#include "core/rendering/GraphicsContextAnnotator.h" |
+#include "core/rendering/PaintInfo.h" |
+#include "core/rendering/svg/RenderSVGPath.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) |
+{ |
+ if (m_renderSVGShape.useRectRenderingFastPath()) { |
+ context->fillRect(m_renderSVGShape.objectBoundingBox()); |
+ return; |
+ } |
+ |
+ if (m_renderSVGShape.useEllipseRenderingFastPath()) { |
+ context->fillEllipse(m_renderSVGShape.objectBoundingBox()); |
+ return; |
+ } |
+ |
+ context->fillPath(m_renderSVGShape.path()); |
+} |
+ |
+void SVGShapePainter::strokeShape(GraphicsContext* context) |
+{ |
+ if (!m_renderSVGShape.style()->svgStyle().hasVisibleStroke()) |
+ return; |
+ |
+ if (m_renderSVGShape.useRectRenderingFastPath()) { |
+ context->strokeRect(m_renderSVGShape.objectBoundingBox(), m_renderSVGShape.strokeWidth()); |
+ return; |
+ } |
+ |
+ if (m_renderSVGShape.useEllipseRenderingFastPath()) { |
+ context->strokeEllipse(m_renderSVGShape.objectBoundingBox()); |
+ return; |
+ } |
+ |
+ 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)) |
+ SVGMarkerPainter(*marker).paint(paintInfo, (*markerPositions)[i], strokeWidth); |
+ } |
+} |
+ |
+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 |