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/loader/cache/DocumentResource.h"
33#include "core/loader/cache/DocumentResourceReference.h"
34#include "core/page/Page.h"
35#include "core/platform/FloatConversion.h"
36#include "core/platform/graphics/ColorSpace.h"
37#include "core/platform/graphics/filters/FEColorMatrix.h"
38#include "core/platform/graphics/filters/FEComponentTransfer.h"
39#include "core/platform/graphics/filters/FEDropShadow.h"
40#include "core/platform/graphics/filters/FEGaussianBlur.h"
41#include "core/platform/graphics/filters/custom/CustomFilterGlobalContext.h"
42#include "core/platform/graphics/filters/custom/CustomFilterValidatedProgram.h"
43#include "core/platform/graphics/filters/custom/FECustomFilter.h"
44#include "core/platform/graphics/filters/custom/ValidatedCustomFilterOperation.h"
45#include "core/rendering/RenderLayer.h"
46#include "core/rendering/RenderView.h"
47#include "core/rendering/svg/ReferenceFilterBuilder.h"
48
49#include "core/svg/SVGElement.h"
50#include "core/svg/SVGFilterPrimitiveStandardAttributes.h"
51#include "wtf/MathExtras.h"
52#include <algorithm>
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
71inline bool isFilterSizeValid(FloatRect rect)
72{
73    if (rect.width() < 0 || rect.width() > kMaxFilterSize
74        || rect.height() < 0 || rect.height() > kMaxFilterSize)
75        return false;
76    return true;
77}
78
79static PassRefPtr<FECustomFilter> createCustomFilterEffect(Filter* filter, Document* document, ValidatedCustomFilterOperation* operation)
80{
81    if (!document)
82        return 0;
83
84    CustomFilterGlobalContext* globalContext = document->renderView()->customFilterGlobalContext();
85    globalContext->prepareContextIfNeeded();
86    if (!globalContext->context())
87        return 0;
88
89    return FECustomFilter::create(filter, globalContext->context(), operation->validatedProgram(), operation->parameters(),
90        operation->meshRows(), operation->meshColumns(),  operation->meshType());
91}
92
93FilterEffectRenderer::FilterEffectRenderer()
94    : Filter(AffineTransform())
95    , m_graphicsBufferAttached(false)
96    , m_hasFilterThatMovesPixels(false)
97    , m_hasCustomShaderFilter(false)
98{
99    setFilterResolution(FloatSize(1, 1));
100    m_sourceGraphic = SourceGraphic::create(this);
101}
102
103FilterEffectRenderer::~FilterEffectRenderer()
104{
105}
106
107GraphicsContext* FilterEffectRenderer::inputContext()
108{
109    return sourceImage() ? sourceImage()->context() : 0;
110}
111
112bool FilterEffectRenderer::build(RenderObject* renderer, const FilterOperations& operations)
113{
114    m_hasCustomShaderFilter = false;
115    m_hasFilterThatMovesPixels = operations.hasFilterThatMovesPixels();
116
117    // Inverse zoom the pre-zoomed CSS shorthand filters, so that they are in the same zoom as the unzoomed reference filters.
118    const RenderStyle* style = renderer->style();
119    // FIXME: The effects now contain high dpi information, but the software path doesn't (yet) scale its backing.
120    //        When the proper dpi dependant backing size is allocated, we should remove deviceScaleFactor(...) here.
121    float invZoom = 1.0f / ((style ? style->effectiveZoom() : 1.0f) * deviceScaleFactor(renderer->frame()));
122
123    RefPtr<FilterEffect> previousEffect = m_sourceGraphic;
124    for (size_t i = 0; i < operations.operations().size(); ++i) {
125        RefPtr<FilterEffect> effect;
126        FilterOperation* filterOperation = operations.operations().at(i).get();
127        switch (filterOperation->getOperationType()) {
128        case FilterOperation::REFERENCE: {
129            ReferenceFilterOperation* referenceOperation = static_cast<ReferenceFilterOperation*>(filterOperation);
130            effect = ReferenceFilterBuilder::build(this, renderer, previousEffect.get(), referenceOperation);
131            break;
132        }
133        case FilterOperation::GRAYSCALE: {
134            BasicColorMatrixFilterOperation* colorMatrixOperation = static_cast<BasicColorMatrixFilterOperation*>(filterOperation);
135            Vector<float> inputParameters;
136            double oneMinusAmount = clampTo(1 - colorMatrixOperation->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            BasicColorMatrixFilterOperation* colorMatrixOperation = static_cast<BasicColorMatrixFilterOperation*>(filterOperation);
163            Vector<float> inputParameters;
164            double oneMinusAmount = clampTo(1 - colorMatrixOperation->amount(), 0.0, 1.0);
165
166            // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#sepiaEquivalent
167            // for information on parameters.
168
169            inputParameters.append(narrowPrecisionToFloat(0.393 + 0.607 * oneMinusAmount));
170            inputParameters.append(narrowPrecisionToFloat(0.769 - 0.769 * oneMinusAmount));
171            inputParameters.append(narrowPrecisionToFloat(0.189 - 0.189 * oneMinusAmount));
172            endMatrixRow(inputParameters);
173
174            inputParameters.append(narrowPrecisionToFloat(0.349 - 0.349 * oneMinusAmount));
175            inputParameters.append(narrowPrecisionToFloat(0.686 + 0.314 * oneMinusAmount));
176            inputParameters.append(narrowPrecisionToFloat(0.168 - 0.168 * oneMinusAmount));
177            endMatrixRow(inputParameters);
178
179            inputParameters.append(narrowPrecisionToFloat(0.272 - 0.272 * oneMinusAmount));
180            inputParameters.append(narrowPrecisionToFloat(0.534 - 0.534 * oneMinusAmount));
181            inputParameters.append(narrowPrecisionToFloat(0.131 + 0.869 * oneMinusAmount));
182            endMatrixRow(inputParameters);
183
184            lastMatrixRow(inputParameters);
185
186            effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters);
187            break;
188        }
189        case FilterOperation::SATURATE: {
190            BasicColorMatrixFilterOperation* colorMatrixOperation = static_cast<BasicColorMatrixFilterOperation*>(filterOperation);
191            Vector<float> inputParameters;
192            inputParameters.append(narrowPrecisionToFloat(colorMatrixOperation->amount()));
193            effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_SATURATE, inputParameters);
194            break;
195        }
196        case FilterOperation::HUE_ROTATE: {
197            BasicColorMatrixFilterOperation* colorMatrixOperation = static_cast<BasicColorMatrixFilterOperation*>(filterOperation);
198            Vector<float> inputParameters;
199            inputParameters.append(narrowPrecisionToFloat(colorMatrixOperation->amount()));
200            effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_HUEROTATE, inputParameters);
201            break;
202        }
203        case FilterOperation::INVERT: {
204            BasicComponentTransferFilterOperation* componentTransferOperation = static_cast<BasicComponentTransferFilterOperation*>(filterOperation);
205            ComponentTransferFunction transferFunction;
206            transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE;
207            Vector<float> transferParameters;
208            transferParameters.append(narrowPrecisionToFloat(componentTransferOperation->amount()));
209            transferParameters.append(narrowPrecisionToFloat(1 - componentTransferOperation->amount()));
210            transferFunction.tableValues = transferParameters;
211
212            ComponentTransferFunction nullFunction;
213            effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction);
214            break;
215        }
216        case FilterOperation::OPACITY: {
217            BasicComponentTransferFilterOperation* componentTransferOperation = static_cast<BasicComponentTransferFilterOperation*>(filterOperation);
218            ComponentTransferFunction transferFunction;
219            transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE;
220            Vector<float> transferParameters;
221            transferParameters.append(0);
222            transferParameters.append(narrowPrecisionToFloat(componentTransferOperation->amount()));
223            transferFunction.tableValues = transferParameters;
224
225            ComponentTransferFunction nullFunction;
226            effect = FEComponentTransfer::create(this, nullFunction, nullFunction, nullFunction, transferFunction);
227            break;
228        }
229        case FilterOperation::BRIGHTNESS: {
230            BasicComponentTransferFilterOperation* componentTransferOperation = static_cast<BasicComponentTransferFilterOperation*>(filterOperation);
231            ComponentTransferFunction transferFunction;
232            transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR;
233            transferFunction.slope = narrowPrecisionToFloat(componentTransferOperation->amount());
234            transferFunction.intercept = 0;
235
236            ComponentTransferFunction nullFunction;
237            effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction);
238            break;
239        }
240        case FilterOperation::CONTRAST: {
241            BasicComponentTransferFilterOperation* componentTransferOperation = static_cast<BasicComponentTransferFilterOperation*>(filterOperation);
242            ComponentTransferFunction transferFunction;
243            transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR;
244            float amount = narrowPrecisionToFloat(componentTransferOperation->amount());
245            transferFunction.slope = amount;
246            transferFunction.intercept = -0.5 * amount + 0.5;
247
248            ComponentTransferFunction nullFunction;
249            effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction);
250            break;
251        }
252        case FilterOperation::BLUR: {
253            BlurFilterOperation* blurOperation = static_cast<BlurFilterOperation*>(filterOperation);
254            float stdDeviation = floatValueForLength(blurOperation->stdDeviation(), 0) * invZoom;
255            effect = FEGaussianBlur::create(this, stdDeviation, stdDeviation);
256            break;
257        }
258        case FilterOperation::DROP_SHADOW: {
259            DropShadowFilterOperation* dropShadowOperation = static_cast<DropShadowFilterOperation*>(filterOperation);
260            float stdDeviation = dropShadowOperation->stdDeviation() * invZoom;
261            float x = dropShadowOperation->x() * invZoom;
262            float y = dropShadowOperation->y() * invZoom;
263            effect = FEDropShadow::create(this, stdDeviation, stdDeviation, x, y, dropShadowOperation->color(), 1);
264            break;
265        }
266        case FilterOperation::CUSTOM:
267            // CUSTOM operations are always converted to VALIDATED_CUSTOM before getting here.
268            // The conversion happens in RenderLayer::computeFilterOperations.
269            ASSERT_NOT_REACHED();
270            break;
271        case FilterOperation::VALIDATED_CUSTOM: {
272            ValidatedCustomFilterOperation* customFilterOperation = static_cast<ValidatedCustomFilterOperation*>(filterOperation);
273            Document* document = renderer ? renderer->document() : 0;
274            effect = createCustomFilterEffect(this, document, customFilterOperation);
275            if (effect)
276                m_hasCustomShaderFilter = true;
277            break;
278        }
279        default:
280            break;
281        }
282
283        if (effect) {
284            if (filterOperation->getOperationType() != FilterOperation::REFERENCE) {
285                // Unlike SVG, filters applied here should not clip to their primitive subregions.
286                effect->setClipsToBounds(false);
287                effect->setOperatingColorSpace(ColorSpaceDeviceRGB);
288                effect->inputEffects().append(previousEffect);
289            }
290            previousEffect = effect.release();
291        }
292    }
293
294    // We need to keep the old effects alive until this point, so that filters like FECustomFilter
295    // can share cached resources across frames.
296    m_lastEffect = previousEffect;
297
298    // If we didn't make any effects, tell our caller we are not valid
299    if (!m_lastEffect.get())
300        return false;
301
302    return true;
303}
304
305bool FilterEffectRenderer::updateBackingStoreRect(const FloatRect& filterRect)
306{
307    if (!filterRect.isZero() && isFilterSizeValid(filterRect)) {
308        FloatRect currentSourceRect = sourceImageRect();
309        if (filterRect != currentSourceRect) {
310            setSourceImageRect(filterRect);
311            return true;
312        }
313    }
314    return false;
315}
316
317void FilterEffectRenderer::allocateBackingStoreIfNeeded()
318{
319    // At this point the effect chain has been built, and the
320    // source image sizes set. We just need to attach the graphic
321    // buffer if we have not yet done so.
322    if (!m_graphicsBufferAttached) {
323        IntSize logicalSize(m_sourceDrawingRegion.width(), m_sourceDrawingRegion.height());
324        if (!sourceImage() || sourceImage()->logicalSize() != logicalSize)
325            setSourceImage(ImageBuffer::create(logicalSize, 1, renderingMode()));
326        m_graphicsBufferAttached = true;
327    }
328}
329
330void FilterEffectRenderer::clearIntermediateResults()
331{
332    if (m_lastEffect.get())
333        m_lastEffect->clearResultsRecursive();
334}
335
336void FilterEffectRenderer::apply()
337{
338    RefPtr<FilterEffect> effect = lastEffect();
339    effect->apply();
340    effect->transformResultColorSpace(ColorSpaceDeviceRGB);
341}
342
343LayoutRect 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
361bool 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();
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
400GraphicsContext* 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
428GraphicsContext* 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