Add WTF::move()
[WebKit-https.git] / Source / WebCore / rendering / svg / RenderSVGResourceFilter.cpp
index ee6a49d..cde1fc4 100644 (file)
 
 #include "config.h"
 
-#if ENABLE(SVG) && ENABLE(FILTERS)
+#if ENABLE(FILTERS)
 #include "RenderSVGResourceFilter.h"
 
-#include "AffineTransform.h"
+#include "ElementChildIterator.h"
 #include "FilterEffect.h"
 #include "FloatPoint.h"
-#include "FloatRect.h"
+#include "Frame.h"
 #include "GraphicsContext.h"
 #include "Image.h"
-#include "ImageBuffer.h"
 #include "ImageData.h"
 #include "IntRect.h"
 #include "Page.h"
-#include "RenderSVGResource.h"
 #include "RenderSVGResourceFilterPrimitive.h"
-#include "SVGElement.h"
-#include "SVGFilter.h"
-#include "SVGFilterElement.h"
+#include "RenderView.h"
 #include "SVGFilterPrimitiveStandardAttributes.h"
 #include "SVGNames.h"
 #include "SVGRenderingContext.h"
-#include "SVGStyledElement.h"
-#include "SVGUnitTypes.h"
 #include "Settings.h"
 #include "SourceAlpha.h"
 #include "SourceGraphic.h"
 
-#include <wtf/UnusedParam.h>
-#include <wtf/Vector.h>
-
-using namespace std;
-
 namespace WebCore {
 
 RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
 
-RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
-    : RenderSVGResourceContainer(node)
+RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement& element, PassRef<RenderStyle> style)
+    : RenderSVGResourceContainer(element, WTF::move(style))
 {
 }
 
 RenderSVGResourceFilter::~RenderSVGResourceFilter()
 {
-    if (m_filter.isEmpty())
-        return;
-
-    deleteAllValues(m_filter);
-    m_filter.clear();
 }
 
 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
 {
-    if (!m_filter.isEmpty()) {
-        deleteAllValues(m_filter);
-        m_filter.clear();
-    }
+    m_filter.clear();
 
     markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
 }
 
-void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation)
+void RenderSVGResourceFilter::removeClientFromCache(RenderElement& client, bool markForInvalidation)
 {
-    ASSERT(client);
-
-    if (FilterData* filterData = m_filter.get(client)) {
+    if (FilterData* filterData = m_filter.get(&client)) {
         if (filterData->savedContext)
-            filterData->markedForRemoval = true;
+            filterData->state = FilterData::MarkedForRemoval;
         else
-            delete m_filter.take(client);
+            m_filter.remove(&client);
     }
 
     markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
 }
 
-PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* filter)
+std::unique_ptr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* filter) const
 {
-    SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
     FloatRect targetBoundingBox = filter->targetBoundingBox();
 
     // Add effects to the builder
-    RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(SourceGraphic::create(filter), SourceAlpha::create(filter));
-    for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) {
-        if (!node->isSVGElement())
-            continue;
-
-        SVGElement* element = static_cast<SVGElement*>(node);
-        if (!element->isFilterEffect())
-            continue;
-
-        SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
-        RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter);
+    auto builder = std::make_unique<SVGFilterBuilder>(SourceGraphic::create(filter), SourceAlpha::create(filter));
+    for (auto& element : childrenOfType<SVGFilterPrimitiveStandardAttributes>(filterElement())) {
+        RefPtr<FilterEffect> effect = element.build(builder.get(), filter);
         if (!effect) {
             builder->clearEffects();
-            return 0;
+            return nullptr;
         }
-        builder->appendEffectToEffectReferences(effect, effectElement->renderer());
-        effectElement->setStandardAttributes(effect.get());
-        effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnits(), targetBoundingBox));
-        effect->setColorSpace(effectElement->renderer()->style()->svgStyle()->colorInterpolationFilters() == CI_LINEARRGB
-                ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
-        builder->add(effectElement->result(), effect);
+        builder->appendEffectToEffectReferences(effect, element.renderer());
+        element.setStandardAttributes(effect.get());
+        effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(&element, filterElement().primitiveUnits(), targetBoundingBox));
+        effect->setOperatingColorSpace(element.renderer()->style().svgStyle().colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
+        builder->add(element.result(), effect.release());
     }
-    return builder.release();
+    return builder;
 }
 
 bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale)
 {
-    bool matchesFilterSize = true;
-    if (size.width() > kMaxFilterSize) {
-        scale.setWidth(scale.width() * kMaxFilterSize / size.width());
-        matchesFilterSize = false;
-    }
-    if (size.height() > kMaxFilterSize) {
-        scale.setHeight(scale.height() * kMaxFilterSize / size.height());
-        matchesFilterSize = false;
-    }
+    FloatSize scaledSize(size);
+    scaledSize.scale(scale.width(), scale.height());
+    float scaledArea = scaledSize.width() * scaledSize.height();
 
-    return matchesFilterSize;
+    if (scaledArea <= FilterEffect::maxFilterArea())
+        return true;
+
+    // If area of scaled size is bigger than the upper limit, adjust the scale
+    // to fit.
+    scale.scale(sqrt(FilterEffect::maxFilterArea() / scaledArea));
+    return false;
 }
 
-bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
+bool RenderSVGResourceFilter::applyResource(RenderElement& renderer, const RenderStyle&, GraphicsContext*& context, unsigned short resourceMode)
 {
-    ASSERT(object);
     ASSERT(context);
     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
 
-    // Returning false here, to avoid drawings onto the context. We just want to
-    // draw the stored filter output, not the unfiltered object as well.
-    if (m_filter.contains(object)) {
-        FilterData* filterData = m_filter.get(object);
-        if (filterData->builded)
-            return false;
-
-        delete m_filter.take(object); // Oops, have to rebuild, go through normal code path
+    if (m_filter.contains(&renderer)) {
+        FilterData* filterData = m_filter.get(&renderer);
+        if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying)
+            filterData->state = FilterData::CycleDetected;
+        return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now.
     }
 
-    OwnPtr<FilterData> filterData(adoptPtr(new FilterData));
-    FloatRect targetBoundingBox = object->objectBoundingBox();
+    auto filterData = std::make_unique<FilterData>();
+    FloatRect targetBoundingBox = renderer.objectBoundingBox();
 
-    SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
-    filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits(), targetBoundingBox);
+    filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(&filterElement(), filterElement().filterUnits(), targetBoundingBox);
     if (filterData->boundaries.isEmpty())
         return false;
 
     // Determine absolute transformation matrix for filter. 
     AffineTransform absoluteTransform;
-    SVGRenderingContext::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform);
+    SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(renderer, absoluteTransform);
     if (!absoluteTransform.isInvertible())
         return false;
 
@@ -178,12 +142,12 @@ bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*,
 
     // Determine absolute boundaries of the filter and the drawing region.
     FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries);
-    FloatRect drawingRegion = object->strokeBoundingBox();
-    drawingRegion.intersect(filterData->boundaries);
-    FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(drawingRegion);
+    filterData->drawingRegion = renderer.strokeBoundingBox();
+    filterData->drawingRegion.intersect(filterData->boundaries);
+    FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(filterData->drawingRegion);
 
     // Create the SVGFilter object.
-    bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
+    bool primitiveBoundingBoxMode = filterElement().primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
     filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
 
     // Create all relevant filter primitives.
@@ -194,9 +158,9 @@ bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*,
     // Calculate the scale factor for the use of filterRes.
     // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
     FloatSize scale(1, 1);
-    if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
-        scale.setWidth(filterElement->filterResX() / absoluteFilterBoundaries.width());
-        scale.setHeight(filterElement->filterResY() / absoluteFilterBoundaries.height());
+    if (filterElement().hasAttribute(SVGNames::filterResAttr)) {
+        scale.setWidth(filterElement().filterResX() / absoluteFilterBoundaries.width());
+        scale.setHeight(filterElement().filterResY() / absoluteFilterBoundaries.height());
     }
 
     if (scale.isEmpty())
@@ -225,10 +189,10 @@ bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*,
 
     // If the drawingRegion is empty, we have something like <g filter=".."/>.
     // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource.
-    if (drawingRegion.isEmpty()) {
-        ASSERT(!m_filter.contains(object));
+    if (filterData->drawingRegion.isEmpty()) {
+        ASSERT(!m_filter.contains(&renderer));
         filterData->savedContext = context;
-        m_filter.set(object, filterData.leakPtr());
+        m_filter.set(&renderer, WTF::move(filterData));
         return false;
     }
 
@@ -237,12 +201,12 @@ bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*,
     effectiveTransform.scale(scale.width(), scale.height());
     effectiveTransform.multiply(filterData->shearFreeAbsoluteTransform);
 
-    OwnPtr<ImageBuffer> sourceGraphic;
-    RenderingMode renderingMode = object->document()->page()->settings()->acceleratedFiltersEnabled() ? Accelerated : Unaccelerated;
-    if (!SVGRenderingContext::createImageBuffer(drawingRegion, effectiveTransform, sourceGraphic, ColorSpaceLinearRGB, renderingMode)) {
-        ASSERT(!m_filter.contains(object));
+    std::unique_ptr<ImageBuffer> sourceGraphic;
+    RenderingMode renderingMode = renderer.frame().settings().acceleratedFiltersEnabled() ? Accelerated : Unaccelerated;
+    if (!SVGRenderingContext::createImageBuffer(filterData->drawingRegion, effectiveTransform, sourceGraphic, ColorSpaceLinearRGB, renderingMode)) {
+        ASSERT(!m_filter.contains(&renderer));
         filterData->savedContext = context;
-        m_filter.set(object, filterData.leakPtr());
+        m_filter.set(&renderer, WTF::move(filterData));
         return false;
     }
     
@@ -252,40 +216,51 @@ bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*,
     GraphicsContext* sourceGraphicContext = sourceGraphic->context();
     ASSERT(sourceGraphicContext);
   
-    filterData->sourceGraphicBuffer = sourceGraphic.release();
+    filterData->sourceGraphicBuffer = WTF::move(sourceGraphic);
     filterData->savedContext = context;
 
     context = sourceGraphicContext;
 
-    ASSERT(!m_filter.contains(object));
-    m_filter.set(object, filterData.leakPtr());
+    ASSERT(!m_filter.contains(&renderer));
+    m_filter.set(&renderer, WTF::move(filterData));
 
     return true;
 }
 
-void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*)
+void RenderSVGResourceFilter::postApplyResource(RenderElement& renderer, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*)
 {
-    ASSERT(object);
     ASSERT(context);
     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
 
-    FilterData* filterData = m_filter.get(object);
+    FilterData* filterData = m_filter.get(&renderer);
     if (!filterData)
         return;
 
-    if (filterData->markedForRemoval) {
-        delete m_filter.take(object);
+    switch (filterData->state) {
+    case FilterData::MarkedForRemoval:
+        m_filter.remove(&renderer);
         return;
-    }
 
-    if (!filterData->builded) {
+    case FilterData::CycleDetected:
+    case FilterData::Applying:
+        // We have a cycle if we are already applying the data.
+        // This can occur due to FeImage referencing a source that makes use of the FEImage itself.
+        // This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack
+        // will continue correctly.
+        filterData->state = FilterData::PaintingSource;
+        return;
+
+    case FilterData::PaintingSource:
         if (!filterData->savedContext) {
-            removeClientFromCache(object);
+            removeClientFromCache(renderer);
             return;
         }
 
         context = filterData->savedContext;
         filterData->savedContext = 0;
+        break;
+
+    case FilterData::Built: { } // Empty
     }
 
     FilterEffect* lastEffect = filterData->builder->lastEffect();
@@ -294,52 +269,44 @@ void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsCo
         // This is the real filtering of the object. It just needs to be called on the
         // initial filtering process. We just take the stored filter result on a
         // second drawing.
-        if (!filterData->builded)
-            filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release());
+        if (filterData->state != FilterData::Built)
+            filterData->filter->setSourceImage(WTF::move(filterData->sourceGraphicBuffer));
 
-        // Always true if filterData is just built (filterData->builded is false).
+        // Always true if filterData is just built (filterData->state == FilterData::Built).
         if (!lastEffect->hasResult()) {
-            lastEffect->apply();
+            filterData->state = FilterData::Applying;
+            lastEffect->applyAll();
             lastEffect->correctFilterResultIfNeeded();
-#if !USE(CG)
-            ImageBuffer* resultImage = lastEffect->asImageBuffer();
-            if (resultImage)
-                resultImage->transformColorSpace(lastEffect->colorSpace(), ColorSpaceDeviceRGB);
-#endif
+            lastEffect->transformResultColorSpace(ColorSpaceDeviceRGB);
         }
-        filterData->builded = true;
+        filterData->state = FilterData::Built;
 
         ImageBuffer* resultImage = lastEffect->asImageBuffer();
         if (resultImage) {
             context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse());
 
             context->scale(FloatSize(1 / filterData->filter->filterResolution().width(), 1 / filterData->filter->filterResolution().height()));
-            context->drawImageBuffer(resultImage, object->style()->colorSpace(), lastEffect->absolutePaintRect());
+            context->drawImageBuffer(resultImage, renderer.style().colorSpace(), lastEffect->absolutePaintRect());
             context->scale(filterData->filter->filterResolution());
 
             context->concatCTM(filterData->shearFreeAbsoluteTransform);
         }
     }
-    filterData->sourceGraphicBuffer.clear();
+    filterData->sourceGraphicBuffer.reset();
 }
 
-FloatRect RenderSVGResourceFilter::resourceBoundingBox(RenderObject* object)
+FloatRect RenderSVGResourceFilter::resourceBoundingBox(const RenderObject& object)
 {
-    if (SVGFilterElement* element = static_cast<SVGFilterElement*>(node()))
-        return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnits(), object->objectBoundingBox());
-
-    return FloatRect();
+    return SVGLengthContext::resolveRectangle<SVGFilterElement>(&filterElement(), filterElement().filterUnits(), object.objectBoundingBox());
 }
 
 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
 {
-    HashMap<RenderObject*, FilterData*>::iterator it = m_filter.begin();
-    HashMap<RenderObject*, FilterData*>::iterator end = m_filter.end();
     SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
 
-    for (; it != end; ++it) {
-        FilterData* filterData = it->second;
-        if (!filterData->builded)
+    for (const auto& objectFilterDataPair : m_filter) {
+        const auto& filterData = objectFilterDataPair.value;
+        if (filterData->state != FilterData::Built)
             continue;
 
         SVGFilterBuilder* builder = filterData->builder.get();
@@ -353,8 +320,15 @@ void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, co
         builder->clearResultsRecursive(effect);
 
         // Repaint the image on the screen.
-        markClientForInvalidation(it->first, RepaintInvalidation);
+        markClientForInvalidation(*objectFilterDataPair.key, RepaintInvalidation);
     }
+    markAllClientLayersForInvalidation();
+}
+
+FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const
+{
+    FilterData* filterData = m_filter.get(object);
+    return filterData ? filterData->drawingRegion : FloatRect();
 }
 
 }