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 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#if ENABLE(CSS_FILTERS)
30
31#include "FilterEffectRenderer.h"
32
33#include "CachedSVGDocument.h"
34#include "CachedSVGDocumentReference.h"
35#include "ColorSpace.h"
36#include "Document.h"
37#include "ElementIterator.h"
38#include "FEColorMatrix.h"
39#include "FEComponentTransfer.h"
40#include "FEDropShadow.h"
41#include "FEGaussianBlur.h"
42#include "FEMerge.h"
43#include "FloatConversion.h"
44#include "Frame.h"
45#include "RenderLayer.h"
46#include "SVGElement.h"
47#include "SVGFilterPrimitiveStandardAttributes.h"
48#include "Settings.h"
49#include "SourceAlpha.h"
50
51#include <algorithm>
52#include <wtf/MathExtras.h>
53
54namespace WebCore {
55
56static inline void endMatrixRow(Vector<float>& parameters)
57{
58    parameters.append(0);
59    parameters.append(0);
60}
61
62static inline void lastMatrixRow(Vector<float>& parameters)
63{
64    parameters.append(0);
65    parameters.append(0);
66    parameters.append(0);
67    parameters.append(1);
68    parameters.append(0);
69}
70
71FilterEffectRenderer::FilterEffectRenderer()
72    : Filter(AffineTransform())
73    , m_graphicsBufferAttached(false)
74    , m_hasFilterThatMovesPixels(false)
75{
76    setFilterResolution(FloatSize(1, 1));
77    m_sourceGraphic = SourceGraphic::create(this);
78}
79
80FilterEffectRenderer::~FilterEffectRenderer()
81{
82}
83
84GraphicsContext* FilterEffectRenderer::inputContext()
85{
86    return sourceImage() ? sourceImage()->context() : 0;
87}
88
89PassRefPtr<FilterEffect> FilterEffectRenderer::buildReferenceFilter(RenderElement* renderer, PassRefPtr<FilterEffect> previousEffect, ReferenceFilterOperation* filterOperation)
90{
91    if (!renderer)
92        return 0;
93
94    Document* document = &renderer->document();
95
96    CachedSVGDocumentReference* cachedSVGDocumentReference = filterOperation->cachedSVGDocumentReference();
97    CachedSVGDocument* cachedSVGDocument = cachedSVGDocumentReference ? cachedSVGDocumentReference->document() : 0;
98
99    // If we have an SVG document, this is an external reference. Otherwise
100    // we look up the referenced node in the current document.
101    if (cachedSVGDocument)
102        document = cachedSVGDocument->document();
103
104    if (!document)
105        return 0;
106
107    Element* filter = document->getElementById(filterOperation->fragment());
108    if (!filter) {
109        // Although we did not find the referenced filter, it might exist later in the document.
110        // FIXME: This skips anonymous RenderObjects. <https://webkit.org/b/131085>
111        if (Element* element = renderer->element())
112            document->accessSVGExtensions()->addPendingResource(filterOperation->fragment(), element);
113        return 0;
114    }
115
116    RefPtr<FilterEffect> effect;
117
118    // FIXME: Figure out what to do with SourceAlpha. Right now, we're
119    // using the alpha of the original input layer, which is obviously
120    // wrong. We should probably be extracting the alpha from the
121    // previousEffect, but this requires some more processing.
122    // This may need a spec clarification.
123    auto builder = std::make_unique<SVGFilterBuilder>(previousEffect, SourceAlpha::create(this));
124
125    for (auto& effectElement : childrenOfType<SVGFilterPrimitiveStandardAttributes>(*filter)) {
126        effect = effectElement.build(builder.get(), this);
127        if (!effect)
128            continue;
129
130        effectElement.setStandardAttributes(effect.get());
131        builder->add(effectElement.result(), effect);
132        m_effects.append(effect);
133    }
134    return effect;
135}
136
137bool FilterEffectRenderer::build(RenderElement* renderer, const FilterOperations& operations, FilterConsumer consumer)
138{
139    m_hasFilterThatMovesPixels = operations.hasFilterThatMovesPixels();
140    if (m_hasFilterThatMovesPixels)
141        m_outsets = operations.outsets();
142
143    m_effects.clear();
144
145    RefPtr<FilterEffect> previousEffect = m_sourceGraphic;
146    for (size_t i = 0; i < operations.operations().size(); ++i) {
147        RefPtr<FilterEffect> effect;
148        FilterOperation* filterOperation = operations.operations().at(i).get();
149        switch (filterOperation->type()) {
150        case FilterOperation::REFERENCE: {
151            ReferenceFilterOperation* referenceOperation = toReferenceFilterOperation(filterOperation);
152            effect = buildReferenceFilter(renderer, previousEffect, referenceOperation);
153            referenceOperation->setFilterEffect(effect);
154            break;
155        }
156        case FilterOperation::GRAYSCALE: {
157            BasicColorMatrixFilterOperation* colorMatrixOperation = toBasicColorMatrixFilterOperation(filterOperation);
158            Vector<float> inputParameters;
159            double oneMinusAmount = clampTo(1 - colorMatrixOperation->amount(), 0.0, 1.0);
160
161            // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#grayscaleEquivalent
162            // for information on parameters.
163
164            inputParameters.append(narrowPrecisionToFloat(0.2126 + 0.7874 * oneMinusAmount));
165            inputParameters.append(narrowPrecisionToFloat(0.7152 - 0.7152 * oneMinusAmount));
166            inputParameters.append(narrowPrecisionToFloat(0.0722 - 0.0722 * oneMinusAmount));
167            endMatrixRow(inputParameters);
168
169            inputParameters.append(narrowPrecisionToFloat(0.2126 - 0.2126 * oneMinusAmount));
170            inputParameters.append(narrowPrecisionToFloat(0.7152 + 0.2848 * oneMinusAmount));
171            inputParameters.append(narrowPrecisionToFloat(0.0722 - 0.0722 * oneMinusAmount));
172            endMatrixRow(inputParameters);
173
174            inputParameters.append(narrowPrecisionToFloat(0.2126 - 0.2126 * oneMinusAmount));
175            inputParameters.append(narrowPrecisionToFloat(0.7152 - 0.7152 * oneMinusAmount));
176            inputParameters.append(narrowPrecisionToFloat(0.0722 + 0.9278 * oneMinusAmount));
177            endMatrixRow(inputParameters);
178
179            lastMatrixRow(inputParameters);
180
181            effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters);
182            break;
183        }
184        case FilterOperation::SEPIA: {
185            BasicColorMatrixFilterOperation* colorMatrixOperation = toBasicColorMatrixFilterOperation(filterOperation);
186            Vector<float> inputParameters;
187            double oneMinusAmount = clampTo(1 - colorMatrixOperation->amount(), 0.0, 1.0);
188
189            // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#sepiaEquivalent
190            // for information on parameters.
191
192            inputParameters.append(narrowPrecisionToFloat(0.393 + 0.607 * oneMinusAmount));
193            inputParameters.append(narrowPrecisionToFloat(0.769 - 0.769 * oneMinusAmount));
194            inputParameters.append(narrowPrecisionToFloat(0.189 - 0.189 * oneMinusAmount));
195            endMatrixRow(inputParameters);
196
197            inputParameters.append(narrowPrecisionToFloat(0.349 - 0.349 * oneMinusAmount));
198            inputParameters.append(narrowPrecisionToFloat(0.686 + 0.314 * oneMinusAmount));
199            inputParameters.append(narrowPrecisionToFloat(0.168 - 0.168 * oneMinusAmount));
200            endMatrixRow(inputParameters);
201
202            inputParameters.append(narrowPrecisionToFloat(0.272 - 0.272 * oneMinusAmount));
203            inputParameters.append(narrowPrecisionToFloat(0.534 - 0.534 * oneMinusAmount));
204            inputParameters.append(narrowPrecisionToFloat(0.131 + 0.869 * oneMinusAmount));
205            endMatrixRow(inputParameters);
206
207            lastMatrixRow(inputParameters);
208
209            effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters);
210            break;
211        }
212        case FilterOperation::SATURATE: {
213            BasicColorMatrixFilterOperation* colorMatrixOperation = toBasicColorMatrixFilterOperation(filterOperation);
214            Vector<float> inputParameters;
215            inputParameters.append(narrowPrecisionToFloat(colorMatrixOperation->amount()));
216            effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_SATURATE, inputParameters);
217            break;
218        }
219        case FilterOperation::HUE_ROTATE: {
220            BasicColorMatrixFilterOperation* colorMatrixOperation = toBasicColorMatrixFilterOperation(filterOperation);
221            Vector<float> inputParameters;
222            inputParameters.append(narrowPrecisionToFloat(colorMatrixOperation->amount()));
223            effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_HUEROTATE, inputParameters);
224            break;
225        }
226        case FilterOperation::INVERT: {
227            BasicComponentTransferFilterOperation* componentTransferOperation = toBasicComponentTransferFilterOperation(filterOperation);
228            ComponentTransferFunction transferFunction;
229            transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE;
230            Vector<float> transferParameters;
231            transferParameters.append(narrowPrecisionToFloat(componentTransferOperation->amount()));
232            transferParameters.append(narrowPrecisionToFloat(1 - componentTransferOperation->amount()));
233            transferFunction.tableValues = transferParameters;
234
235            ComponentTransferFunction nullFunction;
236            effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction);
237            break;
238        }
239        case FilterOperation::OPACITY: {
240            BasicComponentTransferFilterOperation* componentTransferOperation = toBasicComponentTransferFilterOperation(filterOperation);
241            ComponentTransferFunction transferFunction;
242            transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE;
243            Vector<float> transferParameters;
244            transferParameters.append(0);
245            transferParameters.append(narrowPrecisionToFloat(componentTransferOperation->amount()));
246            transferFunction.tableValues = transferParameters;
247
248            ComponentTransferFunction nullFunction;
249            effect = FEComponentTransfer::create(this, nullFunction, nullFunction, nullFunction, transferFunction);
250            break;
251        }
252        case FilterOperation::BRIGHTNESS: {
253            BasicComponentTransferFilterOperation* componentTransferOperation = toBasicComponentTransferFilterOperation(filterOperation);
254            ComponentTransferFunction transferFunction;
255            transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR;
256            transferFunction.slope = narrowPrecisionToFloat(componentTransferOperation->amount());
257            transferFunction.intercept = 0;
258
259            ComponentTransferFunction nullFunction;
260            effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction);
261            break;
262        }
263        case FilterOperation::CONTRAST: {
264            BasicComponentTransferFilterOperation* componentTransferOperation = toBasicComponentTransferFilterOperation(filterOperation);
265            ComponentTransferFunction transferFunction;
266            transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR;
267            float amount = narrowPrecisionToFloat(componentTransferOperation->amount());
268            transferFunction.slope = amount;
269            transferFunction.intercept = -0.5 * amount + 0.5;
270
271            ComponentTransferFunction nullFunction;
272            effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction);
273            break;
274        }
275        case FilterOperation::BLUR: {
276            BlurFilterOperation* blurOperation = toBlurFilterOperation(filterOperation);
277            float stdDeviation = floatValueForLength(blurOperation->stdDeviation(), 0);
278            effect = FEGaussianBlur::create(this, stdDeviation, stdDeviation, consumer == FilterProperty ? EDGEMODE_NONE : EDGEMODE_DUPLICATE);
279            break;
280        }
281        case FilterOperation::DROP_SHADOW: {
282            DropShadowFilterOperation* dropShadowOperation = toDropShadowFilterOperation(filterOperation);
283            effect = FEDropShadow::create(this, dropShadowOperation->stdDeviation(), dropShadowOperation->stdDeviation(),
284                                                dropShadowOperation->x(), dropShadowOperation->y(), dropShadowOperation->color(), 1);
285            break;
286        }
287        default:
288            break;
289        }
290
291        if (effect) {
292            // Unlike SVG Filters and CSSFilterImages, filter functions on the filter
293            // property applied here should not clip to their primitive subregions.
294            effect->setClipsToBounds(consumer == FilterFunction);
295            effect->setOperatingColorSpace(ColorSpaceDeviceRGB);
296
297            if (filterOperation->type() != FilterOperation::REFERENCE) {
298                effect->inputEffects().append(previousEffect);
299                m_effects.append(effect);
300            }
301            previousEffect = effect.release();
302        }
303    }
304
305    // If we didn't make any effects, tell our caller we are not valid
306    if (!m_effects.size())
307        return false;
308
309    setMaxEffectRects(m_sourceDrawingRegion);
310
311    return true;
312}
313
314bool FilterEffectRenderer::updateBackingStoreRect(const FloatRect& filterRect)
315{
316    if (!filterRect.isZero() && FilterEffect::isFilterSizeValid(filterRect)) {
317        FloatRect currentSourceRect = sourceImageRect();
318        if (filterRect != currentSourceRect) {
319            setSourceImageRect(filterRect);
320            return true;
321        }
322    }
323    return false;
324}
325
326void FilterEffectRenderer::allocateBackingStoreIfNeeded()
327{
328    // At this point the effect chain has been built, and the
329    // source image sizes set. We just need to attach the graphic
330    // buffer if we have not yet done so.
331    if (!m_graphicsBufferAttached) {
332        IntSize logicalSize(m_sourceDrawingRegion.width(), m_sourceDrawingRegion.height());
333        if (!sourceImage() || sourceImage()->logicalSize() != logicalSize)
334            setSourceImage(ImageBuffer::create(logicalSize, filterScale(), ColorSpaceDeviceRGB, renderingMode()));
335        m_graphicsBufferAttached = true;
336    }
337}
338
339void FilterEffectRenderer::clearIntermediateResults()
340{
341    m_sourceGraphic->clearResult();
342    for (size_t i = 0; i < m_effects.size(); ++i)
343        m_effects[i]->clearResult();
344}
345
346void FilterEffectRenderer::apply()
347{
348    RefPtr<FilterEffect> effect = lastEffect();
349    effect->apply();
350    effect->transformResultColorSpace(ColorSpaceDeviceRGB);
351}
352
353LayoutRect FilterEffectRenderer::computeSourceImageRectForDirtyRect(const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect)
354{
355    // The result of this function is the area in the "filterBoxRect" that needs to be repainted, so that we fully cover the "dirtyRect".
356    LayoutRect rectForRepaint = dirtyRect;
357    if (hasFilterThatMovesPixels()) {
358        // Note that the outsets are reversed here because we are going backwards -> we have the dirty rect and
359        // need to find out what is the rectangle that might influence the result inside that dirty rect.
360        rectForRepaint.move(-m_outsets.right(), -m_outsets.bottom());
361        rectForRepaint.expand(m_outsets.left() + m_outsets.right(), m_outsets.top() + m_outsets.bottom());
362    }
363    rectForRepaint.intersect(filterBoxRect);
364    return rectForRepaint;
365}
366
367bool FilterEffectRendererHelper::prepareFilterEffect(RenderLayer* renderLayer, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect)
368{
369    ASSERT(m_haveFilterEffect && renderLayer->filterRenderer());
370    m_renderLayer = renderLayer;
371    m_repaintRect = dirtyRect;
372
373    FilterEffectRenderer* filter = renderLayer->filterRenderer();
374    LayoutRect filterSourceRect = filter->computeSourceImageRectForDirtyRect(filterBoxRect, dirtyRect);
375    m_paintOffset = filterSourceRect.location();
376
377    if (filterSourceRect.isEmpty()) {
378        // The dirty rect is not in view, just bail out.
379        m_haveFilterEffect = false;
380        return false;
381    }
382
383    bool hasUpdatedBackingStore = filter->updateBackingStoreRect(filterSourceRect);
384    if (filter->hasFilterThatMovesPixels()) {
385        if (hasUpdatedBackingStore)
386            m_repaintRect = filterSourceRect;
387        else {
388            m_repaintRect.unite(layerRepaintRect);
389            m_repaintRect.intersect(filterSourceRect);
390        }
391    }
392    return true;
393}
394
395GraphicsContext* FilterEffectRendererHelper::filterContext() const
396{
397    if (!m_haveFilterEffect)
398        return 0;
399
400    FilterEffectRenderer* filter = m_renderLayer->filterRenderer();
401    return filter->inputContext();
402}
403
404bool FilterEffectRendererHelper::beginFilterEffect()
405{
406    ASSERT(m_renderLayer);
407
408    FilterEffectRenderer* filter = m_renderLayer->filterRenderer();
409    filter->allocateBackingStoreIfNeeded();
410    // Paint into the context that represents the SourceGraphic of the filter.
411    GraphicsContext* sourceGraphicsContext = filter->inputContext();
412    if (!sourceGraphicsContext || !FilterEffect::isFilterSizeValid(filter->filterRegion())) {
413        // Disable the filters and continue.
414        m_haveFilterEffect = false;
415        return false;
416    }
417
418    // Translate the context so that the contents of the layer is captuterd in the offscreen memory buffer.
419    sourceGraphicsContext->save();
420    sourceGraphicsContext->translate(-m_paintOffset.x(), -m_paintOffset.y());
421    sourceGraphicsContext->clearRect(m_repaintRect);
422    sourceGraphicsContext->clip(m_repaintRect);
423
424    m_startedFilterEffect = true;
425    return true;
426}
427
428void FilterEffectRendererHelper::applyFilterEffect(GraphicsContext* destinationContext)
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    LayoutRect destRect = filter->outputRect();
438    destRect.move(m_paintOffset.x(), m_paintOffset.y());
439
440    destinationContext->drawImageBuffer(filter->output(), m_renderLayer->renderer().style().colorSpace(),
441        pixelSnappedForPainting(destRect, m_renderLayer->renderer().document().deviceScaleFactor()));
442
443    filter->clearIntermediateResults();
444}
445
446} // namespace WebCore
447
448#endif // ENABLE(CSS_FILTERS)
449