Index: Source/core/paint/SVGShapePainter.cpp |
diff --git a/Source/core/paint/SVGShapePainter.cpp b/Source/core/paint/SVGShapePainter.cpp |
index 6b59e3e52975893d6cadec115cf25806bcb25608..22b0e3aa083cf46cd58c977e666f694270b38968 100644 |
--- a/Source/core/paint/SVGShapePainter.cpp |
+++ b/Source/core/paint/SVGShapePainter.cpp |
@@ -20,6 +20,7 @@ |
#include "core/paint/SVGPaintContext.h" |
#include "core/paint/TransformRecorder.h" |
#include "platform/graphics/paint/DisplayItemListContextRecorder.h" |
+#include "third_party/skia/include/core/SkPaint.h" |
#include "third_party/skia/include/core/SkPicture.h" |
namespace blink { |
@@ -34,14 +35,46 @@ static bool setupNonScalingStrokeContext(AffineTransform& strokeTransform, Graph |
return true; |
} |
-static void useStrokeStyleToFill(GraphicsContext* context) |
+static bool paintForLayoutObject(const PaintInfo& paintInfo, const ComputedStyle& style, LayoutObject& layoutObject, LayoutSVGResourceMode resourceMode, SkPaint& paint, const AffineTransform* additionalPaintServerTransform = nullptr) |
{ |
- if (Gradient* gradient = context->strokeGradient()) |
- context->setFillGradient(gradient); |
- else if (Pattern* pattern = context->strokePattern()) |
- context->setFillPattern(pattern); |
- else |
- context->setFillColor(context->strokeColor()); |
+ if (paintInfo.isRenderingClipPathAsMaskImage()) { |
+ if (resourceMode == ApplyToStrokeMode) |
+ return false; |
+ paint.setColor(SVGComputedStyle::initialFillPaintColor().rgb()); |
+ paint.setShader(nullptr); |
+ return true; |
+ } |
+ |
+ SVGPaintServer paintServer = SVGPaintServer::requestForLayoutObject(layoutObject, style, resourceMode); |
+ if (!paintServer.isValid()) |
+ return false; |
+ |
+ if (additionalPaintServerTransform && paintServer.isTransformDependent()) |
+ paintServer.prependTransform(*additionalPaintServerTransform); |
+ |
+ const SVGComputedStyle& svgStyle = style.svgStyle(); |
+ float paintAlpha = resourceMode == ApplyToFillMode ? svgStyle.fillOpacity() : svgStyle.strokeOpacity(); |
+ paintServer.applyToSkPaint(paint, paintAlpha); |
+ |
+ paint.setFilterQuality(WebCoreInterpolationQualityToSkFilterQuality(InterpolationDefault)); |
+ |
+ // TODO(fs): The color filter can set when generating a picture for a mask - |
+ // due to color-interpolation. We could also just apply the |
+ // color-interpolation property from the the shape itself (which could mean |
+ // the paintserver if it has it specified), since that would be more in line |
+ // with the spec for color-interpolation. For now, just steal it from the GC |
+ // though. |
+ // Additionally, it's not really safe/guaranteed to be correct, as |
+ // something down the paint pipe may want to farther tweak the color |
+ // filter, which could yield incorrect results. (Consider just using |
+ // saveLayer() w/ this color filter explicitly instead.) |
+ paint.setColorFilter(paintInfo.context->colorFilter()); |
+ return true; |
+} |
+ |
+static SkPath::FillType fillRuleFromStyle(const PaintInfo& paintInfo, const SVGComputedStyle& svgStyle) |
+{ |
+ return WebCoreWindRuleToSkFillType(paintInfo.isRenderingClipPathAsMaskImage() ? svgStyle.clipRule() : svgStyle.fillRule()); |
} |
void SVGShapePainter::paint(const PaintInfo& paintInfo) |
@@ -64,21 +97,15 @@ void SVGShapePainter::paint(const PaintInfo& paintInfo) |
const SVGComputedStyle& svgStyle = m_renderSVGShape.style()->svgStyle(); |
bool shouldAntiAlias = svgStyle.shapeRendering() != SR_CRISPEDGES; |
- // We're munging GC paint attributes without saving first (and so does |
- // updateGraphicsContext below). This is in line with making GC less stateful, |
- // but we should keep an eye out for unintended side effects. |
- // |
- // FIXME: the mutation avoidance check should be in (all) the GC setters. |
- if (shouldAntiAlias != paintContext.paintInfo().context->shouldAntialias()) |
- paintContext.paintInfo().context->setShouldAntialias(shouldAntiAlias); |
for (int i = 0; i < 3; i++) { |
switch (svgStyle.paintOrderType(i)) { |
case PT_FILL: { |
- GraphicsContextStateSaver stateSaver(*paintContext.paintInfo().context, false); |
- if (!SVGLayoutSupport::updateGraphicsContext(paintContext.paintInfo(), stateSaver, m_renderSVGShape.styleRef(), m_renderSVGShape, ApplyToFillMode)) |
+ SkPaint fillPaint; |
+ if (!paintForLayoutObject(paintContext.paintInfo(), m_renderSVGShape.styleRef(), m_renderSVGShape, ApplyToFillMode, fillPaint)) |
break; |
- fillShape(paintContext.paintInfo().context); |
+ fillPaint.setAntiAlias(shouldAntiAlias); |
+ fillShape(paintContext.paintInfo().context, fillPaint, fillRuleFromStyle(paintContext.paintInfo(), svgStyle)); |
break; |
} |
case PT_STROKE: |
@@ -96,9 +123,16 @@ void SVGShapePainter::paint(const PaintInfo& paintInfo) |
additionalPaintServerTransform = &nonScalingTransform; |
} |
- if (!SVGLayoutSupport::updateGraphicsContext(paintContext.paintInfo(), stateSaver, m_renderSVGShape.styleRef(), m_renderSVGShape, ApplyToStrokeMode, additionalPaintServerTransform)) |
+ SkPaint strokePaint; |
+ if (!paintForLayoutObject(paintContext.paintInfo(), m_renderSVGShape.styleRef(), m_renderSVGShape, ApplyToStrokeMode, strokePaint, additionalPaintServerTransform)) |
break; |
- strokeShape(paintContext.paintInfo().context); |
+ strokePaint.setAntiAlias(shouldAntiAlias); |
+ |
+ StrokeData strokeData; |
+ SVGLayoutSupport::applyStrokeStyleToStrokeData(strokeData, m_renderSVGShape.styleRef(), m_renderSVGShape); |
+ strokeData.setupPaint(&strokePaint); |
+ |
+ strokeShape(paintContext.paintInfo().context, strokePaint); |
} |
break; |
case PT_MARKERS: |
@@ -121,39 +155,61 @@ void SVGShapePainter::paint(const PaintInfo& paintInfo) |
} |
} |
-void SVGShapePainter::fillShape(GraphicsContext* context) |
+class PathWithTemporaryWindingRule { |
+public: |
+ PathWithTemporaryWindingRule(Path& path, SkPath::FillType fillType) |
+ : m_path(const_cast<SkPath&>(path.skPath())) |
+ { |
+ m_savedFillType = m_path.getFillType(); |
+ m_path.setFillType(fillType); |
+ } |
+ ~PathWithTemporaryWindingRule() |
+ { |
+ m_path.setFillType(m_savedFillType); |
+ } |
+ |
+ const SkPath& skPath() const { return m_path; } |
+ |
+private: |
+ SkPath& m_path; |
+ SkPath::FillType m_savedFillType; |
+}; |
+ |
+void SVGShapePainter::fillShape(GraphicsContext* context, const SkPaint& paint, SkPath::FillType fillType) |
{ |
switch (m_renderSVGShape.geometryCodePath()) { |
case RectGeometryFastPath: |
- context->fillRect(m_renderSVGShape.objectBoundingBox()); |
+ context->drawRect(m_renderSVGShape.objectBoundingBox(), paint); |
break; |
case EllipseGeometryFastPath: |
- context->fillEllipse(m_renderSVGShape.objectBoundingBox()); |
+ context->drawOval(m_renderSVGShape.objectBoundingBox(), paint); |
break; |
- default: |
- context->fillPath(m_renderSVGShape.path()); |
+ default: { |
+ PathWithTemporaryWindingRule pathWithWinding(m_renderSVGShape.path(), fillType); |
+ context->drawPath(pathWithWinding.skPath(), paint); |
+ } |
} |
} |
-void SVGShapePainter::strokeShape(GraphicsContext* context) |
+void SVGShapePainter::strokeShape(GraphicsContext* context, const SkPaint& paint) |
{ |
if (!m_renderSVGShape.style()->svgStyle().hasVisibleStroke()) |
return; |
switch (m_renderSVGShape.geometryCodePath()) { |
case RectGeometryFastPath: |
- context->strokeRect(m_renderSVGShape.objectBoundingBox(), m_renderSVGShape.strokeWidth()); |
+ context->drawRect(m_renderSVGShape.objectBoundingBox(), paint); |
break; |
case EllipseGeometryFastPath: |
- context->strokeEllipse(m_renderSVGShape.objectBoundingBox()); |
+ context->drawOval(m_renderSVGShape.objectBoundingBox(), paint); |
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); |
+ context->drawPath(usePath->skPath(), paint); |
+ strokeZeroLengthLineCaps(context, paint); |
} |
} |
@@ -203,39 +259,35 @@ void SVGShapePainter::paintMarker(const PaintInfo& paintInfo, LayoutSVGResourceM |
} |
} |
-void SVGShapePainter::strokeZeroLengthLineCaps(GraphicsContext* context) |
+void SVGShapePainter::strokeZeroLengthLineCaps(GraphicsContext* context, const SkPaint& strokePaint) |
{ |
const Vector<FloatPoint>* zeroLengthLineCaps = m_renderSVGShape.zeroLengthLineCaps(); |
if (!zeroLengthLineCaps || zeroLengthLineCaps->isEmpty()) |
return; |
- Path* usePath; |
- AffineTransform nonScalingTransform; |
+ // We need a paint for filling. |
+ SkPaint fillPaint = strokePaint; |
+ fillPaint.setStyle(SkPaint::kFill_Style); |
+ AffineTransform nonScalingTransform; |
if (m_renderSVGShape.hasNonScalingStroke()) |
nonScalingTransform = m_renderSVGShape.nonScalingStrokeTransform(); |
- GraphicsContextStateSaver stateSaver(*context, true); |
- useStrokeStyleToFill(context); |
+ Path tempPath; |
for (size_t i = 0; i < zeroLengthLineCaps->size(); ++i) { |
- usePath = zeroLengthLinecapPath((*zeroLengthLineCaps)[i]); |
+ FloatRect subpathRect = LayoutSVGPath::zeroLengthSubpathRect((*zeroLengthLineCaps)[i], m_renderSVGShape.strokeWidth()); |
+ tempPath.clear(); |
+ if (m_renderSVGShape.style()->svgStyle().capStyle() == SquareCap) |
+ tempPath.addRect(subpathRect); |
+ else |
+ tempPath.addEllipse(subpathRect); |
+ // This open-codes LayoutSVGShape::nonScalingStrokePath, because the |
+ // requirements here differ (we have a temporary path that we can |
+ // mutate.) |
if (m_renderSVGShape.hasNonScalingStroke()) |
- usePath = m_renderSVGShape.nonScalingStrokePath(usePath, nonScalingTransform); |
- context->fillPath(*usePath); |
+ tempPath.transform(nonScalingTransform); |
+ context->drawPath(tempPath.skPath(), fillPaint); |
} |
} |
-Path* SVGShapePainter::zeroLengthLinecapPath(const FloatPoint& linecapPosition) const |
-{ |
- DEFINE_STATIC_LOCAL(Path, tempPath, ()); |
- |
- tempPath.clear(); |
- if (m_renderSVGShape.style()->svgStyle().capStyle() == SquareCap) |
- tempPath.addRect(LayoutSVGPath::zeroLengthSubpathRect(linecapPosition, m_renderSVGShape.strokeWidth())); |
- else |
- tempPath.addEllipse(LayoutSVGPath::zeroLengthSubpathRect(linecapPosition, m_renderSVGShape.strokeWidth())); |
- |
- return &tempPath; |
-} |
- |
} // namespace blink |