Home | History | Annotate | Download | only in rendering
      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/fetch/DocumentResource.h"
     32 #include "core/fetch/DocumentResourceReference.h"
     33 #include "core/frame/Settings.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/SkiaImageFilterBuilder.h"
     49 #include "wtf/MathExtras.h"
     50 #include <algorithm>
     51 
     52 namespace blink {
     53 
     54 static inline void endMatrixRow(Vector<float>& parameters)
     55 {
     56     parameters.append(0);
     57     parameters.append(0);
     58 }
     59 
     60 static inline void lastMatrixRow(Vector<float>& parameters)
     61 {
     62     parameters.append(0);
     63     parameters.append(0);
     64     parameters.append(0);
     65     parameters.append(1);
     66     parameters.append(0);
     67 }
     68 
     69 FilterEffectRenderer::FilterEffectRenderer()
     70     : Filter(AffineTransform())
     71     , m_graphicsBufferAttached(false)
     72     , m_hasFilterThatMovesPixels(false)
     73 {
     74     m_sourceGraphic = SourceGraphic::create(this);
     75 }
     76 
     77 FilterEffectRenderer::~FilterEffectRenderer()
     78 {
     79 }
     80 
     81 GraphicsContext* FilterEffectRenderer::inputContext()
     82 {
     83     return sourceImage() ? sourceImage()->context() : 0;
     84 }
     85 
     86 bool FilterEffectRenderer::build(RenderObject* renderer, const FilterOperations& operations)
     87 {
     88     m_hasFilterThatMovesPixels = operations.hasFilterThatMovesPixels();
     89 
     90     // Inverse zoom the pre-zoomed CSS shorthand filters, so that they are in the same zoom as the unzoomed reference filters.
     91     const RenderStyle* style = renderer->style();
     92     float invZoom = style ? 1.0f / style->effectiveZoom() : 1.0f;
     93 
     94     RefPtr<FilterEffect> previousEffect = m_sourceGraphic;
     95     for (size_t i = 0; i < operations.operations().size(); ++i) {
     96         RefPtr<FilterEffect> effect;
     97         FilterOperation* filterOperation = operations.operations().at(i).get();
     98         switch (filterOperation->type()) {
     99         case FilterOperation::REFERENCE: {
    100             effect = ReferenceFilterBuilder::build(this, renderer, previousEffect.get(), toReferenceFilterOperation(filterOperation));
    101             break;
    102         }
    103         case FilterOperation::GRAYSCALE: {
    104             Vector<float> inputParameters;
    105             double oneMinusAmount = clampTo(1 - toBasicColorMatrixFilterOperation(filterOperation)->amount(), 0.0, 1.0);
    106 
    107             // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#grayscaleEquivalent
    108             // for information on parameters.
    109 
    110             inputParameters.append(narrowPrecisionToFloat(0.2126 + 0.7874 * oneMinusAmount));
    111             inputParameters.append(narrowPrecisionToFloat(0.7152 - 0.7152 * oneMinusAmount));
    112             inputParameters.append(narrowPrecisionToFloat(0.0722 - 0.0722 * oneMinusAmount));
    113             endMatrixRow(inputParameters);
    114 
    115             inputParameters.append(narrowPrecisionToFloat(0.2126 - 0.2126 * oneMinusAmount));
    116             inputParameters.append(narrowPrecisionToFloat(0.7152 + 0.2848 * oneMinusAmount));
    117             inputParameters.append(narrowPrecisionToFloat(0.0722 - 0.0722 * oneMinusAmount));
    118             endMatrixRow(inputParameters);
    119 
    120             inputParameters.append(narrowPrecisionToFloat(0.2126 - 0.2126 * oneMinusAmount));
    121             inputParameters.append(narrowPrecisionToFloat(0.7152 - 0.7152 * oneMinusAmount));
    122             inputParameters.append(narrowPrecisionToFloat(0.0722 + 0.9278 * oneMinusAmount));
    123             endMatrixRow(inputParameters);
    124 
    125             lastMatrixRow(inputParameters);
    126 
    127             effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters);
    128             break;
    129         }
    130         case FilterOperation::SEPIA: {
    131             Vector<float> inputParameters;
    132             double oneMinusAmount = clampTo(1 - toBasicColorMatrixFilterOperation(filterOperation)->amount(), 0.0, 1.0);
    133 
    134             // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#sepiaEquivalent
    135             // for information on parameters.
    136 
    137             inputParameters.append(narrowPrecisionToFloat(0.393 + 0.607 * oneMinusAmount));
    138             inputParameters.append(narrowPrecisionToFloat(0.769 - 0.769 * oneMinusAmount));
    139             inputParameters.append(narrowPrecisionToFloat(0.189 - 0.189 * oneMinusAmount));
    140             endMatrixRow(inputParameters);
    141 
    142             inputParameters.append(narrowPrecisionToFloat(0.349 - 0.349 * oneMinusAmount));
    143             inputParameters.append(narrowPrecisionToFloat(0.686 + 0.314 * oneMinusAmount));
    144             inputParameters.append(narrowPrecisionToFloat(0.168 - 0.168 * oneMinusAmount));
    145             endMatrixRow(inputParameters);
    146 
    147             inputParameters.append(narrowPrecisionToFloat(0.272 - 0.272 * oneMinusAmount));
    148             inputParameters.append(narrowPrecisionToFloat(0.534 - 0.534 * oneMinusAmount));
    149             inputParameters.append(narrowPrecisionToFloat(0.131 + 0.869 * oneMinusAmount));
    150             endMatrixRow(inputParameters);
    151 
    152             lastMatrixRow(inputParameters);
    153 
    154             effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters);
    155             break;
    156         }
    157         case FilterOperation::SATURATE: {
    158             Vector<float> inputParameters;
    159             inputParameters.append(narrowPrecisionToFloat(toBasicColorMatrixFilterOperation(filterOperation)->amount()));
    160             effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_SATURATE, inputParameters);
    161             break;
    162         }
    163         case FilterOperation::HUE_ROTATE: {
    164             Vector<float> inputParameters;
    165             inputParameters.append(narrowPrecisionToFloat(toBasicColorMatrixFilterOperation(filterOperation)->amount()));
    166             effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_HUEROTATE, inputParameters);
    167             break;
    168         }
    169         case FilterOperation::INVERT: {
    170             BasicComponentTransferFilterOperation* componentTransferOperation = toBasicComponentTransferFilterOperation(filterOperation);
    171             ComponentTransferFunction transferFunction;
    172             transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE;
    173             Vector<float> transferParameters;
    174             transferParameters.append(narrowPrecisionToFloat(componentTransferOperation->amount()));
    175             transferParameters.append(narrowPrecisionToFloat(1 - componentTransferOperation->amount()));
    176             transferFunction.tableValues = transferParameters;
    177 
    178             ComponentTransferFunction nullFunction;
    179             effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction);
    180             break;
    181         }
    182         case FilterOperation::OPACITY: {
    183             ComponentTransferFunction transferFunction;
    184             transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE;
    185             Vector<float> transferParameters;
    186             transferParameters.append(0);
    187             transferParameters.append(narrowPrecisionToFloat(toBasicComponentTransferFilterOperation(filterOperation)->amount()));
    188             transferFunction.tableValues = transferParameters;
    189 
    190             ComponentTransferFunction nullFunction;
    191             effect = FEComponentTransfer::create(this, nullFunction, nullFunction, nullFunction, transferFunction);
    192             break;
    193         }
    194         case FilterOperation::BRIGHTNESS: {
    195             ComponentTransferFunction transferFunction;
    196             transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR;
    197             transferFunction.slope = narrowPrecisionToFloat(toBasicComponentTransferFilterOperation(filterOperation)->amount());
    198             transferFunction.intercept = 0;
    199 
    200             ComponentTransferFunction nullFunction;
    201             effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction);
    202             break;
    203         }
    204         case FilterOperation::CONTRAST: {
    205             ComponentTransferFunction transferFunction;
    206             transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR;
    207             float amount = narrowPrecisionToFloat(toBasicComponentTransferFilterOperation(filterOperation)->amount());
    208             transferFunction.slope = amount;
    209             transferFunction.intercept = -0.5 * amount + 0.5;
    210 
    211             ComponentTransferFunction nullFunction;
    212             effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction);
    213             break;
    214         }
    215         case FilterOperation::BLUR: {
    216             float stdDeviation = floatValueForLength(toBlurFilterOperation(filterOperation)->stdDeviation(), 0) * invZoom;
    217             effect = FEGaussianBlur::create(this, stdDeviation, stdDeviation);
    218             break;
    219         }
    220         case FilterOperation::DROP_SHADOW: {
    221             DropShadowFilterOperation* dropShadowOperation = toDropShadowFilterOperation(filterOperation);
    222             float stdDeviation = dropShadowOperation->stdDeviation() * invZoom;
    223             float x = dropShadowOperation->x() * invZoom;
    224             float y = dropShadowOperation->y() * invZoom;
    225             effect = FEDropShadow::create(this, stdDeviation, stdDeviation, x, y, dropShadowOperation->color(), 1);
    226             break;
    227         }
    228         default:
    229             break;
    230         }
    231 
    232         if (effect) {
    233             if (filterOperation->type() != FilterOperation::REFERENCE) {
    234                 // Unlike SVG, filters applied here should not clip to their primitive subregions.
    235                 effect->setClipsToBounds(false);
    236                 effect->setOperatingColorSpace(ColorSpaceDeviceRGB);
    237                 effect->inputEffects().append(previousEffect);
    238             }
    239             previousEffect = effect.release();
    240         }
    241     }
    242 
    243     // We need to keep the old effects alive until this point, so that SVG reference filters
    244     // can share cached resources across frames.
    245     m_lastEffect = previousEffect;
    246 
    247     // If we didn't make any effects, tell our caller we are not valid
    248     if (!m_lastEffect.get())
    249         return false;
    250 
    251     return true;
    252 }
    253 
    254 bool FilterEffectRenderer::updateBackingStoreRect(const FloatRect& floatFilterRect)
    255 {
    256     IntRect filterRect = enclosingIntRect(floatFilterRect);
    257     if (!filterRect.isEmpty() && FilterEffect::isFilterSizeValid(filterRect)) {
    258         FloatRect currentSourceRect = sourceImageRect();
    259         if (filterRect != currentSourceRect) {
    260             setSourceImageRect(filterRect);
    261             return true;
    262         }
    263     }
    264     return false;
    265 }
    266 
    267 void FilterEffectRenderer::allocateBackingStoreIfNeeded()
    268 {
    269     // At this point the effect chain has been built, and the
    270     // source image sizes set. We just need to attach the graphic
    271     // buffer if we have not yet done so.
    272     if (!m_graphicsBufferAttached) {
    273         IntSize logicalSize(m_sourceDrawingRegion.width(), m_sourceDrawingRegion.height());
    274         if (!sourceImage() || sourceImage()->size() != logicalSize) {
    275             OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(logicalSize));
    276             setSourceImage(ImageBuffer::create(surface.release()));
    277         }
    278         m_graphicsBufferAttached = true;
    279     }
    280 }
    281 
    282 void FilterEffectRenderer::clearIntermediateResults()
    283 {
    284     if (m_lastEffect.get())
    285         m_lastEffect->clearResultsRecursive();
    286 }
    287 
    288 void FilterEffectRenderer::apply()
    289 {
    290     RefPtr<FilterEffect> effect = lastEffect();
    291     effect->apply();
    292     effect->transformResultColorSpace(ColorSpaceDeviceRGB);
    293 }
    294 
    295 LayoutRect FilterEffectRenderer::computeSourceImageRectForDirtyRect(const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect)
    296 {
    297     // The result of this function is the area in the "filterBoxRect" that needs paint invalidation, so that we fully cover the "dirtyRect".
    298     FloatRect rectForPaintInvalidation = dirtyRect;
    299     float inf = std::numeric_limits<float>::infinity();
    300     FloatRect clipRect = FloatRect(FloatPoint(-inf, -inf), FloatSize(inf, inf));
    301     rectForPaintInvalidation = lastEffect()->getSourceRect(rectForPaintInvalidation, clipRect);
    302     rectForPaintInvalidation.intersect(filterBoxRect);
    303     return LayoutRect(rectForPaintInvalidation);
    304 }
    305 
    306 bool FilterEffectRendererHelper::prepareFilterEffect(RenderLayer* renderLayer, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect)
    307 {
    308     ASSERT(m_haveFilterEffect && renderLayer->filterRenderer());
    309     m_renderLayer = renderLayer;
    310     m_paintInvalidationRect = dirtyRect;
    311 
    312     // Get the zoom factor to scale the filterSourceRect input
    313     const RenderLayerModelObject* renderer = renderLayer->renderer();
    314     const RenderStyle* style = renderer ? renderer->style() : 0;
    315     float zoom = style ? style->effectiveZoom() : 1.0f;
    316 
    317     // Prepare a transformation that brings the coordinates into the space
    318     // filter coordinates are defined in.
    319     AffineTransform absoluteTransform;
    320     // FIXME: Should these really be upconverted to doubles and not rounded? crbug.com/350474
    321     absoluteTransform.translate(filterBoxRect.x().toDouble(), filterBoxRect.y().toDouble());
    322     absoluteTransform.scale(zoom, zoom);
    323 
    324     FilterEffectRenderer* filter = renderLayer->filterRenderer();
    325     filter->setAbsoluteTransform(absoluteTransform);
    326 
    327     IntRect filterSourceRect = pixelSnappedIntRect(filter->computeSourceImageRectForDirtyRect(filterBoxRect, dirtyRect));
    328 
    329     if (filterSourceRect.isEmpty()) {
    330         // The dirty rect is not in view, just bail out.
    331         m_haveFilterEffect = false;
    332         return false;
    333     }
    334 
    335     m_filterBoxRect = filterBoxRect;
    336     filter->setFilterRegion(filter->mapAbsoluteRectToLocalRect(filterSourceRect));
    337     filter->lastEffect()->determineFilterPrimitiveSubregion(MapRectForward);
    338 
    339     bool hasUpdatedBackingStore = filter->updateBackingStoreRect(filterSourceRect);
    340     if (filter->hasFilterThatMovesPixels()) {
    341         if (hasUpdatedBackingStore)
    342             m_paintInvalidationRect = filterSourceRect;
    343         else
    344             m_paintInvalidationRect.intersect(filterSourceRect);
    345     }
    346     return true;
    347 }
    348 
    349 GraphicsContext* FilterEffectRendererHelper::beginFilterEffect(GraphicsContext* context)
    350 {
    351     ASSERT(m_renderLayer);
    352 
    353     FilterEffectRenderer* filter = m_renderLayer->filterRenderer();
    354     if (m_renderLayer->renderer()->document().settings()->deferredFiltersEnabled()) {
    355         SkiaImageFilterBuilder builder(context);
    356         RefPtr<ImageFilter> imageFilter = builder.build(filter->lastEffect().get(), ColorSpaceDeviceRGB);
    357         if (!imageFilter) {
    358             m_haveFilterEffect = false;
    359             return context;
    360         }
    361         m_savedGraphicsContext = context;
    362         context->save();
    363         FloatRect boundaries = mapImageFilterRect(imageFilter.get(), m_filterBoxRect);
    364         context->translate(m_filterBoxRect.x(), m_filterBoxRect.y());
    365         boundaries.move(-m_filterBoxRect.x(), -m_filterBoxRect.y());
    366         context->beginLayer(1, CompositeSourceOver, &boundaries, ColorFilterNone, imageFilter.get());
    367         context->translate(-m_filterBoxRect.x(), -m_filterBoxRect.y());
    368         return context;
    369     }
    370     filter->allocateBackingStoreIfNeeded();
    371     // Paint into the context that represents the SourceGraphic of the filter.
    372     GraphicsContext* sourceGraphicsContext = filter->inputContext();
    373     if (!sourceGraphicsContext || !FilterEffect::isFilterSizeValid(filter->absoluteFilterRegion())) {
    374         // Disable the filters and continue.
    375         m_haveFilterEffect = false;
    376         return context;
    377     }
    378 
    379     m_savedGraphicsContext = context;
    380 
    381     // Translate the context so that the contents of the layer is captuterd in the offscreen memory buffer.
    382     sourceGraphicsContext->save();
    383     // FIXME: can we just use sourceImageRect for everything, and get rid of
    384     // m_paintInvalidationRect?
    385     FloatPoint offset = filter->sourceImageRect().location();
    386     sourceGraphicsContext->translate(-offset.x(), -offset.y());
    387     sourceGraphicsContext->clearRect(m_paintInvalidationRect);
    388     sourceGraphicsContext->clip(m_paintInvalidationRect);
    389 
    390     return sourceGraphicsContext;
    391 }
    392 
    393 GraphicsContext* FilterEffectRendererHelper::applyFilterEffect()
    394 {
    395     ASSERT(m_haveFilterEffect && m_renderLayer->filterRenderer());
    396     FilterEffectRenderer* filter = m_renderLayer->filterRenderer();
    397 
    398     if (m_renderLayer->renderer()->document().settings()->deferredFiltersEnabled()) {
    399         GraphicsContext* context = m_savedGraphicsContext;
    400         context->endLayer();
    401         context->restore();
    402         return context;
    403     }
    404 
    405     filter->inputContext()->restore();
    406 
    407     filter->apply();
    408 
    409     // Get the filtered output and draw it in place.
    410     m_savedGraphicsContext->drawImageBuffer(filter->output(), filter->outputRect());
    411 
    412     filter->clearIntermediateResults();
    413 
    414     return m_savedGraphicsContext;
    415 }
    416 
    417 } // namespace blink
    418 
    419