1 /*
2 * Copyright (C) 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2013 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "config.h"
28
29 #include "core/rendering/FilterEffectRenderer.h"
30
31 #include "core/dom/Document.h"
32 #include "core/fetch/DocumentResource.h"
33 #include "core/fetch/DocumentResourceReference.h"
34 #include "core/page/Page.h"
35 #include "core/rendering/RenderLayer.h"
36 #include "core/rendering/RenderView.h"
37 #include "core/rendering/svg/ReferenceFilterBuilder.h"
38 #include "core/svg/SVGElement.h"
39 #include "core/svg/SVGFilterPrimitiveStandardAttributes.h"
40 #include "platform/FloatConversion.h"
41 #include "platform/LengthFunctions.h"
42 #include "platform/graphics/ColorSpace.h"
43 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
44 #include "platform/graphics/filters/FEColorMatrix.h"
45 #include "platform/graphics/filters/FEComponentTransfer.h"
46 #include "platform/graphics/filters/FEDropShadow.h"
47 #include "platform/graphics/filters/FEGaussianBlur.h"
48 #include "platform/graphics/filters/custom/CustomFilterGlobalContext.h"
49 #include "platform/graphics/filters/custom/CustomFilterValidatedProgram.h"
50 #include "platform/graphics/filters/custom/FECustomFilter.h"
51 #include "platform/graphics/filters/custom/ValidatedCustomFilterOperation.h"
52 #include "platform/graphics/gpu/AcceleratedImageBufferSurface.h"
53 #include "wtf/MathExtras.h"
54 #include <algorithm>
55
56 namespace WebCore {
57
endMatrixRow(Vector<float> & parameters)58 static inline void endMatrixRow(Vector<float>& parameters)
59 {
60 parameters.append(0);
61 parameters.append(0);
62 }
63
lastMatrixRow(Vector<float> & parameters)64 static inline void lastMatrixRow(Vector<float>& parameters)
65 {
66 parameters.append(0);
67 parameters.append(0);
68 parameters.append(0);
69 parameters.append(1);
70 parameters.append(0);
71 }
72
isFilterSizeValid(FloatRect rect)73 inline bool isFilterSizeValid(FloatRect rect)
74 {
75 if (rect.width() < 0 || rect.width() > kMaxFilterSize
76 || rect.height() < 0 || rect.height() > kMaxFilterSize)
77 return false;
78 return true;
79 }
80
createCustomFilterEffect(Filter * filter,Document * document,ValidatedCustomFilterOperation * operation)81 static PassRefPtr<FECustomFilter> createCustomFilterEffect(Filter* filter, Document* document, ValidatedCustomFilterOperation* operation)
82 {
83 if (!document)
84 return 0;
85
86 CustomFilterGlobalContext* globalContext = document->renderView()->customFilterGlobalContext();
87 globalContext->prepareContextIfNeeded();
88 if (!globalContext->context())
89 return 0;
90
91 return FECustomFilter::create(filter, globalContext->context(), operation->validatedProgram(), operation->parameters(),
92 operation->meshRows(), operation->meshColumns(), operation->meshType());
93 }
94
FilterEffectRenderer()95 FilterEffectRenderer::FilterEffectRenderer()
96 : Filter(AffineTransform())
97 , m_graphicsBufferAttached(false)
98 , m_hasFilterThatMovesPixels(false)
99 , m_hasCustomShaderFilter(false)
100 {
101 setFilterResolution(FloatSize(1, 1));
102 m_sourceGraphic = SourceGraphic::create(this);
103 }
104
~FilterEffectRenderer()105 FilterEffectRenderer::~FilterEffectRenderer()
106 {
107 }
108
inputContext()109 GraphicsContext* FilterEffectRenderer::inputContext()
110 {
111 return sourceImage() ? sourceImage()->context() : 0;
112 }
113
build(RenderObject * renderer,const FilterOperations & operations)114 bool FilterEffectRenderer::build(RenderObject* renderer, const FilterOperations& operations)
115 {
116 m_hasCustomShaderFilter = false;
117 m_hasFilterThatMovesPixels = operations.hasFilterThatMovesPixels();
118
119 // Inverse zoom the pre-zoomed CSS shorthand filters, so that they are in the same zoom as the unzoomed reference filters.
120 const RenderStyle* style = renderer->style();
121 // FIXME: The effects now contain high dpi information, but the software path doesn't (yet) scale its backing.
122 // When the proper dpi dependant backing size is allocated, we should remove deviceScaleFactor(...) here.
123 float invZoom = 1.0f / ((style ? style->effectiveZoom() : 1.0f) * deviceScaleFactor(renderer->frame()));
124
125 RefPtr<FilterEffect> previousEffect = m_sourceGraphic;
126 for (size_t i = 0; i < operations.operations().size(); ++i) {
127 RefPtr<FilterEffect> effect;
128 FilterOperation* filterOperation = operations.operations().at(i).get();
129 switch (filterOperation->type()) {
130 case FilterOperation::REFERENCE: {
131 effect = ReferenceFilterBuilder::build(this, renderer, previousEffect.get(), toReferenceFilterOperation(filterOperation));
132 break;
133 }
134 case FilterOperation::GRAYSCALE: {
135 Vector<float> inputParameters;
136 double oneMinusAmount = clampTo(1 - toBasicColorMatrixFilterOperation(filterOperation)->amount(), 0.0, 1.0);
137
138 // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#grayscaleEquivalent
139 // for information on parameters.
140
141 inputParameters.append(narrowPrecisionToFloat(0.2126 + 0.7874 * oneMinusAmount));
142 inputParameters.append(narrowPrecisionToFloat(0.7152 - 0.7152 * oneMinusAmount));
143 inputParameters.append(narrowPrecisionToFloat(0.0722 - 0.0722 * oneMinusAmount));
144 endMatrixRow(inputParameters);
145
146 inputParameters.append(narrowPrecisionToFloat(0.2126 - 0.2126 * oneMinusAmount));
147 inputParameters.append(narrowPrecisionToFloat(0.7152 + 0.2848 * oneMinusAmount));
148 inputParameters.append(narrowPrecisionToFloat(0.0722 - 0.0722 * oneMinusAmount));
149 endMatrixRow(inputParameters);
150
151 inputParameters.append(narrowPrecisionToFloat(0.2126 - 0.2126 * oneMinusAmount));
152 inputParameters.append(narrowPrecisionToFloat(0.7152 - 0.7152 * oneMinusAmount));
153 inputParameters.append(narrowPrecisionToFloat(0.0722 + 0.9278 * oneMinusAmount));
154 endMatrixRow(inputParameters);
155
156 lastMatrixRow(inputParameters);
157
158 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters);
159 break;
160 }
161 case FilterOperation::SEPIA: {
162 Vector<float> inputParameters;
163 double oneMinusAmount = clampTo(1 - toBasicColorMatrixFilterOperation(filterOperation)->amount(), 0.0, 1.0);
164
165 // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#sepiaEquivalent
166 // for information on parameters.
167
168 inputParameters.append(narrowPrecisionToFloat(0.393 + 0.607 * oneMinusAmount));
169 inputParameters.append(narrowPrecisionToFloat(0.769 - 0.769 * oneMinusAmount));
170 inputParameters.append(narrowPrecisionToFloat(0.189 - 0.189 * oneMinusAmount));
171 endMatrixRow(inputParameters);
172
173 inputParameters.append(narrowPrecisionToFloat(0.349 - 0.349 * oneMinusAmount));
174 inputParameters.append(narrowPrecisionToFloat(0.686 + 0.314 * oneMinusAmount));
175 inputParameters.append(narrowPrecisionToFloat(0.168 - 0.168 * oneMinusAmount));
176 endMatrixRow(inputParameters);
177
178 inputParameters.append(narrowPrecisionToFloat(0.272 - 0.272 * oneMinusAmount));
179 inputParameters.append(narrowPrecisionToFloat(0.534 - 0.534 * oneMinusAmount));
180 inputParameters.append(narrowPrecisionToFloat(0.131 + 0.869 * oneMinusAmount));
181 endMatrixRow(inputParameters);
182
183 lastMatrixRow(inputParameters);
184
185 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters);
186 break;
187 }
188 case FilterOperation::SATURATE: {
189 Vector<float> inputParameters;
190 inputParameters.append(narrowPrecisionToFloat(toBasicColorMatrixFilterOperation(filterOperation)->amount()));
191 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_SATURATE, inputParameters);
192 break;
193 }
194 case FilterOperation::HUE_ROTATE: {
195 Vector<float> inputParameters;
196 inputParameters.append(narrowPrecisionToFloat(toBasicColorMatrixFilterOperation(filterOperation)->amount()));
197 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_HUEROTATE, inputParameters);
198 break;
199 }
200 case FilterOperation::INVERT: {
201 BasicComponentTransferFilterOperation* componentTransferOperation = toBasicComponentTransferFilterOperation(filterOperation);
202 ComponentTransferFunction transferFunction;
203 transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE;
204 Vector<float> transferParameters;
205 transferParameters.append(narrowPrecisionToFloat(componentTransferOperation->amount()));
206 transferParameters.append(narrowPrecisionToFloat(1 - componentTransferOperation->amount()));
207 transferFunction.tableValues = transferParameters;
208
209 ComponentTransferFunction nullFunction;
210 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction);
211 break;
212 }
213 case FilterOperation::OPACITY: {
214 ComponentTransferFunction transferFunction;
215 transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE;
216 Vector<float> transferParameters;
217 transferParameters.append(0);
218 transferParameters.append(narrowPrecisionToFloat(toBasicComponentTransferFilterOperation(filterOperation)->amount()));
219 transferFunction.tableValues = transferParameters;
220
221 ComponentTransferFunction nullFunction;
222 effect = FEComponentTransfer::create(this, nullFunction, nullFunction, nullFunction, transferFunction);
223 break;
224 }
225 case FilterOperation::BRIGHTNESS: {
226 ComponentTransferFunction transferFunction;
227 transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR;
228 transferFunction.slope = narrowPrecisionToFloat(toBasicComponentTransferFilterOperation(filterOperation)->amount());
229 transferFunction.intercept = 0;
230
231 ComponentTransferFunction nullFunction;
232 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction);
233 break;
234 }
235 case FilterOperation::CONTRAST: {
236 ComponentTransferFunction transferFunction;
237 transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR;
238 float amount = narrowPrecisionToFloat(toBasicComponentTransferFilterOperation(filterOperation)->amount());
239 transferFunction.slope = amount;
240 transferFunction.intercept = -0.5 * amount + 0.5;
241
242 ComponentTransferFunction nullFunction;
243 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction);
244 break;
245 }
246 case FilterOperation::BLUR: {
247 float stdDeviation = floatValueForLength(toBlurFilterOperation(filterOperation)->stdDeviation(), 0) * invZoom;
248 effect = FEGaussianBlur::create(this, stdDeviation, stdDeviation);
249 break;
250 }
251 case FilterOperation::DROP_SHADOW: {
252 DropShadowFilterOperation* dropShadowOperation = toDropShadowFilterOperation(filterOperation);
253 float stdDeviation = dropShadowOperation->stdDeviation() * invZoom;
254 float x = dropShadowOperation->x() * invZoom;
255 float y = dropShadowOperation->y() * invZoom;
256 effect = FEDropShadow::create(this, stdDeviation, stdDeviation, x, y, dropShadowOperation->color(), 1);
257 break;
258 }
259 case FilterOperation::CUSTOM:
260 // CUSTOM operations are always converted to VALIDATED_CUSTOM before getting here.
261 // The conversion happens in RenderLayer::computeFilterOperations.
262 ASSERT_NOT_REACHED();
263 break;
264 case FilterOperation::VALIDATED_CUSTOM: {
265 Document* document = renderer ? &renderer->document() : 0;
266 effect = createCustomFilterEffect(this, document, toValidatedCustomFilterOperation(filterOperation));
267 if (effect)
268 m_hasCustomShaderFilter = true;
269 break;
270 }
271 default:
272 break;
273 }
274
275 if (effect) {
276 if (filterOperation->type() != FilterOperation::REFERENCE) {
277 // Unlike SVG, filters applied here should not clip to their primitive subregions.
278 effect->setClipsToBounds(false);
279 effect->setOperatingColorSpace(ColorSpaceDeviceRGB);
280 effect->inputEffects().append(previousEffect);
281 }
282 previousEffect = effect.release();
283 }
284 }
285
286 // We need to keep the old effects alive until this point, so that filters like FECustomFilter
287 // can share cached resources across frames.
288 m_lastEffect = previousEffect;
289
290 // If we didn't make any effects, tell our caller we are not valid
291 if (!m_lastEffect.get())
292 return false;
293
294 return true;
295 }
296
updateBackingStoreRect(const FloatRect & filterRect)297 bool FilterEffectRenderer::updateBackingStoreRect(const FloatRect& filterRect)
298 {
299 if (!filterRect.isZero() && isFilterSizeValid(filterRect)) {
300 FloatRect currentSourceRect = sourceImageRect();
301 if (filterRect != currentSourceRect) {
302 setSourceImageRect(filterRect);
303 return true;
304 }
305 }
306 return false;
307 }
308
allocateBackingStoreIfNeeded()309 void FilterEffectRenderer::allocateBackingStoreIfNeeded()
310 {
311 // At this point the effect chain has been built, and the
312 // source image sizes set. We just need to attach the graphic
313 // buffer if we have not yet done so.
314 if (!m_graphicsBufferAttached) {
315 IntSize logicalSize(m_sourceDrawingRegion.width(), m_sourceDrawingRegion.height());
316 if (!sourceImage() || sourceImage()->size() != logicalSize) {
317 OwnPtr<ImageBufferSurface> surface;
318 if (isAccelerated()) {
319 surface = adoptPtr(new AcceleratedImageBufferSurface(logicalSize));
320 }
321 if (!surface || !surface->isValid()) {
322 surface = adoptPtr(new UnacceleratedImageBufferSurface(logicalSize));
323 }
324 setSourceImage(ImageBuffer::create(surface.release()));
325 }
326 m_graphicsBufferAttached = true;
327 }
328 }
329
clearIntermediateResults()330 void FilterEffectRenderer::clearIntermediateResults()
331 {
332 if (m_lastEffect.get())
333 m_lastEffect->clearResultsRecursive();
334 }
335
apply()336 void FilterEffectRenderer::apply()
337 {
338 RefPtr<FilterEffect> effect = lastEffect();
339 effect->apply();
340 effect->transformResultColorSpace(ColorSpaceDeviceRGB);
341 }
342
computeSourceImageRectForDirtyRect(const LayoutRect & filterBoxRect,const LayoutRect & dirtyRect)343 LayoutRect FilterEffectRenderer::computeSourceImageRectForDirtyRect(const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect)
344 {
345 if (hasCustomShaderFilter()) {
346 // When we have at least a custom shader in the chain, we need to compute the whole source image, because the shader can
347 // reference any pixel and we cannot control that.
348 return filterBoxRect;
349 }
350 // The result of this function is the area in the "filterBoxRect" that needs to be repainted, so that we fully cover the "dirtyRect".
351 FloatRect rectForRepaint = dirtyRect;
352 rectForRepaint.move(-filterBoxRect.location().x(), -filterBoxRect.location().y());
353 float inf = std::numeric_limits<float>::infinity();
354 FloatRect clipRect = FloatRect(FloatPoint(-inf, -inf), FloatSize(inf, inf));
355 rectForRepaint = lastEffect()->getSourceRect(rectForRepaint, clipRect);
356 rectForRepaint.move(filterBoxRect.location().x(), filterBoxRect.location().y());
357 rectForRepaint.intersect(filterBoxRect);
358 return LayoutRect(rectForRepaint);
359 }
360
prepareFilterEffect(RenderLayer * renderLayer,const LayoutRect & filterBoxRect,const LayoutRect & dirtyRect,const LayoutRect & layerRepaintRect)361 bool FilterEffectRendererHelper::prepareFilterEffect(RenderLayer* renderLayer, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect)
362 {
363 ASSERT(m_haveFilterEffect && renderLayer->filterRenderer());
364 m_renderLayer = renderLayer;
365 m_repaintRect = dirtyRect;
366
367 FilterEffectRenderer* filter = renderLayer->filterRenderer();
368 LayoutRect filterSourceRect = filter->computeSourceImageRectForDirtyRect(filterBoxRect, dirtyRect);
369
370 if (filterSourceRect.isEmpty()) {
371 // The dirty rect is not in view, just bail out.
372 m_haveFilterEffect = false;
373 return false;
374 }
375
376 // Get the zoom factor to scale the filterSourceRect input
377 const RenderLayerModelObject* renderer = renderLayer->renderer();
378 const RenderStyle* style = renderer ? renderer->style() : 0;
379 float zoom = style ? style->effectiveZoom() : 1.0f;
380
381 AffineTransform absoluteTransform;
382 absoluteTransform.translate(filterBoxRect.x(), filterBoxRect.y());
383 filter->setAbsoluteTransform(absoluteTransform);
384 filter->setAbsoluteFilterRegion(AffineTransform().scale(zoom).mapRect(filterSourceRect));
385 filter->setFilterRegion(absoluteTransform.inverse().mapRect(filterSourceRect));
386 filter->lastEffect()->determineFilterPrimitiveSubregion(MapRectForward);
387
388 bool hasUpdatedBackingStore = filter->updateBackingStoreRect(filterSourceRect);
389 if (filter->hasFilterThatMovesPixels()) {
390 if (hasUpdatedBackingStore)
391 m_repaintRect = filterSourceRect;
392 else {
393 m_repaintRect.unite(layerRepaintRect);
394 m_repaintRect.intersect(filterSourceRect);
395 }
396 }
397 return true;
398 }
399
beginFilterEffect(GraphicsContext * oldContext)400 GraphicsContext* FilterEffectRendererHelper::beginFilterEffect(GraphicsContext* oldContext)
401 {
402 ASSERT(m_renderLayer);
403
404 FilterEffectRenderer* filter = m_renderLayer->filterRenderer();
405 filter->allocateBackingStoreIfNeeded();
406 // Paint into the context that represents the SourceGraphic of the filter.
407 GraphicsContext* sourceGraphicsContext = filter->inputContext();
408 if (!sourceGraphicsContext || !isFilterSizeValid(filter->absoluteFilterRegion())) {
409 // Disable the filters and continue.
410 m_haveFilterEffect = false;
411 return oldContext;
412 }
413
414 m_savedGraphicsContext = oldContext;
415
416 // Translate the context so that the contents of the layer is captuterd in the offscreen memory buffer.
417 sourceGraphicsContext->save();
418 // FIXME: can we just use sourceImageRect for everything, and get rid of
419 // m_repaintRect?
420 FloatPoint offset = filter->sourceImageRect().location();
421 sourceGraphicsContext->translate(-offset.x(), -offset.y());
422 sourceGraphicsContext->clearRect(m_repaintRect);
423 sourceGraphicsContext->clip(m_repaintRect);
424
425 return sourceGraphicsContext;
426 }
427
applyFilterEffect()428 GraphicsContext* FilterEffectRendererHelper::applyFilterEffect()
429 {
430 ASSERT(m_haveFilterEffect && m_renderLayer->filterRenderer());
431 FilterEffectRenderer* filter = m_renderLayer->filterRenderer();
432 filter->inputContext()->restore();
433
434 filter->apply();
435
436 // Get the filtered output and draw it in place.
437 m_savedGraphicsContext->drawImageBuffer(filter->output(), filter->outputRect(), CompositeSourceOver);
438
439 filter->clearIntermediateResults();
440
441 return m_savedGraphicsContext;
442 }
443
444 } // namespace WebCore
445
446