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/SourceAlpha.h"
     34 #include "platform/graphics/filters/SourceGraphic.h"
     35 #include "platform/graphics/gpu/AcceleratedImageBufferSurface.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 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
     54 {
     55     m_filter.clear();
     56     markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
     57 }
     58 
     59 void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation)
     60 {
     61     ASSERT(client);
     62 
     63     if (FilterData* filterData = m_filter.get(client)) {
     64         if (filterData->savedContext)
     65             filterData->state = FilterData::MarkedForRemoval;
     66         else
     67             m_filter.remove(client);
     68     }
     69 
     70     markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
     71 }
     72 
     73 PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* filter)
     74 {
     75     SVGFilterElement* filterElement = toSVGFilterElement(element());
     76     FloatRect targetBoundingBox = filter->targetBoundingBox();
     77 
     78     // Add effects to the builder
     79     RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(SourceGraphic::create(filter), SourceAlpha::create(filter));
     80     for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) {
     81         if (!node->isSVGElement())
     82             continue;
     83 
     84         SVGElement* element = toSVGElement(node);
     85         if (!element->isFilterEffect() || !element->renderer())
     86             continue;
     87 
     88         SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
     89         RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter);
     90         if (!effect) {
     91             builder->clearEffects();
     92             return 0;
     93         }
     94         builder->appendEffectToEffectReferences(effect, effectElement->renderer());
     95         effectElement->setStandardAttributes(effect.get());
     96         effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnitsCurrentValue(), targetBoundingBox));
     97         effect->setOperatingColorSpace(
     98             effectElement->renderer()->style()->svgStyle()->colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
     99         builder->add(effectElement->resultCurrentValue(), effect);
    100     }
    101     return builder.release();
    102 }
    103 
    104 bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale)
    105 {
    106     bool matchesFilterSize = true;
    107     if (size.width() > kMaxFilterSize) {
    108         scale.setWidth(scale.width() * kMaxFilterSize / size.width());
    109         matchesFilterSize = false;
    110     }
    111     if (size.height() > kMaxFilterSize) {
    112         scale.setHeight(scale.height() * kMaxFilterSize / size.height());
    113         matchesFilterSize = false;
    114     }
    115 
    116     return matchesFilterSize;
    117 }
    118 
    119 static bool createImageBuffer(const FloatRect& targetRect, const AffineTransform& absoluteTransform,
    120     OwnPtr<ImageBuffer>& imageBuffer, bool accelerated)
    121 {
    122     IntRect paintRect = SVGRenderingContext::calculateImageBufferRect(targetRect, absoluteTransform);
    123     // Don't create empty ImageBuffers.
    124     if (paintRect.isEmpty())
    125         return false;
    126 
    127     OwnPtr<ImageBufferSurface> surface;
    128     if (accelerated)
    129         surface = adoptPtr(new AcceleratedImageBufferSurface(paintRect.size()));
    130     if (!accelerated || !surface->isValid())
    131         surface = adoptPtr(new UnacceleratedImageBufferSurface(paintRect.size()));
    132     if (!surface->isValid())
    133         return false;
    134     OwnPtr<ImageBuffer> image = ImageBuffer::create(surface.release());
    135 
    136     GraphicsContext* imageContext = image->context();
    137     ASSERT(imageContext);
    138 
    139     imageContext->translate(-paintRect.x(), -paintRect.y());
    140     imageContext->concatCTM(absoluteTransform);
    141 
    142     imageBuffer = image.release();
    143     return true;
    144 }
    145 
    146 bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
    147 {
    148     ASSERT(object);
    149     ASSERT(context);
    150     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
    151 
    152     clearInvalidationMask();
    153 
    154     if (m_filter.contains(object)) {
    155         FilterData* filterData = m_filter.get(object);
    156         if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying)
    157             filterData->state = FilterData::CycleDetected;
    158         return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now.
    159     }
    160 
    161     OwnPtr<FilterData> filterData(adoptPtr(new FilterData));
    162     FloatRect targetBoundingBox = object->objectBoundingBox();
    163 
    164     SVGFilterElement* filterElement = toSVGFilterElement(element());
    165     filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnitsCurrentValue(), targetBoundingBox);
    166     if (filterData->boundaries.isEmpty())
    167         return false;
    168 
    169     // Determine absolute transformation matrix for filter.
    170     AffineTransform absoluteTransform;
    171     SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransform);
    172     if (!absoluteTransform.isInvertible())
    173         return false;
    174 
    175     // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile.
    176     filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), 0, 0);
    177 
    178     // Determine absolute boundaries of the filter and the drawing region.
    179     FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries);
    180     filterData->drawingRegion = object->strokeBoundingBox();
    181     filterData->drawingRegion.intersect(filterData->boundaries);
    182     FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(filterData->drawingRegion);
    183 
    184     // Create the SVGFilter object.
    185     bool primitiveBoundingBoxMode = filterElement->primitiveUnitsCurrentValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
    186     filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
    187 
    188     // Create all relevant filter primitives.
    189     filterData->builder = buildPrimitives(filterData->filter.get());
    190     if (!filterData->builder)
    191         return false;
    192 
    193     // Calculate the scale factor for the use of filterRes.
    194     // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
    195     FloatSize scale(1, 1);
    196     if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
    197         scale.setWidth(filterElement->filterResXCurrentValue() / absoluteFilterBoundaries.width());
    198         scale.setHeight(filterElement->filterResYCurrentValue() / absoluteFilterBoundaries.height());
    199     }
    200 
    201     if (scale.isEmpty())
    202         return false;
    203 
    204     // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize.
    205     FloatRect tempSourceRect = absoluteDrawingRegion;
    206     tempSourceRect.scale(scale.width(), scale.height());
    207     fitsInMaximumImageSize(tempSourceRect.size(), scale);
    208 
    209     // Set the scale level in SVGFilter.
    210     filterData->filter->setFilterResolution(scale);
    211 
    212     FilterEffect* lastEffect = filterData->builder->lastEffect();
    213     if (!lastEffect)
    214         return false;
    215 
    216     lastEffect->determineFilterPrimitiveSubregion(ClipToFilterRegion);
    217     FloatRect subRegion = lastEffect->maxEffectRect();
    218     // At least one FilterEffect has a too big image size,
    219     // recalculate the effect sizes with new scale factors.
    220     if (!fitsInMaximumImageSize(subRegion.size(), scale)) {
    221         filterData->filter->setFilterResolution(scale);
    222         lastEffect->determineFilterPrimitiveSubregion(ClipToFilterRegion);
    223     }
    224 
    225     // If the drawingRegion is empty, we have something like <g filter=".."/>.
    226     // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource.
    227     if (filterData->drawingRegion.isEmpty()) {
    228         ASSERT(!m_filter.contains(object));
    229         filterData->savedContext = context;
    230         m_filter.set(object, filterData.release());
    231         return false;
    232     }
    233 
    234     // Change the coordinate transformation applied to the filtered element to reflect the resolution of the filter.
    235     AffineTransform effectiveTransform;
    236     effectiveTransform.scale(scale.width(), scale.height());
    237     effectiveTransform.multiply(filterData->shearFreeAbsoluteTransform);
    238 
    239     OwnPtr<ImageBuffer> sourceGraphic;
    240     bool isAccelerated = object->document().settings()->acceleratedFiltersEnabled();
    241     if (!createImageBuffer(filterData->drawingRegion, effectiveTransform, sourceGraphic, isAccelerated)) {
    242         ASSERT(!m_filter.contains(object));
    243         filterData->savedContext = context;
    244         m_filter.set(object, filterData.release());
    245         return false;
    246     }
    247 
    248     // Set the rendering mode from the page's settings.
    249     filterData->filter->setIsAccelerated(isAccelerated);
    250 
    251     GraphicsContext* sourceGraphicContext = sourceGraphic->context();
    252     ASSERT(sourceGraphicContext);
    253 
    254     filterData->sourceGraphicBuffer = sourceGraphic.release();
    255     filterData->savedContext = context;
    256 
    257     context = sourceGraphicContext;
    258 
    259     ASSERT(!m_filter.contains(object));
    260     m_filter.set(object, filterData.release());
    261 
    262     return true;
    263 }
    264 
    265 void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*)
    266 {
    267     ASSERT(object);
    268     ASSERT(context);
    269     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
    270 
    271     FilterData* filterData = m_filter.get(object);
    272     if (!filterData)
    273         return;
    274 
    275     switch (filterData->state) {
    276     case FilterData::MarkedForRemoval:
    277         m_filter.remove(object);
    278         return;
    279 
    280     case FilterData::CycleDetected:
    281     case FilterData::Applying:
    282         // We have a cycle if we are already applying the data.
    283         // This can occur due to FeImage referencing a source that makes use of the FEImage itself.
    284         // This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack
    285         // will continue correctly.
    286         filterData->state = FilterData::PaintingSource;
    287         return;
    288 
    289     case FilterData::PaintingSource:
    290         if (!filterData->savedContext) {
    291             removeClientFromCache(object);
    292             return;
    293         }
    294 
    295         context = filterData->savedContext;
    296         filterData->savedContext = 0;
    297         break;
    298 
    299     case FilterData::Built: { } // Empty
    300     }
    301 
    302     FilterEffect* lastEffect = filterData->builder->lastEffect();
    303 
    304     if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
    305         // This is the real filtering of the object. It just needs to be called on the
    306         // initial filtering process. We just take the stored filter result on a
    307         // second drawing.
    308         if (filterData->state != FilterData::Built)
    309             filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release());
    310 
    311         // Always true if filterData is just built (filterData->state == FilterData::Built).
    312         if (!lastEffect->hasResult()) {
    313             filterData->state = FilterData::Applying;
    314             lastEffect->apply();
    315             lastEffect->correctFilterResultIfNeeded();
    316             lastEffect->transformResultColorSpace(ColorSpaceDeviceRGB);
    317         }
    318         filterData->state = FilterData::Built;
    319 
    320         ImageBuffer* resultImage = lastEffect->asImageBuffer();
    321         if (resultImage) {
    322             context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse());
    323 
    324             context->scale(FloatSize(1 / filterData->filter->filterResolution().width(), 1 / filterData->filter->filterResolution().height()));
    325             context->drawImageBuffer(resultImage, lastEffect->absolutePaintRect());
    326             context->scale(filterData->filter->filterResolution());
    327 
    328             context->concatCTM(filterData->shearFreeAbsoluteTransform);
    329         }
    330     }
    331     filterData->sourceGraphicBuffer.clear();
    332 }
    333 
    334 FloatRect RenderSVGResourceFilter::resourceBoundingBox(RenderObject* object)
    335 {
    336     if (SVGFilterElement* element = toSVGFilterElement(this->element()))
    337         return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnitsCurrentValue(), object->objectBoundingBox());
    338 
    339     return FloatRect();
    340 }
    341 
    342 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
    343 {
    344     FilterMap::iterator it = m_filter.begin();
    345     FilterMap::iterator end = m_filter.end();
    346     SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
    347 
    348     for (; it != end; ++it) {
    349         FilterData* filterData = it->value.get();
    350         if (filterData->state != FilterData::Built)
    351             continue;
    352 
    353         SVGFilterBuilder* builder = filterData->builder.get();
    354         FilterEffect* effect = builder->effectByRenderer(object);
    355         if (!effect)
    356             continue;
    357         // Since all effects shares the same attribute value, all
    358         // or none of them will be changed.
    359         if (!primitve->setFilterEffectAttribute(effect, attribute))
    360             return;
    361         builder->clearResultsRecursive(effect);
    362 
    363         // Repaint the image on the screen.
    364         markClientForInvalidation(it->key, RepaintInvalidation);
    365     }
    366     markAllClientLayersForInvalidation();
    367 }
    368 
    369 FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const
    370 {
    371     FilterData* filterData = m_filter.get(object);
    372     return filterData ? filterData->drawingRegion : FloatRect();
    373 }
    374 
    375 }
    376