OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "config.h" | 5 #include "config.h" |
6 #include "core/paint/SVGShapePainter.h" | 6 #include "core/paint/SVGShapePainter.h" |
7 | 7 |
8 #include "core/layout/PaintInfo.h" | 8 #include "core/layout/PaintInfo.h" |
9 #include "core/layout/svg/LayoutSVGPath.h" | 9 #include "core/layout/svg/LayoutSVGPath.h" |
10 #include "core/layout/svg/LayoutSVGResourceMarker.h" | 10 #include "core/layout/svg/LayoutSVGResourceMarker.h" |
11 #include "core/layout/svg/LayoutSVGShape.h" | 11 #include "core/layout/svg/LayoutSVGShape.h" |
12 #include "core/layout/svg/SVGLayoutSupport.h" | 12 #include "core/layout/svg/SVGLayoutSupport.h" |
13 #include "core/layout/svg/SVGMarkerData.h" | 13 #include "core/layout/svg/SVGMarkerData.h" |
14 #include "core/layout/svg/SVGResources.h" | 14 #include "core/layout/svg/SVGResources.h" |
15 #include "core/layout/svg/SVGResourcesCache.h" | 15 #include "core/layout/svg/SVGResourcesCache.h" |
16 #include "core/paint/GraphicsContextAnnotator.h" | 16 #include "core/paint/GraphicsContextAnnotator.h" |
17 #include "core/paint/LayoutObjectDrawingRecorder.h" | 17 #include "core/paint/LayoutObjectDrawingRecorder.h" |
18 #include "core/paint/ObjectPainter.h" | 18 #include "core/paint/ObjectPainter.h" |
19 #include "core/paint/SVGContainerPainter.h" | 19 #include "core/paint/SVGContainerPainter.h" |
20 #include "core/paint/SVGPaintContext.h" | 20 #include "core/paint/SVGPaintContext.h" |
21 #include "core/paint/TransformRecorder.h" | 21 #include "core/paint/TransformRecorder.h" |
22 #include "platform/graphics/paint/DisplayItemListContextRecorder.h" | 22 #include "platform/graphics/paint/DisplayItemListContextRecorder.h" |
23 #include "third_party/skia/include/core/SkPaint.h" | |
23 #include "third_party/skia/include/core/SkPicture.h" | 24 #include "third_party/skia/include/core/SkPicture.h" |
24 | 25 |
25 namespace blink { | 26 namespace blink { |
26 | 27 |
27 static bool setupNonScalingStrokeContext(AffineTransform& strokeTransform, Graph icsContextStateSaver& stateSaver) | 28 static bool setupNonScalingStrokeContext(AffineTransform& strokeTransform, Graph icsContextStateSaver& stateSaver) |
28 { | 29 { |
29 if (!strokeTransform.isInvertible()) | 30 if (!strokeTransform.isInvertible()) |
30 return false; | 31 return false; |
31 | 32 |
32 stateSaver.save(); | 33 stateSaver.save(); |
33 stateSaver.context()->concatCTM(strokeTransform.inverse()); | 34 stateSaver.context()->concatCTM(strokeTransform.inverse()); |
34 return true; | 35 return true; |
35 } | 36 } |
36 | 37 |
37 static void useStrokeStyleToFill(GraphicsContext* context) | 38 static bool paintForLayoutObject(const PaintInfo& paintInfo, const ComputedStyle & style, LayoutObject& layoutObject, LayoutSVGResourceMode resourceMode, SkPaint & paint, const AffineTransform* additionalPaintServerTransform = nullptr) |
38 { | 39 { |
39 if (Gradient* gradient = context->strokeGradient()) | 40 if (paintInfo.isRenderingClipPathAsMaskImage()) { |
40 context->setFillGradient(gradient); | 41 if (resourceMode == ApplyToStrokeMode) |
41 else if (Pattern* pattern = context->strokePattern()) | 42 return false; |
42 context->setFillPattern(pattern); | 43 paint.setColor(SVGComputedStyle::initialFillPaintColor().rgb()); |
43 else | 44 paint.setShader(nullptr); |
44 context->setFillColor(context->strokeColor()); | 45 return true; |
46 } | |
47 | |
48 SVGPaintServer paintServer = SVGPaintServer::requestForLayoutObject(layoutOb ject, style, resourceMode); | |
49 if (!paintServer.isValid()) | |
50 return false; | |
51 | |
52 if (additionalPaintServerTransform && paintServer.isTransformDependent()) | |
53 paintServer.prependTransform(*additionalPaintServerTransform); | |
54 | |
55 const SVGComputedStyle& svgStyle = style.svgStyle(); | |
56 float paintAlpha = resourceMode == ApplyToFillMode ? svgStyle.fillOpacity() : svgStyle.strokeOpacity(); | |
57 paintServer.transferToSkPaint(paint, paintAlpha); | |
f(malita)
2015/04/02 15:51:12
Would it make sense to compute the paintAlpha in S
fs
2015/04/03 18:19:44
I believe I had it there a while when working on t
fs
2015/04/07 12:03:56
I revisited this, and while it probably would be a
| |
58 | |
59 paint.setFilterQuality(WebCoreInterpolationQualityToSkFilterQuality(Interpol ationDefault)); | |
60 | |
61 if (resourceMode == ApplyToFillMode) | |
62 paintInfo.context->setFillRule(svgStyle.fillRule()); | |
63 // TODO(fs): The color filter can set when generating a picture for a mask - | |
64 // due to color-interpolation. We could also just apply the | |
65 // color-interpolation property from the the shape itself (which could mean | |
66 // the paintserver if it has it specified), since that would be more in line | |
67 // with the spec for color-interpolation. For now, just steal it from the GC | |
68 // though. | |
69 paint.setColorFilter(paintInfo.context->colorFilter()); | |
f(malita)
2015/04/02 15:51:12
This is one of the problematic GC states: we can s
fs
2015/04/03 18:19:44
Hmm, examples? It would boil down to ordering then
fs
2015/04/07 12:03:56
I've extended the comment, borrowing some of the a
f(malita)
2015/04/07 12:44:30
I was thinking mainly of nesting (masks vs. filter
| |
70 return true; | |
45 } | 71 } |
46 | 72 |
47 void SVGShapePainter::paint(const PaintInfo& paintInfo) | 73 void SVGShapePainter::paint(const PaintInfo& paintInfo) |
48 { | 74 { |
49 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, &m_renderSVGShape); | 75 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, &m_renderSVGShape); |
50 if (paintInfo.phase != PaintPhaseForeground | 76 if (paintInfo.phase != PaintPhaseForeground |
51 || m_renderSVGShape.style()->visibility() == HIDDEN | 77 || m_renderSVGShape.style()->visibility() == HIDDEN |
52 || m_renderSVGShape.isShapeEmpty()) | 78 || m_renderSVGShape.isShapeEmpty()) |
53 return; | 79 return; |
54 | 80 |
55 PaintInfo paintInfoBeforeFiltering(paintInfo); | 81 PaintInfo paintInfoBeforeFiltering(paintInfo); |
56 FloatRect boundingBox = m_renderSVGShape.paintInvalidationRectInLocalCoordin ates(); | 82 FloatRect boundingBox = m_renderSVGShape.paintInvalidationRectInLocalCoordin ates(); |
57 | 83 |
58 TransformRecorder transformRecorder(*paintInfoBeforeFiltering.context, m_ren derSVGShape, m_renderSVGShape.localTransform()); | 84 TransformRecorder transformRecorder(*paintInfoBeforeFiltering.context, m_ren derSVGShape, m_renderSVGShape.localTransform()); |
59 { | 85 { |
60 SVGPaintContext paintContext(m_renderSVGShape, paintInfoBeforeFiltering) ; | 86 SVGPaintContext paintContext(m_renderSVGShape, paintInfoBeforeFiltering) ; |
61 if (paintContext.applyClipMaskAndFilterIfNecessary()) { | 87 if (paintContext.applyClipMaskAndFilterIfNecessary()) { |
62 LayoutObjectDrawingRecorder recorder(*paintContext.paintInfo().conte xt, m_renderSVGShape, paintContext.paintInfo().phase, boundingBox); | 88 LayoutObjectDrawingRecorder recorder(*paintContext.paintInfo().conte xt, m_renderSVGShape, paintContext.paintInfo().phase, boundingBox); |
63 if (!recorder.canUseCachedDrawing()) { | 89 if (!recorder.canUseCachedDrawing()) { |
64 const SVGComputedStyle& svgStyle = m_renderSVGShape.style()->svg Style(); | 90 const SVGComputedStyle& svgStyle = m_renderSVGShape.style()->svg Style(); |
65 | 91 |
66 bool shouldAntiAlias = svgStyle.shapeRendering() != SR_CRISPEDGE S; | 92 bool shouldAntiAlias = svgStyle.shapeRendering() != SR_CRISPEDGE S; |
67 // We're munging GC paint attributes without saving first (and s o does | |
68 // updateGraphicsContext below). This is in line with making GC less stateful, | |
69 // but we should keep an eye out for unintended side effects. | |
70 // | |
71 // FIXME: the mutation avoidance check should be in (all) the GC setters. | |
72 if (shouldAntiAlias != paintContext.paintInfo().context->shouldA ntialias()) | |
73 paintContext.paintInfo().context->setShouldAntialias(shouldA ntiAlias); | |
74 | 93 |
75 for (int i = 0; i < 3; i++) { | 94 for (int i = 0; i < 3; i++) { |
76 switch (svgStyle.paintOrderType(i)) { | 95 switch (svgStyle.paintOrderType(i)) { |
77 case PT_FILL: { | 96 case PT_FILL: { |
78 GraphicsContextStateSaver stateSaver(*paintContext.paint Info().context, false); | 97 SkPaint fillPaint; |
79 if (!SVGLayoutSupport::updateGraphicsContext(paintContex t.paintInfo(), stateSaver, m_renderSVGShape.styleRef(), m_renderSVGShape, ApplyT oFillMode)) | 98 if (!paintForLayoutObject(paintContext.paintInfo(), m_re nderSVGShape.styleRef(), m_renderSVGShape, ApplyToFillMode, fillPaint)) |
80 break; | 99 break; |
81 fillShape(paintContext.paintInfo().context); | 100 fillPaint.setAntiAlias(shouldAntiAlias); |
101 fillShape(paintContext.paintInfo().context, fillPaint); | |
82 break; | 102 break; |
83 } | 103 } |
84 case PT_STROKE: | 104 case PT_STROKE: |
85 if (svgStyle.hasVisibleStroke()) { | 105 if (svgStyle.hasVisibleStroke()) { |
86 GraphicsContextStateSaver stateSaver(*paintContext.p aintInfo().context, false); | 106 GraphicsContextStateSaver stateSaver(*paintContext.p aintInfo().context, false); |
87 AffineTransform nonScalingTransform; | 107 AffineTransform nonScalingTransform; |
88 const AffineTransform* additionalPaintServerTransfor m = 0; | 108 const AffineTransform* additionalPaintServerTransfor m = 0; |
89 | 109 |
90 if (m_renderSVGShape.hasNonScalingStroke()) { | 110 if (m_renderSVGShape.hasNonScalingStroke()) { |
91 nonScalingTransform = m_renderSVGShape.nonScalin gStrokeTransform(); | 111 nonScalingTransform = m_renderSVGShape.nonScalin gStrokeTransform(); |
92 if (!setupNonScalingStrokeContext(nonScalingTran sform, stateSaver)) | 112 if (!setupNonScalingStrokeContext(nonScalingTran sform, stateSaver)) |
93 return; | 113 return; |
94 | 114 |
95 // Non-scaling stroke needs to reset the transfo rm back to the host transform. | 115 // Non-scaling stroke needs to reset the transfo rm back to the host transform. |
96 additionalPaintServerTransform = &nonScalingTran sform; | 116 additionalPaintServerTransform = &nonScalingTran sform; |
97 } | 117 } |
98 | 118 |
99 if (!SVGLayoutSupport::updateGraphicsContext(paintCo ntext.paintInfo(), stateSaver, m_renderSVGShape.styleRef(), m_renderSVGShape, Ap plyToStrokeMode, additionalPaintServerTransform)) | 119 SkPaint strokePaint; |
120 if (!paintForLayoutObject(paintContext.paintInfo(), m_renderSVGShape.styleRef(), m_renderSVGShape, ApplyToStrokeMode, strokePaint, a dditionalPaintServerTransform)) | |
100 break; | 121 break; |
101 strokeShape(paintContext.paintInfo().context); | 122 strokePaint.setAntiAlias(shouldAntiAlias); |
123 | |
124 StrokeData strokeData; | |
125 SVGLayoutSupport::applyStrokeStyleToStrokeData(strok eData, m_renderSVGShape.styleRef(), m_renderSVGShape); | |
126 strokeData.setupPaint(&strokePaint); | |
127 | |
128 strokeShape(paintContext.paintInfo().context, stroke Paint); | |
102 } | 129 } |
103 break; | 130 break; |
104 case PT_MARKERS: | 131 case PT_MARKERS: |
105 paintMarkers(paintContext.paintInfo()); | 132 paintMarkers(paintContext.paintInfo()); |
106 break; | 133 break; |
107 default: | 134 default: |
108 ASSERT_NOT_REACHED(); | 135 ASSERT_NOT_REACHED(); |
109 break; | 136 break; |
110 } | 137 } |
111 } | 138 } |
112 } | 139 } |
113 } | 140 } |
114 } | 141 } |
115 | 142 |
116 if (m_renderSVGShape.style()->outlineWidth()) { | 143 if (m_renderSVGShape.style()->outlineWidth()) { |
117 PaintInfo outlinePaintInfo(paintInfoBeforeFiltering); | 144 PaintInfo outlinePaintInfo(paintInfoBeforeFiltering); |
118 outlinePaintInfo.phase = PaintPhaseSelfOutline; | 145 outlinePaintInfo.phase = PaintPhaseSelfOutline; |
119 LayoutRect layoutObjectBounds(boundingBox); | 146 LayoutRect layoutObjectBounds(boundingBox); |
120 ObjectPainter(m_renderSVGShape).paintOutline(outlinePaintInfo, layoutObj ectBounds, layoutObjectBounds); | 147 ObjectPainter(m_renderSVGShape).paintOutline(outlinePaintInfo, layoutObj ectBounds, layoutObjectBounds); |
121 } | 148 } |
122 } | 149 } |
123 | 150 |
124 void SVGShapePainter::fillShape(GraphicsContext* context) | 151 void SVGShapePainter::fillShape(GraphicsContext* context, const SkPaint& paint) |
125 { | 152 { |
126 switch (m_renderSVGShape.geometryCodePath()) { | 153 switch (m_renderSVGShape.geometryCodePath()) { |
127 case RectGeometryFastPath: | 154 case RectGeometryFastPath: |
128 context->fillRect(m_renderSVGShape.objectBoundingBox()); | 155 context->fillRect(m_renderSVGShape.objectBoundingBox(), &paint); |
129 break; | 156 break; |
130 case EllipseGeometryFastPath: | 157 case EllipseGeometryFastPath: |
131 context->fillEllipse(m_renderSVGShape.objectBoundingBox()); | 158 context->fillEllipse(m_renderSVGShape.objectBoundingBox(), &paint); |
132 break; | 159 break; |
133 default: | 160 default: |
134 context->fillPath(m_renderSVGShape.path()); | 161 context->fillPath(m_renderSVGShape.path(), &paint); |
135 } | 162 } |
136 } | 163 } |
137 | 164 |
138 void SVGShapePainter::strokeShape(GraphicsContext* context) | 165 void SVGShapePainter::strokeShape(GraphicsContext* context, const SkPaint& paint ) |
139 { | 166 { |
140 if (!m_renderSVGShape.style()->svgStyle().hasVisibleStroke()) | 167 if (!m_renderSVGShape.style()->svgStyle().hasVisibleStroke()) |
141 return; | 168 return; |
142 | 169 |
143 switch (m_renderSVGShape.geometryCodePath()) { | 170 switch (m_renderSVGShape.geometryCodePath()) { |
144 case RectGeometryFastPath: | 171 case RectGeometryFastPath: |
145 context->strokeRect(m_renderSVGShape.objectBoundingBox(), m_renderSVGSha pe.strokeWidth()); | 172 context->strokeRect(m_renderSVGShape.objectBoundingBox(), paint); |
146 break; | 173 break; |
147 case EllipseGeometryFastPath: | 174 case EllipseGeometryFastPath: |
148 context->strokeEllipse(m_renderSVGShape.objectBoundingBox()); | 175 context->strokeEllipse(m_renderSVGShape.objectBoundingBox(), &paint); |
149 break; | 176 break; |
150 default: | 177 default: |
151 ASSERT(m_renderSVGShape.hasPath()); | 178 ASSERT(m_renderSVGShape.hasPath()); |
152 Path* usePath = &m_renderSVGShape.path(); | 179 Path* usePath = &m_renderSVGShape.path(); |
153 if (m_renderSVGShape.hasNonScalingStroke()) | 180 if (m_renderSVGShape.hasNonScalingStroke()) |
154 usePath = m_renderSVGShape.nonScalingStrokePath(usePath, m_renderSVG Shape.nonScalingStrokeTransform()); | 181 usePath = m_renderSVGShape.nonScalingStrokePath(usePath, m_renderSVG Shape.nonScalingStrokeTransform()); |
155 context->strokePath(*usePath); | 182 context->strokePath(*usePath, &paint); |
156 strokeZeroLengthLineCaps(context); | 183 strokeZeroLengthLineCaps(context, paint); |
157 } | 184 } |
158 } | 185 } |
159 | 186 |
160 void SVGShapePainter::paintMarkers(const PaintInfo& paintInfo) | 187 void SVGShapePainter::paintMarkers(const PaintInfo& paintInfo) |
161 { | 188 { |
162 const Vector<MarkerPosition>* markerPositions = m_renderSVGShape.markerPosit ions(); | 189 const Vector<MarkerPosition>* markerPositions = m_renderSVGShape.markerPosit ions(); |
163 if (!markerPositions || markerPositions->isEmpty()) | 190 if (!markerPositions || markerPositions->isEmpty()) |
164 return; | 191 return; |
165 | 192 |
166 SVGResources* resources = SVGResourcesCache::cachedResourcesForLayoutObject( &m_renderSVGShape); | 193 SVGResources* resources = SVGResourcesCache::cachedResourcesForLayoutObject( &m_renderSVGShape); |
(...skipping 29 matching lines...) Expand all Loading... | |
196 | 223 |
197 TransformRecorder transformRecorder(*markerPaintInfo.context, marker, ma rker.markerTransformation(position.origin, position.angle, strokeWidth)); | 224 TransformRecorder transformRecorder(*markerPaintInfo.context, marker, ma rker.markerTransformation(position.origin, position.angle, strokeWidth)); |
198 OwnPtr<FloatClipRecorder> clipRecorder; | 225 OwnPtr<FloatClipRecorder> clipRecorder; |
199 if (SVGLayoutSupport::isOverflowHidden(&marker)) | 226 if (SVGLayoutSupport::isOverflowHidden(&marker)) |
200 clipRecorder = adoptPtr(new FloatClipRecorder(*markerPaintInfo.conte xt, marker, markerPaintInfo.phase, marker.viewport())); | 227 clipRecorder = adoptPtr(new FloatClipRecorder(*markerPaintInfo.conte xt, marker, markerPaintInfo.phase, marker.viewport())); |
201 | 228 |
202 SVGContainerPainter(marker).paint(markerPaintInfo); | 229 SVGContainerPainter(marker).paint(markerPaintInfo); |
203 } | 230 } |
204 } | 231 } |
205 | 232 |
206 void SVGShapePainter::strokeZeroLengthLineCaps(GraphicsContext* context) | 233 void SVGShapePainter::strokeZeroLengthLineCaps(GraphicsContext* context, const S kPaint& strokePaint) |
207 { | 234 { |
208 const Vector<FloatPoint>* zeroLengthLineCaps = m_renderSVGShape.zeroLengthLi neCaps(); | 235 const Vector<FloatPoint>* zeroLengthLineCaps = m_renderSVGShape.zeroLengthLi neCaps(); |
209 if (!zeroLengthLineCaps || zeroLengthLineCaps->isEmpty()) | 236 if (!zeroLengthLineCaps || zeroLengthLineCaps->isEmpty()) |
210 return; | 237 return; |
211 | 238 |
212 Path* usePath; | 239 // We need a paint for filling. |
240 SkPaint fillPaint = strokePaint; | |
241 fillPaint.setStyle(SkPaint::kFill_Style); | |
242 | |
213 AffineTransform nonScalingTransform; | 243 AffineTransform nonScalingTransform; |
214 | |
215 if (m_renderSVGShape.hasNonScalingStroke()) | 244 if (m_renderSVGShape.hasNonScalingStroke()) |
216 nonScalingTransform = m_renderSVGShape.nonScalingStrokeTransform(); | 245 nonScalingTransform = m_renderSVGShape.nonScalingStrokeTransform(); |
217 | 246 |
218 GraphicsContextStateSaver stateSaver(*context, true); | 247 Path tempPath; |
219 useStrokeStyleToFill(context); | |
220 for (size_t i = 0; i < zeroLengthLineCaps->size(); ++i) { | 248 for (size_t i = 0; i < zeroLengthLineCaps->size(); ++i) { |
221 usePath = zeroLengthLinecapPath((*zeroLengthLineCaps)[i]); | 249 FloatRect subpathRect = LayoutSVGPath::zeroLengthSubpathRect((*zeroLengt hLineCaps)[i], m_renderSVGShape.strokeWidth()); |
250 tempPath.clear(); | |
251 if (m_renderSVGShape.style()->svgStyle().capStyle() == SquareCap) | |
252 tempPath.addRect(subpathRect); | |
253 else | |
254 tempPath.addEllipse(subpathRect); | |
255 // This open-codes LayoutSVGShape::nonScalingStrokePath, because the | |
256 // requirements here differ (we have a temporary path that we can | |
257 // mutate.) | |
222 if (m_renderSVGShape.hasNonScalingStroke()) | 258 if (m_renderSVGShape.hasNonScalingStroke()) |
223 usePath = m_renderSVGShape.nonScalingStrokePath(usePath, nonScalingT ransform); | 259 tempPath.transform(nonScalingTransform); |
224 context->fillPath(*usePath); | 260 context->fillPath(tempPath, &fillPaint); |
225 } | 261 } |
226 } | 262 } |
227 | 263 |
228 Path* SVGShapePainter::zeroLengthLinecapPath(const FloatPoint& linecapPosition) const | |
229 { | |
230 DEFINE_STATIC_LOCAL(Path, tempPath, ()); | |
231 | |
232 tempPath.clear(); | |
233 if (m_renderSVGShape.style()->svgStyle().capStyle() == SquareCap) | |
234 tempPath.addRect(LayoutSVGPath::zeroLengthSubpathRect(linecapPosition, m _renderSVGShape.strokeWidth())); | |
235 else | |
236 tempPath.addEllipse(LayoutSVGPath::zeroLengthSubpathRect(linecapPosition , m_renderSVGShape.strokeWidth())); | |
237 | |
238 return &tempPath; | |
239 } | |
240 | |
241 } // namespace blink | 264 } // namespace blink |
OLD | NEW |