Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1018)

Side by Side Diff: Source/core/rendering/svg/RenderSVGResourcePattern.cpp

Issue 453653003: [SVG] DisplayList-based patterns. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: 9 tests marked for rebaseline Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> 2 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 3 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
4 * Copyright (C) 2014 Google Inc. All rights reserved.
4 * 5 *
5 * This library is free software; you can redistribute it and/or 6 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public 7 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either 8 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version. 9 * version 2 of the License, or (at your option) any later version.
9 * 10 *
10 * This library is distributed in the hope that it will be useful, 11 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details. 14 * Library General Public License for more details.
14 * 15 *
15 * You should have received a copy of the GNU Library General Public License 16 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to 17 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA. 19 * Boston, MA 02110-1301, USA.
19 */ 20 */
20 21
21 #include "config.h" 22 #include "config.h"
22 23
23 #include "core/rendering/svg/RenderSVGResourcePattern.h" 24 #include "core/rendering/svg/RenderSVGResourcePattern.h"
24 25
25 #include "core/dom/ElementTraversal.h" 26 #include "core/dom/ElementTraversal.h"
26 #include "core/rendering/svg/SVGRenderSupport.h" 27 #include "core/rendering/svg/SVGRenderSupport.h"
27 #include "core/rendering/svg/SVGRenderingContext.h" 28 #include "core/rendering/svg/SVGRenderingContext.h"
28 #include "core/svg/SVGFitToViewBox.h" 29 #include "core/svg/SVGFitToViewBox.h"
30 #include "core/svg/SVGPatternElement.h"
31 #include "core/svg/SVGUnitTypes.h"
32 #include "platform/geometry/FloatRect.h"
33 #include "platform/graphics/DisplayList.h"
29 #include "platform/graphics/GraphicsContext.h" 34 #include "platform/graphics/GraphicsContext.h"
35 #include "platform/graphics/ImageBuffer.h"
36 #include "platform/graphics/Pattern.h"
37 #include "platform/transforms/AffineTransform.h"
30 38
31 namespace blink { 39 namespace blink {
32 40
41 struct PatternData {
42 WTF_MAKE_FAST_ALLOCATED;
43 public:
44 RefPtr<Pattern> pattern;
45 AffineTransform transform;
46 };
47
33 const RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternRe sourceType; 48 const RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternRe sourceType;
34 49
35 RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node) 50 RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node)
36 : RenderSVGResourceContainer(node) 51 : RenderSVGResourceContainer(node)
37 , m_shouldCollectPatternAttributes(true) 52 , m_shouldCollectPatternAttributes(true)
38 { 53 {
39 } 54 }
40 55
41 void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidatio n) 56 void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidatio n)
42 { 57 {
43 m_patternMap.clear(); 58 m_patternMap.clear();
44 m_shouldCollectPatternAttributes = true; 59 m_shouldCollectPatternAttributes = true;
45 markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : Pa rentOnlyInvalidation); 60 markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : Pa rentOnlyInvalidation);
46 } 61 }
47 62
48 void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation) 63 void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation)
49 { 64 {
50 ASSERT(client); 65 ASSERT(client);
51 m_patternMap.remove(client); 66 m_patternMap.remove(client);
52 markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation); 67 markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
53 } 68 }
54 69
55 PatternData* RenderSVGResourcePattern::buildPattern(RenderObject* object, unsign ed short resourceMode) 70 PatternData* RenderSVGResourcePattern::patternForRenderer(GraphicsContext& conte xt,
71 RenderObject& renderer)
56 { 72 {
57 ASSERT(object); 73 OwnPtr<PatternData>& patternData = m_patternMap.add(&renderer, nullptr).stor edValue->value;
58 PatternData* currentData = m_patternMap.get(object); 74 if (!patternData)
59 if (currentData && currentData->pattern) 75 patternData = buildPatternData(context, renderer);
60 return currentData;
61 76
77 ASSERT(!m_shouldCollectPatternAttributes);
78 return patternData.get();
79 }
80
81 bool RenderSVGResourcePattern::computeTileMetrics(const RenderObject& renderer,
82 const SVGPatternElement& pattern, FloatRect& bounds, AffineTransform& transf orm) const
Stephen White 2014/08/08 14:00:48 I prefer pointers over non-const refs for outparam
f(malita) 2014/08/08 15:30:35 The only reason I prefer refs is to avoid assert-n
83 {
84 FloatRect clientBoundingBox = renderer.objectBoundingBox();
Erik Dahlström (inactive) 2014/08/08 08:01:02 I've been working a bit in this area too recently,
f(malita) 2014/08/08 15:30:36 Thanks.
85
86 bounds = SVGLengthContext::resolveRectangle(&pattern, m_attributes.patternUn its(),
87 clientBoundingBox, m_attributes.x(), m_attributes.y(), m_attributes.widt h(),
88 m_attributes.height());
89
90 if (bounds.width() <= 0 || bounds.height() <= 0)
Stephen White 2014/08/08 14:00:48 Do we have a layout test that exercises this case?
f(malita) 2014/08/08 15:30:36 Yes, apparently svg/W3C-SVG-1.1-SE/pservers-patter
91 return false;
92
93 // Apply viewBox/objectBoundingBox transformations.
94 AffineTransform viewBoxCTM = SVGFitToViewBox::viewBoxToViewTransform(m_attri butes.viewBox(),
95 m_attributes.preserveAspectRatio(), bounds.width(), bounds.height());
96
97 if (!viewBoxCTM.isIdentity()) {
98 transform = viewBoxCTM;
99 } else {
100 if (m_attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OB JECTBOUNDINGBOX)
101 transform.scale(clientBoundingBox.width(), clientBoundingBox.height( ));
Stephen White 2014/08/08 14:00:48 I'm guessing this assumes the passed-in transform
f(malita) 2014/08/08 15:30:36 This should be addressed by refactoring.
102 }
103
104 return true;
105 }
106
107 PassOwnPtr<PatternData> RenderSVGResourcePattern::buildPatternData(GraphicsConte xt& context,
108 RenderObject& renderer)
109 {
62 SVGPatternElement* patternElement = toSVGPatternElement(element()); 110 SVGPatternElement* patternElement = toSVGPatternElement(element());
63 if (!patternElement) 111 if (!patternElement)
64 return 0; 112 return nullptr;
65 113
66 if (m_shouldCollectPatternAttributes) { 114 if (m_shouldCollectPatternAttributes) {
67 patternElement->synchronizeAnimatedSVGAttribute(anyQName()); 115 patternElement->synchronizeAnimatedSVGAttribute(anyQName());
68 116
69 m_attributes = PatternAttributes(); 117 m_attributes = PatternAttributes();
70 patternElement->collectPatternAttributes(m_attributes); 118 patternElement->collectPatternAttributes(m_attributes);
71 m_shouldCollectPatternAttributes = false; 119 m_shouldCollectPatternAttributes = false;
72 } 120 }
73 121
74 // If we couldn't determine the pattern content element root, stop here. 122 // If we couldn't determine the pattern content element root, stop here.
75 if (!m_attributes.patternContentElement()) 123 if (!m_attributes.patternContentElement())
76 return 0; 124 return nullptr;
77 125
78 // An empty viewBox disables rendering. 126 // An empty viewBox disables rendering.
79 if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty()) 127 if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty())
80 return 0; 128 return nullptr;
81 129
82 // Compute all necessary transformations to build the tile image & the patte rn. 130 FloatRect tileBounds;
83 FloatRect tileBoundaries; 131 AffineTransform tileTransform;
84 AffineTransform tileImageTransform; 132 if (!computeTileMetrics(renderer, *patternElement, tileBounds, tileTransform ))
85 if (!buildTileImageTransform(object, m_attributes, patternElement, tileBound aries, tileImageTransform)) 133 return nullptr;
86 return 0;
87 134
88 AffineTransform absoluteTransformIgnoringRotation;
89 SVGRenderingContext::calculateDeviceSpaceTransformation(object, absoluteTran sformIgnoringRotation);
90
91 // Ignore 2D rotation, as it doesn't affect the size of the tile.
92 SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation);
93 FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect (tileBoundaries);
94
95 // Scale the tile size to match the scale level of the patternTransform.
96 absoluteTileBoundaries.scale(static_cast<float>(m_attributes.patternTransfor m().xScale()),
97 static_cast<float>(m_attributes.patternTransform().yScale()));
98
99 // Build tile image.
100 OwnPtr<ImageBuffer> tileImage = createTileImage(m_attributes, tileBoundaries , absoluteTileBoundaries, tileImageTransform);
101 if (!tileImage)
102 return 0;
103
104 RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore);
105 if (!copiedImage)
106 return 0;
107
108 // Build pattern.
109 OwnPtr<PatternData> patternData = adoptPtr(new PatternData); 135 OwnPtr<PatternData> patternData = adoptPtr(new PatternData);
110 patternData->pattern = Pattern::createBitmapPattern(copiedImage); 136 patternData->pattern = Pattern::createDisplayListPattern(asDisplayList(conte xt, tileBounds,
137 tileTransform));
111 138
112 // Compute pattern space transformation. 139 // Compute pattern space transformation.
113 const IntSize tileImageSize = tileImage->size(); 140 patternData->transform.translate(tileBounds.x(), tileBounds.y());
114 patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y());
115 patternData->transform.scale(tileBoundaries.width() / tileImageSize.width(), tileBoundaries.height() / tileImageSize.height());
116
117 AffineTransform patternTransform = m_attributes.patternTransform(); 141 AffineTransform patternTransform = m_attributes.patternTransform();
118 if (!patternTransform.isIdentity()) 142 if (!patternTransform.isIdentity())
119 patternData->transform = patternTransform * patternData->transform; 143 patternData->transform = patternTransform * patternData->transform;
120 144
121 // Various calls above may trigger invalidations in some fringe cases (Image Buffer allocation 145 return patternData.release();
122 // failures in the SVG image cache for example). To avoid having our Pattern Data deleted by
123 // removeAllClientsFromCache(), we only make it visible in the cache at the very end.
124 return m_patternMap.set(object, patternData.release()).storedValue->value.ge t();
125 } 146 }
126 147
127 bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode) 148 bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
128 { 149 {
129 ASSERT(object); 150 ASSERT(object);
130 ASSERT(style); 151 ASSERT(style);
131 ASSERT(context); 152 ASSERT(context);
132 ASSERT(resourceMode != ApplyToDefaultMode); 153 ASSERT(resourceMode != ApplyToDefaultMode);
133 154
134 clearInvalidationMask(); 155 clearInvalidationMask();
135 156
136 // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified, 157 // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
137 // then the given effect (e.g. a gradient or a filter) will be ignored. 158 // then the given effect (e.g. a gradient or a filter) will be ignored.
138 FloatRect objectBoundingBox = object->objectBoundingBox(); 159 FloatRect objectBoundingBox = object->objectBoundingBox();
139 if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDIN GBOX && objectBoundingBox.isEmpty()) 160 if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDIN GBOX && objectBoundingBox.isEmpty())
140 return false; 161 return false;
141 162
142 PatternData* patternData = buildPattern(object, resourceMode); 163 PatternData* patternData = patternForRenderer(*context, *object);
143 if (!patternData) 164 if (!patternData)
144 return false; 165 return false;
145 166
146 const SVGRenderStyle& svgStyle = style->svgStyle(); 167 const SVGRenderStyle& svgStyle = style->svgStyle();
147 168
148 AffineTransform computedPatternSpaceTransform = computeResourceSpaceTransfor m(object, patternData->transform, svgStyle, resourceMode); 169 AffineTransform computedPatternSpaceTransform = computeResourceSpaceTransfor m(object, patternData->transform, svgStyle, resourceMode);
149 patternData->pattern->setPatternSpaceTransform(computedPatternSpaceTransform ); 170 patternData->pattern->setPatternSpaceTransform(computedPatternSpaceTransform );
150 171
151 // Draw pattern 172 // Draw pattern
152 context->save(); 173 context->save();
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
185 if (resourceMode & ApplyToStrokeMode) { 206 if (resourceMode & ApplyToStrokeMode) {
186 if (path) 207 if (path)
187 context->strokePath(*path); 208 context->strokePath(*path);
188 else if (shape) 209 else if (shape)
189 shape->strokeShape(context); 210 shape->strokeShape(context);
190 } 211 }
191 212
192 context->restore(); 213 context->restore();
193 } 214 }
194 215
195 static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attr ibutes, 216 PassRefPtr<DisplayList> RenderSVGResourcePattern::asDisplayList(GraphicsContext& context,
196 const FloatRect& objectBoundi ngBox, 217 const FloatRect& tileBounds, const AffineTransform& tileTransform) const
197 const SVGPatternElement* patt ernElement)
198 { 218 {
199 ASSERT(patternElement); 219 ASSERT(!m_shouldCollectPatternAttributes);
200 return SVGLengthContext::resolveRectangle(patternElement, attributes.pattern Units(), objectBoundingBox, attributes.x(), attributes.y(), attributes.width(), attributes.height());
201 }
202 220
203 bool RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer, 221 AffineTransform contentTransform;
204 const PatternAttributes& attributes, 222 if (m_attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECT BOUNDINGBOX)
205 const SVGPatternElement* patternElement, 223 contentTransform = tileTransform;
206 FloatRect& patternBoundar ies,
207 AffineTransform& tileImag eTransform) const
208 {
209 ASSERT(renderer);
210 ASSERT(patternElement);
211 224
212 FloatRect objectBoundingBox = renderer->objectBoundingBox(); 225 FloatRect pictureBounds(FloatPoint(), tileBounds.size());
213 patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox , patternElement);
214 if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0)
215 return false;
216 226
217 AffineTransform viewBoxCTM = SVGFitToViewBox::viewBoxToViewTransform(attribu tes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patt ernBoundaries.height()); 227 // Draw the content into a DisplayList.
228 context.beginRecording(pictureBounds);
229 context.concatCTM(tileTransform);
Stephen White 2014/08/08 14:00:48 Just wondering: could the tile transform (e.g., vi
f(malita) 2014/08/08 15:30:36 I don't think so (or if it does, it should be OK t
218 230
219 // Apply viewBox/objectBoundingBox transformations. 231 ASSERT(m_attributes.patternContentElement());
220 if (!viewBoxCTM.isIdentity()) 232 RenderSVGResourceContainer* patternRenderer =
221 tileImageTransform = viewBoxCTM; 233 toRenderSVGResourceContainer(m_attributes.patternContentElement()->rende rer());
222 else if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJ ECTBOUNDINGBOX) 234 ASSERT(patternRenderer);
223 tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.he ight()); 235 ASSERT(!patternRenderer->needsLayout());
224 236
225 return true; 237 for (RenderObject* child = patternRenderer->firstChild(); child; child = chi ld->nextSibling()) {
226 } 238 ASSERT(!child->needsLayout());
227 239 SVGRenderingContext::renderSubtree(&context, child, contentTransform);
228 PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternA ttributes& attributes,
229 const FloatRec t& tileBoundaries,
230 const FloatRec t& absoluteTileBoundaries,
231 const AffineTr ansform& tileImageTransform) const
232 {
233 FloatRect clampedAbsoluteTileBoundaries = SVGRenderingContext::clampedAbsolu teTargetRect(absoluteTileBoundaries);
234
235 IntSize imageSize(roundedIntSize(clampedAbsoluteTileBoundaries.size()));
236 if (imageSize.isEmpty())
237 return nullptr;
238 OwnPtr<ImageBuffer> tileImage = ImageBuffer::create(imageSize);
239 if (!tileImage)
240 return nullptr;
241
242 GraphicsContext* tileImageContext = tileImage->context();
243 ASSERT(tileImageContext);
244 IntSize unclampedImageSize(roundedIntSize(absoluteTileBoundaries.size()));
245 tileImageContext->scale(unclampedImageSize.width() / absoluteTileBoundaries. width(), unclampedImageSize.height() / absoluteTileBoundaries.height());
246
247 // The image buffer represents the final rendered size, so the content has t o be scaled (to avoid pixelation).
248 tileImageContext->scale(
249 clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(),
250 clampedAbsoluteTileBoundaries.height() / tileBoundaries.height());
251
252 // Apply tile image transformations.
253 if (!tileImageTransform.isIdentity())
254 tileImageContext->concatCTM(tileImageTransform);
255
256 AffineTransform contentTransformation;
257 if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBO UNDINGBOX)
258 contentTransformation = tileImageTransform;
259
260 // Draw the content into the ImageBuffer.
261 for (SVGElement* element = Traversal<SVGElement>::firstChild(*attributes.pat ternContentElement()); element; element = Traversal<SVGElement>::nextSibling(*el ement)) {
262 if (!element->renderer())
263 continue;
264 if (element->renderer()->needsLayout())
265 return nullptr;
266 SVGRenderingContext::renderSubtree(tileImage->context(), element->render er(), contentTransformation);
267 } 240 }
268 241
269 return tileImage.release(); 242 return context.endRecording();
270 } 243 }
271 244
272 } 245 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698