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/dom/ElementTraversal.h"
     29 #include "core/frame/Settings.h"
     30 #include "core/rendering/svg/RenderSVGResourceFilterPrimitive.h"
     31 #include "core/rendering/svg/SVGRenderingContext.h"
     32 #include "core/svg/SVGFilterPrimitiveStandardAttributes.h"
     33 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
     34 #include "platform/graphics/filters/SkiaImageFilterBuilder.h"
     35 #include "platform/graphics/filters/SourceAlpha.h"
     36 #include "platform/graphics/filters/SourceGraphic.h"
     37 
     38 namespace blink {
     39 
     40 const RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
     41 
     42 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
     43     : RenderSVGResourceContainer(node)
     44 {
     45 }
     46 
     47 RenderSVGResourceFilter::~RenderSVGResourceFilter()
     48 {
     49 }
     50 
     51 void RenderSVGResourceFilter::destroy()
     52 {
     53     m_filter.clear();
     54     RenderSVGResourceContainer::destroy();
     55 }
     56 
     57 bool RenderSVGResourceFilter::isChildAllowed(RenderObject* child, RenderStyle*) const
     58 {
     59     return child->isSVGResourceFilterPrimitive();
     60 }
     61 
     62 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
     63 {
     64     m_filter.clear();
     65     markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
     66 }
     67 
     68 void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation)
     69 {
     70     ASSERT(client);
     71 
     72     m_filter.remove(client);
     73 
     74     markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
     75 }
     76 
     77 PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* filter)
     78 {
     79     SVGFilterElement* filterElement = toSVGFilterElement(element());
     80     FloatRect targetBoundingBox = filter->targetBoundingBox();
     81 
     82     // Add effects to the builder
     83     RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(SourceGraphic::create(filter), SourceAlpha::create(filter));
     84     for (SVGElement* element = Traversal<SVGElement>::firstChild(*filterElement); element; element = Traversal<SVGElement>::nextSibling(*element)) {
     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 nullptr;
     93         }
     94         builder->appendEffectToEffectReferences(effect, effectElement->renderer());
     95         effectElement->setStandardAttributes(effect.get());
     96         effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnits()->currentValue()->enumValue(), targetBoundingBox));
     97         effect->setOperatingColorSpace(
     98             effectElement->renderer()->style()->svgStyle().colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
     99         builder->add(AtomicString(effectElement->result()->currentValue()->value()), effect);
    100     }
    101     return builder.release();
    102 }
    103 
    104 static void beginDeferredFilter(GraphicsContext* context, FilterData* filterData)
    105 {
    106     context->beginRecording(filterData->boundaries);
    107     context->setShouldSmoothFonts(false);
    108     // We pass the boundaries to SkPictureImageFilter so it knows the
    109     // world-space position of the filter primitives. It gets them
    110     // from the DisplayList, which also applies the inverse translate
    111     // to the origin. So we apply the forward translate here to avoid
    112     // it being applied twice.
    113     // FIXME: we should fix SkPicture to handle this offset itself, or
    114     // make the translate optional on SkPictureImageFilter.
    115     // See https://code.google.com/p/skia/issues/detail?id=2801
    116     context->translate(filterData->boundaries.x(), filterData->boundaries.y());
    117 }
    118 
    119 static void endDeferredFilter(GraphicsContext* context, FilterData* filterData)
    120 {
    121     // FIXME: maybe filterData should just hold onto SourceGraphic after creation?
    122     SourceGraphic* sourceGraphic = static_cast<SourceGraphic*>(filterData->builder->getEffectById(SourceGraphic::effectName()));
    123     ASSERT(sourceGraphic);
    124     sourceGraphic->setDisplayList(context->endRecording());
    125 }
    126 
    127 static void drawDeferredFilter(GraphicsContext* context, FilterData* filterData, SVGFilterElement* filterElement)
    128 {
    129     SkiaImageFilterBuilder builder(context);
    130     SourceGraphic* sourceGraphic = static_cast<SourceGraphic*>(filterData->builder->getEffectById(SourceGraphic::effectName()));
    131     ASSERT(sourceGraphic);
    132     builder.setSourceGraphic(sourceGraphic);
    133     RefPtr<ImageFilter> imageFilter = builder.build(filterData->builder->lastEffect(), ColorSpaceDeviceRGB);
    134     FloatRect boundaries = filterData->boundaries;
    135     context->save();
    136 
    137     FloatSize deviceSize = context->getCTM().mapSize(boundaries.size());
    138     float scaledArea = deviceSize.width() * deviceSize.height();
    139 
    140     // If area of scaled size is bigger than the upper limit, adjust the scale
    141     // to fit. Note that this only really matters in the non-impl-side painting
    142     // case, since the impl-side case never allocates a full-sized backing
    143     // store, only tile-sized.
    144     // FIXME: remove this once all platforms are using impl-side painting.
    145     // crbug.com/169282.
    146     if (scaledArea > FilterEffect::maxFilterArea()) {
    147         float scale = sqrtf(FilterEffect::maxFilterArea() / scaledArea);
    148         context->scale(scale, scale);
    149     }
    150     // Clip drawing of filtered image to the minimum required paint rect.
    151     FilterEffect* lastEffect = filterData->builder->lastEffect();
    152     context->clipRect(lastEffect->determineAbsolutePaintRect(lastEffect->maxEffectRect()));
    153     if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
    154         // Get boundaries in device coords.
    155         // FIXME: See crbug.com/382491. Is the use of getCTM OK here, given it does not include device
    156         // zoom or High DPI adjustments?
    157         FloatSize size = context->getCTM().mapSize(boundaries.size());
    158         // Compute the scale amount required so that the resulting offscreen is exactly filterResX by filterResY pixels.
    159         float filterResScaleX = filterElement->filterResX()->currentValue()->value() / size.width();
    160         float filterResScaleY = filterElement->filterResY()->currentValue()->value() / size.height();
    161         // Scale the CTM so the primitive is drawn to filterRes.
    162         context->scale(filterResScaleX, filterResScaleY);
    163         // Create a resize filter with the inverse scale.
    164         AffineTransform resizeMatrix;
    165         resizeMatrix.scale(1 / filterResScaleX, 1 / filterResScaleY);
    166         imageFilter = builder.buildTransform(resizeMatrix, imageFilter.get());
    167     }
    168     // If the CTM contains rotation or shearing, apply the filter to
    169     // the unsheared/unrotated matrix, and do the shearing/rotation
    170     // as a final pass.
    171     AffineTransform ctm = context->getCTM();
    172     if (ctm.b() || ctm.c()) {
    173         AffineTransform scaleAndTranslate;
    174         scaleAndTranslate.translate(ctm.e(), ctm.f());
    175         scaleAndTranslate.scale(ctm.xScale(), ctm.yScale());
    176         ASSERT(scaleAndTranslate.isInvertible());
    177         AffineTransform shearAndRotate = scaleAndTranslate.inverse();
    178         shearAndRotate.multiply(ctm);
    179         context->setCTM(scaleAndTranslate);
    180         imageFilter = builder.buildTransform(shearAndRotate, imageFilter.get());
    181     }
    182     context->beginLayer(1, CompositeSourceOver, &boundaries, ColorFilterNone, imageFilter.get());
    183     context->endLayer();
    184     context->restore();
    185 }
    186 
    187 bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
    188 {
    189     ASSERT(object);
    190     ASSERT(context);
    191     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
    192 
    193     clearInvalidationMask();
    194 
    195     if (m_filter.contains(object)) {
    196         FilterData* filterData = m_filter.get(object);
    197         if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying)
    198             filterData->state = FilterData::CycleDetected;
    199         return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now.
    200     }
    201 
    202     OwnPtr<FilterData> filterData(adoptPtr(new FilterData));
    203     FloatRect targetBoundingBox = object->objectBoundingBox();
    204 
    205     SVGFilterElement* filterElement = toSVGFilterElement(element());
    206     filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits()->currentValue()->enumValue(), targetBoundingBox);
    207     if (filterData->boundaries.isEmpty())
    208         return false;
    209 
    210     filterData->drawingRegion = object->strokeBoundingBox();
    211     filterData->drawingRegion.intersect(filterData->boundaries);
    212     IntRect intDrawingRegion = enclosingIntRect(filterData->drawingRegion);
    213 
    214     // Create the SVGFilter object.
    215     bool primitiveBoundingBoxMode = filterElement->primitiveUnits()->currentValue()->enumValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
    216     filterData->filter = SVGFilter::create(intDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
    217 
    218     // Create all relevant filter primitives.
    219     filterData->builder = buildPrimitives(filterData->filter.get());
    220     if (!filterData->builder)
    221         return false;
    222 
    223     FilterEffect* lastEffect = filterData->builder->lastEffect();
    224     if (!lastEffect)
    225         return false;
    226 
    227     lastEffect->determineFilterPrimitiveSubregion(ClipToFilterRegion);
    228 
    229     FilterData* data = filterData.get();
    230     m_filter.set(object, filterData.release());
    231     beginDeferredFilter(context, data);
    232     return true;
    233 }
    234 
    235 void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context)
    236 {
    237     ASSERT(object);
    238     ASSERT(context);
    239 
    240     FilterData* filterData = m_filter.get(object);
    241     if (!filterData)
    242         return;
    243 
    244     switch (filterData->state) {
    245     case FilterData::CycleDetected:
    246     case FilterData::Applying:
    247         // We have a cycle if we are already applying the data.
    248         // This can occur due to FeImage referencing a source that makes use of the FEImage itself.
    249         // This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack
    250         // will continue correctly.
    251         filterData->state = FilterData::PaintingSource;
    252         return;
    253 
    254     case FilterData::PaintingSource:
    255         endDeferredFilter(context, filterData);
    256         break;
    257 
    258     case FilterData::Built: { } // Empty
    259     }
    260 
    261     drawDeferredFilter(context, filterData, toSVGFilterElement(element()));
    262     filterData->state = FilterData::Built;
    263 }
    264 
    265 FloatRect RenderSVGResourceFilter::resourceBoundingBox(const RenderObject* object)
    266 {
    267     if (SVGFilterElement* element = toSVGFilterElement(this->element()))
    268         return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnits()->currentValue()->enumValue(), object->objectBoundingBox());
    269 
    270     return FloatRect();
    271 }
    272 
    273 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
    274 {
    275     FilterMap::iterator it = m_filter.begin();
    276     FilterMap::iterator end = m_filter.end();
    277     SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
    278 
    279     for (; it != end; ++it) {
    280         FilterData* filterData = it->value.get();
    281         if (filterData->state != FilterData::Built)
    282             continue;
    283 
    284         SVGFilterBuilder* builder = filterData->builder.get();
    285         FilterEffect* effect = builder->effectByRenderer(object);
    286         if (!effect)
    287             continue;
    288         // Since all effects shares the same attribute value, all
    289         // or none of them will be changed.
    290         if (!primitve->setFilterEffectAttribute(effect, attribute))
    291             return;
    292         builder->clearResultsRecursive(effect);
    293 
    294         // Issue paint invalidations for the image on the screen.
    295         markClientForInvalidation(it->key, PaintInvalidation);
    296     }
    297     markAllClientLayersForInvalidation();
    298 }
    299 
    300 FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const
    301 {
    302     FilterData* filterData = m_filter.get(object);
    303     return filterData ? filterData->drawingRegion : FloatRect();
    304 }
    305 
    306 }
    307