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