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

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: review comments Created 6 years, 2 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 2014 The Chromium Authors. 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 #include "core/rendering/svg/RenderSVGResourcePattern.h" 23 #include "core/rendering/svg/RenderSVGResourcePattern.h"
23 24
24 #include "core/dom/ElementTraversal.h" 25 #include "core/dom/ElementTraversal.h"
25 #include "core/rendering/svg/SVGRenderingContext.h" 26 #include "core/rendering/svg/SVGRenderingContext.h"
26 #include "core/svg/SVGFitToViewBox.h" 27 #include "core/svg/SVGFitToViewBox.h"
28 #include "core/svg/SVGPatternElement.h"
29 #include "platform/graphics/DisplayList.h"
27 #include "platform/graphics/GraphicsContext.h" 30 #include "platform/graphics/GraphicsContext.h"
28 31
29 namespace blink { 32 namespace blink {
30 33
34 struct PatternData {
35 WTF_MAKE_FAST_ALLOCATED;
36 public:
37 RefPtr<Pattern> pattern;
38 AffineTransform transform;
39 };
40
31 const RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternRe sourceType; 41 const RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternRe sourceType;
32 42
33 RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node) 43 RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node)
34 : RenderSVGResourceContainer(node) 44 : RenderSVGResourceContainer(node)
35 , m_shouldCollectPatternAttributes(true) 45 , m_shouldCollectPatternAttributes(true)
36 { 46 {
37 } 47 }
38 48
39 void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidatio n) 49 void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidatio n)
40 { 50 {
41 m_patternMap.clear(); 51 m_patternMap.clear();
42 m_shouldCollectPatternAttributes = true; 52 m_shouldCollectPatternAttributes = true;
43 markAllClientsForInvalidation(markForInvalidation ? PaintInvalidation : Pare ntOnlyInvalidation); 53 markAllClientsForInvalidation(markForInvalidation ? PaintInvalidation : Pare ntOnlyInvalidation);
44 } 54 }
45 55
46 void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation) 56 void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation)
47 { 57 {
48 ASSERT(client); 58 ASSERT(client);
49 m_patternMap.remove(client); 59 m_patternMap.remove(client);
50 markClientForInvalidation(client, markForInvalidation ? PaintInvalidation : ParentOnlyInvalidation); 60 markClientForInvalidation(client, markForInvalidation ? PaintInvalidation : ParentOnlyInvalidation);
51 } 61 }
52 62
53 PatternData* RenderSVGResourcePattern::buildPattern(const RenderObject& object, const SVGPatternElement* patternElement) 63 PatternData* RenderSVGResourcePattern::patternForRenderer(const RenderObject& ob ject)
54 { 64 {
55 PatternData* currentData = m_patternMap.get(&object); 65 OwnPtr<PatternData>& patternData = m_patternMap.add(&object, nullptr).stored Value->value;
Stephen White 2014/10/22 15:11:26 Nit: This seems a bit of a convoluted way to use a
f(malita) 2014/10/22 16:23:47 I agree it's convoluted, but it avoids a double ha
Stephen White 2014/10/22 17:50:48 Acknowledged.
56 if (currentData && currentData->pattern) 66 if (!patternData)
57 return currentData; 67 patternData = buildPatternData(object);
58 68
69 ASSERT(!m_shouldCollectPatternAttributes);
70 return patternData.get();
71 }
72
73 PassOwnPtr<PatternData> RenderSVGResourcePattern::buildPatternData(const RenderO bject& object)
74 {
59 // If we couldn't determine the pattern content element root, stop here. 75 // If we couldn't determine the pattern content element root, stop here.
60 if (!m_attributes.patternContentElement()) 76 if (!m_attributes.patternContentElement())
61 return 0; 77 return nullptr;
62 78
63 // An empty viewBox disables rendering. 79 // An empty viewBox disables rendering.
64 if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty()) 80 if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty())
65 return 0; 81 return nullptr;
66 82
67 // Compute all necessary transformations to build the tile image & the patte rn. 83 ASSERT(element());
68 FloatRect tileBoundaries; 84 // Compute tile metrics.
69 AffineTransform tileImageTransform; 85 FloatRect clientBoundingBox = object.objectBoundingBox();
70 if (!buildTileImageTransform(object, m_attributes, patternElement, tileBound aries, tileImageTransform)) 86 FloatRect tileBounds = SVGLengthContext::resolveRectangle(element(),
71 return 0; 87 m_attributes.patternUnits(), clientBoundingBox,
88 m_attributes.x(), m_attributes.y(), m_attributes.width(), m_attributes.h eight());
89 if (tileBounds.isEmpty())
90 return nullptr;
72 91
73 AffineTransform absoluteTransformIgnoringRotation; 92 AffineTransform tileTransform;
74 SVGRenderingContext::calculateDeviceSpaceTransformation(&object, absoluteTra nsformIgnoringRotation); 93 if (m_attributes.hasViewBox()) {
94 if (m_attributes.viewBox().isEmpty())
95 return nullptr;
96 tileTransform = SVGFitToViewBox::viewBoxToViewTransform(m_attributes.vie wBox(),
97 m_attributes.preserveAspectRatio(), tileBounds.width(), tileBounds.h eight());
98 } else {
99 // A viewbox overrides patternContentUnits, per spec.
100 if (m_attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OB JECTBOUNDINGBOX)
101 tileTransform.scale(clientBoundingBox.width(), clientBoundingBox.hei ght());
102 }
75 103
76 // Ignore 2D rotation, as it doesn't affect the size of the tile.
77 SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation);
78 FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect (tileBoundaries);
79
80 // Scale the tile size to match the scale level of the patternTransform.
81 absoluteTileBoundaries.scale(static_cast<float>(m_attributes.patternTransfor m().xScale()),
82 static_cast<float>(m_attributes.patternTransform().yScale()));
83
84 // Build tile image.
85 OwnPtr<ImageBuffer> tileImage = createTileImage(m_attributes, tileBoundaries , absoluteTileBoundaries, tileImageTransform);
86 if (!tileImage)
87 return 0;
88
89 RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore);
90 if (!copiedImage)
91 return 0;
92
93 // Build pattern.
94 OwnPtr<PatternData> patternData = adoptPtr(new PatternData); 104 OwnPtr<PatternData> patternData = adoptPtr(new PatternData);
95 patternData->pattern = Pattern::createBitmapPattern(copiedImage); 105 patternData->pattern = Pattern::createDisplayListPattern(asDisplayList(tileB ounds, tileTransform));
96 106
97 // Compute pattern space transformation. 107 // Compute pattern space transformation.
98 const IntSize tileImageSize = tileImage->size(); 108 patternData->transform.translate(tileBounds.x(), tileBounds.y());
99 patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y());
100 patternData->transform.scale(tileBoundaries.width() / tileImageSize.width(), tileBoundaries.height() / tileImageSize.height());
101
102 AffineTransform patternTransform = m_attributes.patternTransform(); 109 AffineTransform patternTransform = m_attributes.patternTransform();
103 if (!patternTransform.isIdentity()) 110 if (!patternTransform.isIdentity())
104 patternData->transform = patternTransform * patternData->transform; 111 patternData->transform = patternTransform * patternData->transform;
105 112
106 // Various calls above may trigger invalidations in some fringe cases (Image Buffer allocation 113 return patternData.release();
107 // failures in the SVG image cache for example). To avoid having our Pattern Data deleted by
108 // removeAllClientsFromCache(), we only make it visible in the cache at the very end.
109 return m_patternMap.set(&object, patternData.release()).storedValue->value.g et();
110 } 114 }
111 115
112 SVGPaintServer RenderSVGResourcePattern::preparePaintServer(const RenderObject& object) 116 SVGPaintServer RenderSVGResourcePattern::preparePaintServer(const RenderObject& object)
113 { 117 {
114 clearInvalidationMask(); 118 clearInvalidationMask();
115 119
116 SVGPatternElement* patternElement = toSVGPatternElement(element()); 120 SVGPatternElement* patternElement = toSVGPatternElement(element());
117 if (!patternElement) 121 if (!patternElement)
118 return SVGPaintServer::invalid(); 122 return SVGPaintServer::invalid();
119 123
120 if (m_shouldCollectPatternAttributes) { 124 if (m_shouldCollectPatternAttributes) {
121 patternElement->synchronizeAnimatedSVGAttribute(anyQName()); 125 patternElement->synchronizeAnimatedSVGAttribute(anyQName());
122 126
123 m_attributes = PatternAttributes(); 127 m_attributes = PatternAttributes();
124 patternElement->collectPatternAttributes(m_attributes); 128 patternElement->collectPatternAttributes(m_attributes);
125 m_shouldCollectPatternAttributes = false; 129 m_shouldCollectPatternAttributes = false;
126 } 130 }
127 131
128 // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified, 132 // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
129 // then the given effect (e.g. a gradient or a filter) will be ignored. 133 // then the given effect (e.g. a gradient or a filter) will be ignored.
130 FloatRect objectBoundingBox = object.objectBoundingBox(); 134 FloatRect objectBoundingBox = object.objectBoundingBox();
131 if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDIN GBOX && objectBoundingBox.isEmpty()) 135 if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDIN GBOX && objectBoundingBox.isEmpty())
132 return SVGPaintServer::invalid(); 136 return SVGPaintServer::invalid();
133 137
134 PatternData* patternData = buildPattern(object, patternElement); 138 PatternData* patternData = patternForRenderer(object);
135 if (!patternData) 139 if (!patternData)
136 return SVGPaintServer::invalid(); 140 return SVGPaintServer::invalid();
137 141
138 patternData->pattern->setPatternSpaceTransform(patternData->transform); 142 patternData->pattern->setPatternSpaceTransform(patternData->transform);
139 143
140 return SVGPaintServer(patternData->pattern); 144 return SVGPaintServer(patternData->pattern);
141 } 145 }
142 146
143 static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attr ibutes, 147 PassRefPtr<DisplayList> RenderSVGResourcePattern::asDisplayList(const FloatRect& tileBounds,
144 const FloatRect& objectBoundi ngBox, 148 const AffineTransform& tileTransform) const
145 const SVGPatternElement* patt ernElement)
146 { 149 {
147 ASSERT(patternElement); 150 ASSERT(!m_shouldCollectPatternAttributes);
148 return SVGLengthContext::resolveRectangle(patternElement, attributes.pattern Units(), objectBoundingBox, attributes.x(), attributes.y(), attributes.width(), attributes.height());
149 }
150 151
151 bool RenderSVGResourcePattern::buildTileImageTransform(const RenderObject& rende rer, 152 AffineTransform contentTransform;
152 const PatternAttributes& attributes, 153 if (m_attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECT BOUNDINGBOX)
153 const SVGPatternElement* patternElement, 154 contentTransform = tileTransform;
154 FloatRect& patternBoundar ies,
155 AffineTransform& tileImag eTransform) const
156 {
157 ASSERT(patternElement);
158 155
159 FloatRect objectBoundingBox = renderer.objectBoundingBox(); 156 // Draw the content into a DisplayList.
160 patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox , patternElement); 157 GraphicsContext recordingContext(nullptr);
161 if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0) 158 recordingContext.beginRecording(FloatRect(FloatPoint(), tileBounds.size()));
162 return false; 159 recordingContext.concatCTM(tileTransform);
163 160
164 AffineTransform viewBoxCTM = SVGFitToViewBox::viewBoxToViewTransform(attribu tes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patt ernBoundaries.height()); 161 ASSERT(m_attributes.patternContentElement());
162 RenderSVGResourceContainer* patternRenderer =
163 toRenderSVGResourceContainer(m_attributes.patternContentElement()->rende rer());
164 ASSERT(patternRenderer);
165 ASSERT(!patternRenderer->needsLayout());
165 166
166 // Apply viewBox/objectBoundingBox transformations. 167 for (RenderObject* child = patternRenderer->firstChild(); child; child = chi ld->nextSibling())
167 if (!viewBoxCTM.isIdentity()) 168 SVGRenderingContext::renderSubtree(&recordingContext, child, contentTran sform);
168 tileImageTransform = viewBoxCTM;
169 else if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJ ECTBOUNDINGBOX)
170 tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.he ight());
171 169
172 return true; 170 return recordingContext.endRecording();
173 }
174
175 PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternA ttributes& attributes,
176 const FloatRec t& tileBoundaries,
177 const FloatRec t& absoluteTileBoundaries,
178 const AffineTr ansform& tileImageTransform) const
179 {
180 FloatRect clampedAbsoluteTileBoundaries = SVGRenderingContext::clampedAbsolu teTargetRect(absoluteTileBoundaries);
181
182 IntSize imageSize(roundedIntSize(clampedAbsoluteTileBoundaries.size()));
183 if (imageSize.isEmpty())
184 return nullptr;
185 OwnPtr<ImageBuffer> tileImage = ImageBuffer::create(imageSize);
186 if (!tileImage)
187 return nullptr;
188
189 GraphicsContext* tileImageContext = tileImage->context();
190 ASSERT(tileImageContext);
191 IntSize unclampedImageSize(roundedIntSize(absoluteTileBoundaries.size()));
192 tileImageContext->scale(unclampedImageSize.width() / absoluteTileBoundaries. width(), unclampedImageSize.height() / absoluteTileBoundaries.height());
193
194 // The image buffer represents the final rendered size, so the content has t o be scaled (to avoid pixelation).
195 tileImageContext->scale(
196 clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(),
197 clampedAbsoluteTileBoundaries.height() / tileBoundaries.height());
198
199 // Apply tile image transformations.
200 if (!tileImageTransform.isIdentity())
201 tileImageContext->concatCTM(tileImageTransform);
202
203 AffineTransform contentTransformation;
204 if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBO UNDINGBOX)
205 contentTransformation = tileImageTransform;
206
207 // Draw the content into the ImageBuffer.
208 for (SVGElement* element = Traversal<SVGElement>::firstChild(*attributes.pat ternContentElement()); element; element = Traversal<SVGElement>::nextSibling(*el ement)) {
209 if (!element->renderer())
210 continue;
211 if (element->renderer()->needsLayout())
212 return nullptr;
213 SVGRenderingContext::renderSubtree(tileImage->context(), element->render er(), contentTransformation);
214 }
215
216 return tileImage.release();
217 } 171 }
218 172
219 } 173 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698