Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / svg / RenderSVGResourceFilter.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
4  * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
5  * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
6  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 #include "config.h"
25 #include "core/rendering/svg/RenderSVGResourceFilter.h"
26
27 #include "core/dom/ElementTraversal.h"
28 #include "core/svg/SVGFilterPrimitiveStandardAttributes.h"
29 #include "platform/graphics/GraphicsContext.h"
30 #include "platform/graphics/filters/SkiaImageFilterBuilder.h"
31 #include "platform/graphics/filters/SourceAlpha.h"
32 #include "platform/graphics/filters/SourceGraphic.h"
33
34 namespace blink {
35
36 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
37     : RenderSVGResourceContainer(node)
38 {
39 }
40
41 RenderSVGResourceFilter::~RenderSVGResourceFilter()
42 {
43 }
44
45 void RenderSVGResourceFilter::destroy()
46 {
47     m_filter.clear();
48     RenderSVGResourceContainer::destroy();
49 }
50
51 bool RenderSVGResourceFilter::isChildAllowed(RenderObject* child, RenderStyle*) const
52 {
53     return child->isSVGResourceFilterPrimitive();
54 }
55
56 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
57 {
58     m_filter.clear();
59     markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
60 }
61
62 void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation)
63 {
64     ASSERT(client);
65
66     m_filter.remove(client);
67
68     markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
69 }
70
71 PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* filter)
72 {
73     SVGFilterElement* filterElement = toSVGFilterElement(element());
74     FloatRect targetBoundingBox = filter->targetBoundingBox();
75
76     // Add effects to the builder
77     RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(SourceGraphic::create(filter), SourceAlpha::create(filter));
78     for (SVGElement* element = Traversal<SVGElement>::firstChild(*filterElement); element; element = Traversal<SVGElement>::nextSibling(*element)) {
79         if (!element->isFilterEffect() || !element->renderer())
80             continue;
81
82         SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
83         RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter);
84         if (!effect) {
85             builder->clearEffects();
86             return nullptr;
87         }
88         builder->appendEffectToEffectReferences(effect, effectElement->renderer());
89         effectElement->setStandardAttributes(effect.get());
90         effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnits()->currentValue()->enumValue(), targetBoundingBox));
91         effect->setOperatingColorSpace(
92             effectElement->renderer()->style()->svgStyle().colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
93         builder->add(AtomicString(effectElement->result()->currentValue()->value()), effect);
94     }
95     return builder.release();
96 }
97
98 static void beginDeferredFilter(GraphicsContext* context, FilterData* filterData)
99 {
100     context->beginRecording(filterData->boundaries);
101     context->setShouldSmoothFonts(false);
102     // We pass the boundaries to SkPictureImageFilter so it knows the
103     // world-space position of the filter primitives. It gets them
104     // from the DisplayList, which also applies the inverse translate
105     // to the origin. So we apply the forward translate here to avoid
106     // it being applied twice.
107     // FIXME: we should fix SkPicture to handle this offset itself, or
108     // make the translate optional on SkPictureImageFilter.
109     // See https://code.google.com/p/skia/issues/detail?id=2801
110     context->translate(filterData->boundaries.x(), filterData->boundaries.y());
111 }
112
113 static void endDeferredFilter(GraphicsContext* context, FilterData* filterData)
114 {
115     // FIXME: maybe filterData should just hold onto SourceGraphic after creation?
116     SourceGraphic* sourceGraphic = static_cast<SourceGraphic*>(filterData->builder->getEffectById(SourceGraphic::effectName()));
117     ASSERT(sourceGraphic);
118     sourceGraphic->setDisplayList(context->endRecording());
119 }
120
121 static void drawDeferredFilter(GraphicsContext* context, FilterData* filterData, SVGFilterElement* filterElement)
122 {
123     SkiaImageFilterBuilder builder(context);
124     SourceGraphic* sourceGraphic = static_cast<SourceGraphic*>(filterData->builder->getEffectById(SourceGraphic::effectName()));
125     ASSERT(sourceGraphic);
126     builder.setSourceGraphic(sourceGraphic);
127     RefPtr<ImageFilter> imageFilter = builder.build(filterData->builder->lastEffect(), ColorSpaceDeviceRGB);
128     FloatRect boundaries = filterData->boundaries;
129     context->save();
130
131     FloatSize deviceSize = context->getCTM().mapSize(boundaries.size());
132     float scaledArea = deviceSize.width() * deviceSize.height();
133
134     // If area of scaled size is bigger than the upper limit, adjust the scale
135     // to fit. Note that this only really matters in the non-impl-side painting
136     // case, since the impl-side case never allocates a full-sized backing
137     // store, only tile-sized.
138     // FIXME: remove this once all platforms are using impl-side painting.
139     // crbug.com/169282.
140     if (scaledArea > FilterEffect::maxFilterArea()) {
141         float scale = sqrtf(FilterEffect::maxFilterArea() / scaledArea);
142         context->scale(scale, scale);
143     }
144     // Clip drawing of filtered image to the minimum required paint rect.
145     FilterEffect* lastEffect = filterData->builder->lastEffect();
146     context->clipRect(lastEffect->determineAbsolutePaintRect(lastEffect->maxEffectRect()));
147     if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
148         // Get boundaries in device coords.
149         // FIXME: See crbug.com/382491. Is the use of getCTM OK here, given it does not include device
150         // zoom or High DPI adjustments?
151         FloatSize size = context->getCTM().mapSize(boundaries.size());
152         // Compute the scale amount required so that the resulting offscreen is exactly filterResX by filterResY pixels.
153         float filterResScaleX = filterElement->filterResX()->currentValue()->value() / size.width();
154         float filterResScaleY = filterElement->filterResY()->currentValue()->value() / size.height();
155         // Scale the CTM so the primitive is drawn to filterRes.
156         context->scale(filterResScaleX, filterResScaleY);
157         // Create a resize filter with the inverse scale.
158         AffineTransform resizeMatrix;
159         resizeMatrix.scale(1 / filterResScaleX, 1 / filterResScaleY);
160         imageFilter = builder.buildTransform(resizeMatrix, imageFilter.get());
161     }
162     // If the CTM contains rotation or shearing, apply the filter to
163     // the unsheared/unrotated matrix, and do the shearing/rotation
164     // as a final pass.
165     AffineTransform ctm = context->getCTM();
166     if (ctm.b() || ctm.c()) {
167         AffineTransform scaleAndTranslate;
168         scaleAndTranslate.translate(ctm.e(), ctm.f());
169         scaleAndTranslate.scale(ctm.xScale(), ctm.yScale());
170         ASSERT(scaleAndTranslate.isInvertible());
171         AffineTransform shearAndRotate = scaleAndTranslate.inverse();
172         shearAndRotate.multiply(ctm);
173         context->setCTM(scaleAndTranslate);
174         imageFilter = builder.buildTransform(shearAndRotate, imageFilter.get());
175     }
176     context->beginLayer(1, CompositeSourceOver, &boundaries, ColorFilterNone, imageFilter.get());
177     context->endLayer();
178     context->restore();
179 }
180
181 bool RenderSVGResourceFilter::prepareEffect(RenderObject* object, GraphicsContext*& context)
182 {
183     ASSERT(object);
184     ASSERT(context);
185
186     clearInvalidationMask();
187
188     if (m_filter.contains(object)) {
189         FilterData* filterData = m_filter.get(object);
190         if (filterData->state == FilterData::PaintingSource)
191             filterData->state = FilterData::CycleDetected;
192         return false; // Already built, or we're in a cycle. Regardless, just do nothing more now.
193     }
194
195     OwnPtr<FilterData> filterData(adoptPtr(new FilterData));
196     FloatRect targetBoundingBox = object->objectBoundingBox();
197
198     SVGFilterElement* filterElement = toSVGFilterElement(element());
199     filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits()->currentValue()->enumValue(), targetBoundingBox);
200     if (filterData->boundaries.isEmpty())
201         return false;
202
203     filterData->drawingRegion = object->strokeBoundingBox();
204     filterData->drawingRegion.intersect(filterData->boundaries);
205     IntRect intDrawingRegion = enclosingIntRect(filterData->drawingRegion);
206
207     // Create the SVGFilter object.
208     bool primitiveBoundingBoxMode = filterElement->primitiveUnits()->currentValue()->enumValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
209     filterData->filter = SVGFilter::create(intDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
210
211     // Create all relevant filter primitives.
212     filterData->builder = buildPrimitives(filterData->filter.get());
213     if (!filterData->builder)
214         return false;
215
216     FilterEffect* lastEffect = filterData->builder->lastEffect();
217     if (!lastEffect)
218         return false;
219
220     lastEffect->determineFilterPrimitiveSubregion(ClipToFilterRegion);
221
222     FilterData* data = filterData.get();
223     m_filter.set(object, filterData.release());
224     beginDeferredFilter(context, data);
225     return true;
226 }
227
228 void RenderSVGResourceFilter::finishEffect(RenderObject* object, GraphicsContext*& context)
229 {
230     ASSERT(object);
231     ASSERT(context);
232
233     FilterData* filterData = m_filter.get(object);
234     if (!filterData)
235         return;
236
237     switch (filterData->state) {
238     case FilterData::CycleDetected:
239         // applyResource detected a cycle. This can occur due to FeImage
240         // referencing a source that makes use of the FEImage itself. This is
241         // the first place we've hit the cycle, so set the state back to
242         // PaintingSource so the return stack will continue correctly.
243         filterData->state = FilterData::PaintingSource;
244         return;
245
246     case FilterData::PaintingSource:
247         endDeferredFilter(context, filterData);
248         break;
249
250     case FilterData::Built: { } // Empty
251     }
252
253     drawDeferredFilter(context, filterData, toSVGFilterElement(element()));
254     filterData->state = FilterData::Built;
255 }
256
257 FloatRect RenderSVGResourceFilter::resourceBoundingBox(const RenderObject* object)
258 {
259     if (SVGFilterElement* element = toSVGFilterElement(this->element()))
260         return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnits()->currentValue()->enumValue(), object->objectBoundingBox());
261
262     return FloatRect();
263 }
264
265 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
266 {
267     FilterMap::iterator it = m_filter.begin();
268     FilterMap::iterator end = m_filter.end();
269     SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
270
271     for (; it != end; ++it) {
272         FilterData* filterData = it->value.get();
273         if (filterData->state != FilterData::Built)
274             continue;
275
276         SVGFilterBuilder* builder = filterData->builder.get();
277         FilterEffect* effect = builder->effectByRenderer(object);
278         if (!effect)
279             continue;
280         // Since all effects shares the same attribute value, all
281         // or none of them will be changed.
282         if (!primitve->setFilterEffectAttribute(effect, attribute))
283             return;
284         builder->clearResultsRecursive(effect);
285
286         // Issue paint invalidations for the image on the screen.
287         markClientForInvalidation(it->key, PaintInvalidation);
288     }
289     markAllClientLayersForInvalidation();
290 }
291
292 FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const
293 {
294     FilterData* filterData = m_filter.get(object);
295     return filterData ? filterData->drawingRegion : FloatRect();
296 }
297
298 }