Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2004, 2005 Rob Buis <buis (at) kde.org>
      4  * Copyright (C) 2005 Eric Seidel <eric (at) webkit.org>
      5  * Copyright (C) 2009 Dirk Schulze <krit (at) webkit.org>
      6  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
      7  *
      8  * This library is free software; you can redistribute it and/or
      9  * modify it under the terms of the GNU Library General Public
     10  * License as published by the Free Software Foundation; either
     11  * version 2 of the License, or (at your option) any later version.
     12  *
     13  * This library is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  * Library General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU Library General Public License
     19  * along with this library; see the file COPYING.LIB.  If not, write to
     20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     21  * Boston, MA 02110-1301, USA.
     22  */
     23 
     24 #include "config.h"
     25 
     26 #include "core/rendering/svg/RenderSVGResourceFilter.h"
     27 
     28 #include "core/frame/Settings.h"
     29 #include "core/rendering/svg/RenderSVGResourceFilterPrimitive.h"
     30 #include "core/rendering/svg/SVGRenderingContext.h"
     31 #include "core/svg/SVGFilterPrimitiveStandardAttributes.h"
     32 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
     33 #include "platform/graphics/filters/SkiaImageFilterBuilder.h"
     34 #include "platform/graphics/filters/SourceAlpha.h"
     35 #include "platform/graphics/filters/SourceGraphic.h"
     36 
     37 using namespace std;
     38 
     39 namespace WebCore {
     40 
     41 const RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
     42 
     43 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
     44     : RenderSVGResourceContainer(node)
     45 {
     46 }
     47 
     48 RenderSVGResourceFilter::~RenderSVGResourceFilter()
     49 {
     50     m_filter.clear();
     51 }
     52 
     53 bool RenderSVGResourceFilter::isChildAllowed(RenderObject* child, RenderStyle*) const
     54 {
     55     return child->isSVGResourceFilterPrimitive();
     56 }
     57 
     58 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
     59 {
     60     m_filter.clear();
     61     markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
     62 }
     63 
     64 void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation)
     65 {
     66     ASSERT(client);
     67 
     68     if (FilterData* filterData = m_filter.get(client)) {
     69         if (filterData->savedContext)
     70             filterData->state = FilterData::MarkedForRemoval;
     71         else
     72             m_filter.remove(client);
     73     }
     74 
     75     markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
     76 }
     77 
     78 PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* filter)
     79 {
     80     SVGFilterElement* filterElement = toSVGFilterElement(element());
     81     FloatRect targetBoundingBox = filter->targetBoundingBox();
     82 
     83     // Add effects to the builder
     84     RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(SourceGraphic::create(filter), SourceAlpha::create(filter));
     85     for (SVGElement* element = Traversal<SVGElement>::firstChild(*filterElement); element; element = Traversal<SVGElement>::nextSibling(*element)) {
     86         if (!element->isFilterEffect() || !element->renderer())
     87             continue;
     88 
     89         SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
     90         RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter);
     91         if (!effect) {
     92             builder->clearEffects();
     93             return nullptr;
     94         }
     95         builder->appendEffectToEffectReferences(effect, effectElement->renderer());
     96         effectElement->setStandardAttributes(effect.get());
     97         effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnits()->currentValue()->enumValue(), targetBoundingBox));
     98         effect->setOperatingColorSpace(
     99             effectElement->renderer()->style()->svgStyle()->colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
    100         builder->add(AtomicString(effectElement->result()->currentValue()->value()), effect);
    101     }
    102     return builder.release();
    103 }
    104 
    105 void RenderSVGResourceFilter::adjustScaleForMaximumImageSize(const FloatSize& size, FloatSize& filterScale)
    106 {
    107     FloatSize scaledSize(size);
    108     scaledSize.scale(filterScale.width(), filterScale.height());
    109     float scaledArea = scaledSize.width() * scaledSize.height();
    110 
    111     if (scaledArea <= FilterEffect::maxFilterArea())
    112         return;
    113 
    114     // If area of scaled size is bigger than the upper limit, adjust the scale
    115     // to fit.
    116     filterScale.scale(sqrt(FilterEffect::maxFilterArea() / scaledArea));
    117 }
    118 
    119 static bool createImageBuffer(const Filter* filter, OwnPtr<ImageBuffer>& imageBuffer)
    120 {
    121     IntRect paintRect = filter->sourceImageRect();
    122     // Don't create empty ImageBuffers.
    123     if (paintRect.isEmpty())
    124         return false;
    125 
    126     OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(paintRect.size()));
    127     if (!surface->isValid())
    128         return false;
    129     OwnPtr<ImageBuffer> image = ImageBuffer::create(surface.release());
    130 
    131     GraphicsContext* imageContext = image->context();
    132     ASSERT(imageContext);
    133 
    134     imageContext->translate(-paintRect.x(), -paintRect.y());
    135     imageContext->concatCTM(filter->absoluteTransform());
    136     imageBuffer = image.release();
    137     return true;
    138 }
    139 
    140 static void beginDeferredFilter(GraphicsContext* context, FilterData* filterData, SVGFilterElement* filterElement)
    141 {
    142     SkiaImageFilterBuilder builder(context);
    143     RefPtr<ImageFilter> imageFilter = builder.build(filterData->builder->lastEffect(), ColorSpaceDeviceRGB);
    144     // FIXME: Remove the cache when impl-size painting is enabled on every platform and the non impl-side painting path is removed
    145     if (!context->isRecordingCanvas()) // Recording canvases do not use the cache
    146         filterData->filter->enableCache();
    147     FloatRect boundaries = enclosingIntRect(filterData->boundaries);
    148     context->save();
    149 
    150     FloatSize deviceSize = context->getCTM().mapSize(boundaries.size());
    151     float scaledArea = deviceSize.width() * deviceSize.height();
    152 
    153     // If area of scaled size is bigger than the upper limit, adjust the scale
    154     // to fit. Note that this only really matters in the non-impl-side painting
    155     // case, since the impl-side case never allocates a full-sized backing
    156     // store, only tile-sized.
    157     // FIXME: remove this once all platforms are using impl-side painting.
    158     // crbug.com/169282.
    159     if (scaledArea > FilterEffect::maxFilterArea()) {
    160         float scale = sqrtf(FilterEffect::maxFilterArea() / scaledArea);
    161         context->scale(scale, scale);
    162     }
    163     // Clip drawing of filtered image to primitive boundaries.
    164     context->clipRect(boundaries);
    165     if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
    166         // Get boundaries in device coords.
    167         // FIXME: See crbug.com/382491. Is the use of getCTM OK here, given it does not include device
    168         // zoom or High DPI adjustments?
    169         FloatSize size = context->getCTM().mapSize(boundaries.size());
    170         // Compute the scale amount required so that the resulting offscreen is exactly filterResX by filterResY pixels.
    171         float filterResScaleX = filterElement->filterResX()->currentValue()->value() / size.width();
    172         float filterResScaleY = filterElement->filterResY()->currentValue()->value() / size.height();
    173         // Scale the CTM so the primitive is drawn to filterRes.
    174         context->scale(filterResScaleX, filterResScaleY);
    175         // Create a resize filter with the inverse scale.
    176         AffineTransform resizeMatrix;
    177         resizeMatrix.scale(1 / filterResScaleX, 1 / filterResScaleY);
    178         imageFilter = builder.buildTransform(resizeMatrix, imageFilter.get());
    179     }
    180     // If the CTM contains rotation or shearing, apply the filter to
    181     // the unsheared/unrotated matrix, and do the shearing/rotation
    182     // as a final pass.
    183     AffineTransform ctm = context->getCTM();
    184     if (ctm.b() || ctm.c()) {
    185         AffineTransform scaleAndTranslate;
    186         scaleAndTranslate.translate(ctm.e(), ctm.f());
    187         scaleAndTranslate.scale(ctm.xScale(), ctm.yScale());
    188         ASSERT(scaleAndTranslate.isInvertible());
    189         AffineTransform shearAndRotate = scaleAndTranslate.inverse();
    190         shearAndRotate.multiply(ctm);
    191         context->setCTM(scaleAndTranslate);
    192         imageFilter = builder.buildTransform(shearAndRotate, imageFilter.get());
    193     }
    194     context->beginLayer(1, CompositeSourceOver, &boundaries, ColorFilterNone, imageFilter.get());
    195 }
    196 
    197 static void endDeferredFilter(GraphicsContext* context, FilterData* filterData)
    198 {
    199     context->endLayer();
    200     context->restore();
    201     // FIXME: Remove the cache when impl-size painting is enabled on every platform and the non impl-side painting path is removed
    202     if (!context->isRecordingCanvas()) // Recording canvases do not use the cache
    203         filterData->filter->disableCache();
    204 }
    205 
    206 bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
    207 {
    208     ASSERT(object);
    209     ASSERT(context);
    210     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
    211 
    212     clearInvalidationMask();
    213 
    214     bool deferredFiltersEnabled = object->document().settings()->deferredFiltersEnabled();
    215     if (m_filter.contains(object)) {
    216         FilterData* filterData = m_filter.get(object);
    217         if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying)
    218             filterData->state = FilterData::CycleDetected;
    219         if (deferredFiltersEnabled && filterData->state == FilterData::Built) {
    220             SVGFilterElement* filterElement = toSVGFilterElement(element());
    221             beginDeferredFilter(context, filterData, filterElement);
    222             return true;
    223         }
    224         return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now.
    225     }
    226 
    227     OwnPtr<FilterData> filterData(adoptPtr(new FilterData));
    228     FloatRect targetBoundingBox = object->objectBoundingBox();
    229 
    230     SVGFilterElement* filterElement = toSVGFilterElement(element());
    231     filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits()->currentValue()->enumValue(), targetBoundingBox);
    232     if (filterData->boundaries.isEmpty())
    233         return false;
    234 
    235     // Determine absolute transformation matrix for filter.
    236     AffineTransform absoluteTransform;
    237     SVGRenderingContext::calculateDeviceSpaceTransformation(object, absoluteTransform);
    238     if (!absoluteTransform.isInvertible())
    239         return false;
    240 
    241     // Filters cannot handle a full transformation, only scales in each direction.
    242     FloatSize filterScale;
    243 
    244     // Calculate the scale factor for the filter.
    245     // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
    246     if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
    247         //  If resolution is specified, scale to match it.
    248         filterScale = FloatSize(
    249             filterElement->filterResX()->currentValue()->value() / filterData->boundaries.width(),
    250             filterElement->filterResY()->currentValue()->value() / filterData->boundaries.height());
    251     } else {
    252         // Otherwise, use the scale of the absolute transform.
    253         filterScale = FloatSize(absoluteTransform.xScale(), absoluteTransform.yScale());
    254     }
    255     // The size of the scaled filter boundaries shouldn't be bigger than kMaxFilterSize.
    256     // Intermediate filters are limited by the filter boundaries so they can't be bigger than this.
    257     adjustScaleForMaximumImageSize(filterData->boundaries.size(), filterScale);
    258 
    259     filterData->drawingRegion = object->strokeBoundingBox();
    260     filterData->drawingRegion.intersect(filterData->boundaries);
    261     FloatRect absoluteDrawingRegion = filterData->drawingRegion;
    262     if (!deferredFiltersEnabled)
    263         absoluteDrawingRegion.scale(filterScale.width(), filterScale.height());
    264 
    265     IntRect intDrawingRegion = enclosingIntRect(absoluteDrawingRegion);
    266 
    267     // Create the SVGFilter object.
    268     bool primitiveBoundingBoxMode = filterElement->primitiveUnits()->currentValue()->enumValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
    269     filterData->shearFreeAbsoluteTransform = AffineTransform();
    270     if (!deferredFiltersEnabled)
    271         filterData->shearFreeAbsoluteTransform.scale(filterScale.width(), filterScale.height());
    272     filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, intDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
    273 
    274     // Create all relevant filter primitives.
    275     filterData->builder = buildPrimitives(filterData->filter.get());
    276     if (!filterData->builder)
    277         return false;
    278 
    279     FilterEffect* lastEffect = filterData->builder->lastEffect();
    280     if (!lastEffect)
    281         return false;
    282 
    283     lastEffect->determineFilterPrimitiveSubregion(ClipToFilterRegion);
    284 
    285     if (deferredFiltersEnabled) {
    286         FilterData* data = filterData.get();
    287         m_filter.set(object, filterData.release());
    288         beginDeferredFilter(context, data, filterElement);
    289         return true;
    290     }
    291 
    292     // If the drawingRegion is empty, we have something like <g filter=".."/>.
    293     // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource.
    294     if (filterData->drawingRegion.isEmpty()) {
    295         ASSERT(!m_filter.contains(object));
    296         filterData->savedContext = context;
    297         m_filter.set(object, filterData.release());
    298         return false;
    299     }
    300 
    301     OwnPtr<ImageBuffer> sourceGraphic;
    302     if (!createImageBuffer(filterData->filter.get(), sourceGraphic)) {
    303         ASSERT(!m_filter.contains(object));
    304         filterData->savedContext = context;
    305         m_filter.set(object, filterData.release());
    306         return false;
    307     }
    308 
    309     GraphicsContext* sourceGraphicContext = sourceGraphic->context();
    310     ASSERT(sourceGraphicContext);
    311 
    312     filterData->sourceGraphicBuffer = sourceGraphic.release();
    313     filterData->savedContext = context;
    314 
    315     context = sourceGraphicContext;
    316 
    317     ASSERT(!m_filter.contains(object));
    318     m_filter.set(object, filterData.release());
    319 
    320     return true;
    321 }
    322 
    323 void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*)
    324 {
    325     ASSERT(object);
    326     ASSERT(context);
    327     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
    328 
    329     FilterData* filterData = m_filter.get(object);
    330     if (!filterData)
    331         return;
    332 
    333     if (object->document().settings()->deferredFiltersEnabled() && (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Built)) {
    334         endDeferredFilter(context, filterData);
    335         filterData->state = FilterData::Built;
    336         return;
    337     }
    338 
    339     switch (filterData->state) {
    340     case FilterData::MarkedForRemoval:
    341         m_filter.remove(object);
    342         return;
    343 
    344     case FilterData::CycleDetected:
    345     case FilterData::Applying:
    346         // We have a cycle if we are already applying the data.
    347         // This can occur due to FeImage referencing a source that makes use of the FEImage itself.
    348         // This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack
    349         // will continue correctly.
    350         filterData->state = FilterData::PaintingSource;
    351         return;
    352 
    353     case FilterData::PaintingSource:
    354         if (!filterData->savedContext) {
    355             removeClientFromCache(object);
    356             return;
    357         }
    358 
    359         context = filterData->savedContext;
    360         filterData->savedContext = 0;
    361         break;
    362 
    363     case FilterData::Built: { } // Empty
    364     }
    365 
    366     FilterEffect* lastEffect = filterData->builder->lastEffect();
    367 
    368     if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
    369         // This is the real filtering of the object. It just needs to be called on the
    370         // initial filtering process. We just take the stored filter result on a
    371         // second drawing.
    372         if (filterData->state != FilterData::Built)
    373             filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release());
    374 
    375         // Always true if filterData is just built (filterData->state == FilterData::Built).
    376         if (!lastEffect->hasResult()) {
    377             filterData->state = FilterData::Applying;
    378             lastEffect->apply();
    379             lastEffect->correctFilterResultIfNeeded();
    380             lastEffect->transformResultColorSpace(ColorSpaceDeviceRGB);
    381         }
    382         filterData->state = FilterData::Built;
    383 
    384         ImageBuffer* resultImage = lastEffect->asImageBuffer();
    385         if (resultImage) {
    386             context->drawImageBuffer(resultImage, filterData->filter->mapAbsoluteRectToLocalRect(lastEffect->absolutePaintRect()));
    387         }
    388     }
    389     filterData->sourceGraphicBuffer.clear();
    390 }
    391 
    392 FloatRect RenderSVGResourceFilter::resourceBoundingBox(const RenderObject* object)
    393 {
    394     if (SVGFilterElement* element = toSVGFilterElement(this->element()))
    395         return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnits()->currentValue()->enumValue(), object->objectBoundingBox());
    396 
    397     return FloatRect();
    398 }
    399 
    400 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
    401 {
    402     FilterMap::iterator it = m_filter.begin();
    403     FilterMap::iterator end = m_filter.end();
    404     SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
    405 
    406     for (; it != end; ++it) {
    407         FilterData* filterData = it->value.get();
    408         if (filterData->state != FilterData::Built)
    409             continue;
    410 
    411         SVGFilterBuilder* builder = filterData->builder.get();
    412         FilterEffect* effect = builder->effectByRenderer(object);
    413         if (!effect)
    414             continue;
    415         // Since all effects shares the same attribute value, all
    416         // or none of them will be changed.
    417         if (!primitve->setFilterEffectAttribute(effect, attribute))
    418             return;
    419         builder->clearResultsRecursive(effect);
    420 
    421         // Repaint the image on the screen.
    422         markClientForInvalidation(it->key, RepaintInvalidation);
    423     }
    424     markAllClientLayersForInvalidation();
    425 }
    426 
    427 FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const
    428 {
    429     FilterData* filterData = m_filter.get(object);
    430     return filterData ? filterData->drawingRegion : FloatRect();
    431 }
    432 
    433 }
    434