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 #if ENABLE(SVG) && ENABLE(FILTERS)
     27 #include "RenderSVGResourceFilter.h"
     28 
     29 #include "AffineTransform.h"
     30 #include "FilterEffect.h"
     31 #include "FloatPoint.h"
     32 #include "FloatRect.h"
     33 #include "GraphicsContext.h"
     34 #include "Image.h"
     35 #include "ImageBuffer.h"
     36 #include "ImageData.h"
     37 #include "IntRect.h"
     38 #include "RenderSVGResource.h"
     39 #include "RenderSVGResourceFilterPrimitive.h"
     40 #include "SVGElement.h"
     41 #include "SVGFilter.h"
     42 #include "SVGFilterElement.h"
     43 #include "SVGFilterPrimitiveStandardAttributes.h"
     44 #include "SVGImageBufferTools.h"
     45 #include "SVGNames.h"
     46 #include "SVGStyledElement.h"
     47 #include "SVGUnitTypes.h"
     48 
     49 #include <wtf/UnusedParam.h>
     50 #include <wtf/Vector.h>
     51 
     52 using namespace std;
     53 
     54 namespace WebCore {
     55 
     56 RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
     57 
     58 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
     59     : RenderSVGResourceContainer(node)
     60 {
     61 }
     62 
     63 RenderSVGResourceFilter::~RenderSVGResourceFilter()
     64 {
     65     if (m_filter.isEmpty())
     66         return;
     67 
     68     deleteAllValues(m_filter);
     69     m_filter.clear();
     70 }
     71 
     72 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
     73 {
     74     if (!m_filter.isEmpty()) {
     75         deleteAllValues(m_filter);
     76         m_filter.clear();
     77     }
     78 
     79     markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
     80 }
     81 
     82 void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation)
     83 {
     84     ASSERT(client);
     85 
     86     if (FilterData* filterData = m_filter.get(client)) {
     87         if (filterData->savedContext)
     88             filterData->markedForRemoval = true;
     89         else
     90             delete m_filter.take(client);
     91     }
     92 
     93     markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
     94 }
     95 
     96 PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(Filter* filter)
     97 {
     98     SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
     99     bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
    100 
    101     // Add effects to the builder
    102     RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(filter);
    103     for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) {
    104         if (!node->isSVGElement())
    105             continue;
    106 
    107         SVGElement* element = static_cast<SVGElement*>(node);
    108         if (!element->isFilterEffect())
    109             continue;
    110 
    111         SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
    112         RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter);
    113         if (!effect) {
    114             builder->clearEffects();
    115             return 0;
    116         }
    117         builder->appendEffectToEffectReferences(effect, effectElement->renderer());
    118         effectElement->setStandardAttributes(primitiveBoundingBoxMode, effect.get());
    119         builder->add(effectElement->result(), effect);
    120     }
    121     return builder.release();
    122 }
    123 
    124 bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale)
    125 {
    126     bool matchesFilterSize = true;
    127     if (size.width() > kMaxFilterSize) {
    128         scale.setWidth(scale.width() * kMaxFilterSize / size.width());
    129         matchesFilterSize = false;
    130     }
    131     if (size.height() > kMaxFilterSize) {
    132         scale.setHeight(scale.height() * kMaxFilterSize / size.height());
    133         matchesFilterSize = false;
    134     }
    135 
    136     return matchesFilterSize;
    137 }
    138 
    139 bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
    140 {
    141     ASSERT(object);
    142     ASSERT(context);
    143 #ifndef NDEBUG
    144     ASSERT(resourceMode == ApplyToDefaultMode);
    145 #else
    146     UNUSED_PARAM(resourceMode);
    147 #endif
    148 
    149     // Returning false here, to avoid drawings onto the context. We just want to
    150     // draw the stored filter output, not the unfiltered object as well.
    151     if (m_filter.contains(object)) {
    152         FilterData* filterData = m_filter.get(object);
    153         if (filterData->builded)
    154             return false;
    155 
    156         delete m_filter.take(object); // Oops, have to rebuild, go through normal code path
    157     }
    158 
    159     OwnPtr<FilterData> filterData(new FilterData);
    160     FloatRect targetBoundingBox = object->objectBoundingBox();
    161 
    162     SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
    163     filterData->boundaries = filterElement->filterBoundingBox(targetBoundingBox);
    164     if (filterData->boundaries.isEmpty())
    165         return false;
    166 
    167     // Determine absolute transformation matrix for filter.
    168     AffineTransform absoluteTransform;
    169     SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform);
    170     if (!absoluteTransform.isInvertible())
    171         return false;
    172 
    173     // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile.
    174     filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), absoluteTransform.e(), absoluteTransform.f());
    175 
    176     // Determine absolute boundaries of the filter and the drawing region.
    177     FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries);
    178     FloatRect drawingRegion = object->strokeBoundingBox();
    179     drawingRegion.intersect(filterData->boundaries);
    180     FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(drawingRegion);
    181 
    182     // Create the SVGFilter object.
    183     bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
    184     filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
    185 
    186     // Create all relevant filter primitives.
    187     filterData->builder = buildPrimitives(filterData->filter.get());
    188     if (!filterData->builder)
    189         return false;
    190 
    191     // Calculate the scale factor for the use of filterRes.
    192     // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
    193     FloatSize scale(1, 1);
    194     if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
    195         scale.setWidth(filterElement->filterResX() / absoluteFilterBoundaries.width());
    196         scale.setHeight(filterElement->filterResY() / absoluteFilterBoundaries.height());
    197     }
    198 
    199     if (scale.isEmpty())
    200         return false;
    201 
    202     // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize.
    203     FloatRect tempSourceRect = absoluteDrawingRegion;
    204     tempSourceRect.scale(scale.width(), scale.height());
    205     fitsInMaximumImageSize(tempSourceRect.size(), scale);
    206 
    207     // Set the scale level in SVGFilter.
    208     filterData->filter->setFilterResolution(scale);
    209 
    210     FilterEffect* lastEffect = filterData->builder->lastEffect();
    211     if (!lastEffect)
    212         return false;
    213 
    214     RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect);
    215     FloatRect subRegion = lastEffect->maxEffectRect();
    216     // At least one FilterEffect has a too big image size,
    217     // recalculate the effect sizes with new scale factors.
    218     if (!fitsInMaximumImageSize(subRegion.size(), scale)) {
    219         filterData->filter->setFilterResolution(scale);
    220         RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect);
    221     }
    222 
    223     // If the drawingRegion is empty, we have something like <g filter=".."/>.
    224     // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource.
    225     if (drawingRegion.isEmpty()) {
    226         ASSERT(!m_filter.contains(object));
    227         filterData->savedContext = context;
    228         m_filter.set(object, filterData.leakPtr());
    229         return false;
    230     }
    231 
    232     absoluteDrawingRegion.scale(scale.width(), scale.height());
    233 
    234     OwnPtr<ImageBuffer> sourceGraphic;
    235     if (!SVGImageBufferTools::createImageBuffer(absoluteDrawingRegion, absoluteDrawingRegion, sourceGraphic, ColorSpaceLinearRGB)) {
    236         ASSERT(!m_filter.contains(object));
    237         filterData->savedContext = context;
    238         m_filter.set(object, filterData.leakPtr());
    239         return false;
    240     }
    241 
    242     GraphicsContext* sourceGraphicContext = sourceGraphic->context();
    243     ASSERT(sourceGraphicContext);
    244 
    245     sourceGraphicContext->translate(-absoluteDrawingRegion.x(), -absoluteDrawingRegion.y());
    246     if (scale.width() != 1 || scale.height() != 1)
    247         sourceGraphicContext->scale(scale);
    248 
    249     sourceGraphicContext->concatCTM(filterData->shearFreeAbsoluteTransform);
    250     sourceGraphicContext->clearRect(FloatRect(FloatPoint(), absoluteDrawingRegion.size()));
    251     filterData->sourceGraphicBuffer = sourceGraphic.release();
    252     filterData->savedContext = context;
    253 
    254     context = sourceGraphicContext;
    255 
    256     ASSERT(!m_filter.contains(object));
    257     m_filter.set(object, filterData.leakPtr());
    258 
    259     return true;
    260 }
    261 
    262 void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*)
    263 {
    264     ASSERT(object);
    265     ASSERT(context);
    266 #ifndef NDEBUG
    267     ASSERT(resourceMode == ApplyToDefaultMode);
    268 #else
    269     UNUSED_PARAM(resourceMode);
    270 #endif
    271 
    272     FilterData* filterData = m_filter.get(object);
    273     if (!filterData)
    274         return;
    275 
    276     if (filterData->markedForRemoval) {
    277         delete m_filter.take(object);
    278         return;
    279     }
    280 
    281     if (!filterData->builded) {
    282         if (!filterData->savedContext) {
    283             removeClientFromCache(object);
    284             return;
    285         }
    286 
    287         context = filterData->savedContext;
    288         filterData->savedContext = 0;
    289 #if !USE(CG)
    290         if (filterData->sourceGraphicBuffer)
    291             filterData->sourceGraphicBuffer->transformColorSpace(ColorSpaceDeviceRGB, ColorSpaceLinearRGB);
    292 #endif
    293     }
    294 
    295     FilterEffect* lastEffect = filterData->builder->lastEffect();
    296 
    297     if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
    298         // This is the real filtering of the object. It just needs to be called on the
    299         // initial filtering process. We just take the stored filter result on a
    300         // second drawing.
    301         if (!filterData->builded)
    302             filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release());
    303 
    304         // Always true if filterData is just built (filterData->builded is false).
    305         if (!lastEffect->hasResult()) {
    306             lastEffect->apply();
    307 #if !USE(CG)
    308             ImageBuffer* resultImage = lastEffect->asImageBuffer();
    309             if (resultImage)
    310                 resultImage->transformColorSpace(ColorSpaceLinearRGB, ColorSpaceDeviceRGB);
    311 #endif
    312         }
    313         filterData->builded = true;
    314 
    315         ImageBuffer* resultImage = lastEffect->asImageBuffer();
    316         if (resultImage) {
    317             context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse());
    318 
    319             context->scale(FloatSize(1 / filterData->filter->filterResolution().width(), 1 / filterData->filter->filterResolution().height()));
    320             context->clip(lastEffect->maxEffectRect());
    321             context->drawImageBuffer(resultImage, object->style()->colorSpace(), lastEffect->absolutePaintRect());
    322             context->scale(filterData->filter->filterResolution());
    323 
    324             context->concatCTM(filterData->shearFreeAbsoluteTransform);
    325         }
    326     }
    327     filterData->sourceGraphicBuffer.clear();
    328 }
    329 
    330 FloatRect RenderSVGResourceFilter::resourceBoundingBox(RenderObject* object)
    331 {
    332     if (SVGFilterElement* element = static_cast<SVGFilterElement*>(node()))
    333         return element->filterBoundingBox(object->objectBoundingBox());
    334 
    335     return FloatRect();
    336 }
    337 
    338 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
    339 {
    340     HashMap<RenderObject*, FilterData*>::iterator it = m_filter.begin();
    341     HashMap<RenderObject*, FilterData*>::iterator end = m_filter.end();
    342     SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
    343 
    344     for (; it != end; ++it) {
    345         FilterData* filterData = it->second;
    346         if (!filterData->builded)
    347             continue;
    348 
    349         SVGFilterBuilder* builder = filterData->builder.get();
    350         FilterEffect* effect = builder->effectByRenderer(object);
    351         if (!effect)
    352             continue;
    353         // Since all effects shares the same attribute value, all
    354         // or none of them will be changed.
    355         if (!primitve->setFilterEffectAttribute(effect, attribute))
    356             return;
    357         builder->clearResultsRecursive(effect);
    358 
    359         // Repaint the image on the screen.
    360         markClientForInvalidation(it->first, RepaintInvalidation);
    361     }
    362 }
    363 
    364 }
    365 #endif
    366