Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / svg / RenderSVGResourceFilter.cpp
index 08bb896..920a002 100644 (file)
  */
 
 #include "config.h"
-
 #include "core/rendering/svg/RenderSVGResourceFilter.h"
 
-#include "core/frame/Settings.h"
-#include "core/rendering/svg/RenderSVGResourceFilterPrimitive.h"
-#include "core/rendering/svg/SVGRenderingContext.h"
+#include "core/dom/ElementTraversal.h"
 #include "core/svg/SVGFilterPrimitiveStandardAttributes.h"
-#include "platform/graphics/UnacceleratedImageBufferSurface.h"
+#include "platform/graphics/GraphicsContext.h"
 #include "platform/graphics/filters/SkiaImageFilterBuilder.h"
 #include "platform/graphics/filters/SourceAlpha.h"
 #include "platform/graphics/filters/SourceGraphic.h"
-#include "platform/graphics/gpu/AcceleratedImageBufferSurface.h"
-
-using namespace std;
 
-namespace WebCore {
-
-const RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
+namespace blink {
 
 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
     : RenderSVGResourceContainer(node)
@@ -48,7 +40,12 @@ RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
 
 RenderSVGResourceFilter::~RenderSVGResourceFilter()
 {
+}
+
+void RenderSVGResourceFilter::destroy()
+{
     m_filter.clear();
+    RenderSVGResourceContainer::destroy();
 }
 
 bool RenderSVGResourceFilter::isChildAllowed(RenderObject* child, RenderStyle*) const
@@ -66,12 +63,7 @@ void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool m
 {
     ASSERT(client);
 
-    if (FilterData* filterData = m_filter.get(client)) {
-        if (filterData->savedContext)
-            filterData->state = FilterData::MarkedForRemoval;
-        else
-            m_filter.remove(client);
-    }
+    m_filter.remove(client);
 
     markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
 }
@@ -83,11 +75,7 @@ PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter*
 
     // 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 = toSVGElement(node);
+    for (SVGElement* element = Traversal<SVGElement>::firstChild(*filterElement); element; element = Traversal<SVGElement>::nextSibling(*element)) {
         if (!element->isFilterEffect() || !element->renderer())
             continue;
 
@@ -95,123 +83,130 @@ PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter*
         RefPtr<FilterEffect> effect = effectElement->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->primitiveUnitsCurrentValue(), targetBoundingBox));
+        effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnits()->currentValue()->enumValue(), targetBoundingBox));
         effect->setOperatingColorSpace(
-            effectElement->renderer()->style()->svgStyle()->colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
+            effectElement->renderer()->style()->svgStyle().colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
         builder->add(AtomicString(effectElement->result()->currentValue()->value()), effect);
     }
     return builder.release();
 }
 
-bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale)
+static void beginDeferredFilter(GraphicsContext* context, FilterData* filterData)
 {
-    bool matchesFilterSize = true;
-    if (size.width() * scale.width() > kMaxFilterSize) {
-        scale.setWidth(kMaxFilterSize / size.width());
-        matchesFilterSize = false;
-    }
-    if (size.height() * scale.height() > kMaxFilterSize) {
-        scale.setHeight(kMaxFilterSize / size.height());
-        matchesFilterSize = false;
-    }
-
-    return matchesFilterSize;
+    context->beginRecording(filterData->boundaries);
+    context->setShouldSmoothFonts(false);
+    // We pass the boundaries to SkPictureImageFilter so it knows the
+    // world-space position of the filter primitives. It gets them
+    // from the DisplayList, which also applies the inverse translate
+    // to the origin. So we apply the forward translate here to avoid
+    // it being applied twice.
+    // FIXME: we should fix SkPicture to handle this offset itself, or
+    // make the translate optional on SkPictureImageFilter.
+    // See https://code.google.com/p/skia/issues/detail?id=2801
+    context->translate(filterData->boundaries.x(), filterData->boundaries.y());
 }
 
-static bool createImageBuffer(const Filter* filter, OwnPtr<ImageBuffer>& imageBuffer, bool accelerated)
+static void endDeferredFilter(GraphicsContext* context, FilterData* filterData)
 {
-    IntRect paintRect = filter->sourceImageRect();
-    // Don't create empty ImageBuffers.
-    if (paintRect.isEmpty())
-        return false;
-
-    OwnPtr<ImageBufferSurface> surface;
-    if (accelerated)
-        surface = adoptPtr(new AcceleratedImageBufferSurface(paintRect.size()));
-    if (!accelerated || !surface->isValid())
-        surface = adoptPtr(new UnacceleratedImageBufferSurface(paintRect.size()));
-    if (!surface->isValid())
-        return false;
-    OwnPtr<ImageBuffer> image = ImageBuffer::create(surface.release());
-
-    GraphicsContext* imageContext = image->context();
-    ASSERT(imageContext);
+    // FIXME: maybe filterData should just hold onto SourceGraphic after creation?
+    SourceGraphic* sourceGraphic = static_cast<SourceGraphic*>(filterData->builder->getEffectById(SourceGraphic::effectName()));
+    ASSERT(sourceGraphic);
+    sourceGraphic->setDisplayList(context->endRecording());
+}
 
-    imageContext->translate(-paintRect.x(), -paintRect.y());
-    imageContext->concatCTM(filter->absoluteTransform());
-    imageBuffer = image.release();
-    return true;
+static void drawDeferredFilter(GraphicsContext* context, FilterData* filterData, SVGFilterElement* filterElement)
+{
+    SkiaImageFilterBuilder builder(context);
+    SourceGraphic* sourceGraphic = static_cast<SourceGraphic*>(filterData->builder->getEffectById(SourceGraphic::effectName()));
+    ASSERT(sourceGraphic);
+    builder.setSourceGraphic(sourceGraphic);
+    RefPtr<ImageFilter> imageFilter = builder.build(filterData->builder->lastEffect(), ColorSpaceDeviceRGB);
+    FloatRect boundaries = filterData->boundaries;
+    context->save();
+
+    FloatSize deviceSize = context->getCTM().mapSize(boundaries.size());
+    float scaledArea = deviceSize.width() * deviceSize.height();
+
+    // If area of scaled size is bigger than the upper limit, adjust the scale
+    // to fit. Note that this only really matters in the non-impl-side painting
+    // case, since the impl-side case never allocates a full-sized backing
+    // store, only tile-sized.
+    // FIXME: remove this once all platforms are using impl-side painting.
+    // crbug.com/169282.
+    if (scaledArea > FilterEffect::maxFilterArea()) {
+        float scale = sqrtf(FilterEffect::maxFilterArea() / scaledArea);
+        context->scale(scale, scale);
+    }
+    // Clip drawing of filtered image to the minimum required paint rect.
+    FilterEffect* lastEffect = filterData->builder->lastEffect();
+    context->clipRect(lastEffect->determineAbsolutePaintRect(lastEffect->maxEffectRect()));
+    if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
+        // Get boundaries in device coords.
+        // FIXME: See crbug.com/382491. Is the use of getCTM OK here, given it does not include device
+        // zoom or High DPI adjustments?
+        FloatSize size = context->getCTM().mapSize(boundaries.size());
+        // Compute the scale amount required so that the resulting offscreen is exactly filterResX by filterResY pixels.
+        float filterResScaleX = filterElement->filterResX()->currentValue()->value() / size.width();
+        float filterResScaleY = filterElement->filterResY()->currentValue()->value() / size.height();
+        // Scale the CTM so the primitive is drawn to filterRes.
+        context->scale(filterResScaleX, filterResScaleY);
+        // Create a resize filter with the inverse scale.
+        AffineTransform resizeMatrix;
+        resizeMatrix.scale(1 / filterResScaleX, 1 / filterResScaleY);
+        imageFilter = builder.buildTransform(resizeMatrix, imageFilter.get());
+    }
+    // If the CTM contains rotation or shearing, apply the filter to
+    // the unsheared/unrotated matrix, and do the shearing/rotation
+    // as a final pass.
+    AffineTransform ctm = context->getCTM();
+    if (ctm.b() || ctm.c()) {
+        AffineTransform scaleAndTranslate;
+        scaleAndTranslate.translate(ctm.e(), ctm.f());
+        scaleAndTranslate.scale(ctm.xScale(), ctm.yScale());
+        ASSERT(scaleAndTranslate.isInvertible());
+        AffineTransform shearAndRotate = scaleAndTranslate.inverse();
+        shearAndRotate.multiply(ctm);
+        context->setCTM(scaleAndTranslate);
+        imageFilter = builder.buildTransform(shearAndRotate, imageFilter.get());
+    }
+    context->beginLayer(1, CompositeSourceOver, &boundaries, ColorFilterNone, imageFilter.get());
+    context->endLayer();
+    context->restore();
 }
 
-bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
+bool RenderSVGResourceFilter::prepareEffect(RenderObject* object, GraphicsContext*& context)
 {
     ASSERT(object);
     ASSERT(context);
-    ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
 
     clearInvalidationMask();
 
-    bool deferredFiltersEnabled = object->document().settings()->deferredFiltersEnabled();
-    if (deferredFiltersEnabled) {
-        if (m_objects.contains(object))
-            return false; // We're in a cycle.
-    } else if (m_filter.contains(object)) {
+    if (m_filter.contains(object)) {
         FilterData* filterData = m_filter.get(object);
-        if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying)
+        if (filterData->state == FilterData::PaintingSource)
             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.
+        return false; // Already built, or we're in a cycle. Regardless, just do nothing more now.
     }
 
     OwnPtr<FilterData> filterData(adoptPtr(new FilterData));
     FloatRect targetBoundingBox = object->objectBoundingBox();
 
     SVGFilterElement* filterElement = toSVGFilterElement(element());
-    filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnitsCurrentValue(), targetBoundingBox);
+    filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits()->currentValue()->enumValue(), targetBoundingBox);
     if (filterData->boundaries.isEmpty())
         return false;
 
-    // Determine absolute transformation matrix for filter.
-    AffineTransform absoluteTransform;
-    SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransform);
-    if (!absoluteTransform.isInvertible())
-        return false;
-
-    // Filters cannot handle a full transformation, only scales in each direction.
-    FloatSize filterScale;
-
-    // Calculate the scale factor for the filter.
-    // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
-    if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
-        //  If resolution is specified, scale to match it.
-        filterScale = FloatSize(
-            filterElement->filterResX()->currentValue()->value() / filterData->boundaries.width(),
-            filterElement->filterResY()->currentValue()->value() / filterData->boundaries.height());
-    } else {
-        // Otherwise, use the scale of the absolute transform.
-        filterScale = FloatSize(absoluteTransform.xScale(), absoluteTransform.yScale());
-    }
-    // The size of the scaled filter boundaries shouldn't be bigger than kMaxFilterSize.
-    // Intermediate filters are limited by the filter boundaries so they can't be bigger than this.
-    fitsInMaximumImageSize(filterData->boundaries.size(), filterScale);
-
     filterData->drawingRegion = object->strokeBoundingBox();
     filterData->drawingRegion.intersect(filterData->boundaries);
-    FloatRect absoluteDrawingRegion = filterData->drawingRegion;
-    if (!deferredFiltersEnabled)
-        absoluteDrawingRegion.scale(filterScale.width(), filterScale.height());
-
-    IntRect intDrawingRegion = enclosingIntRect(absoluteDrawingRegion);
+    IntRect intDrawingRegion = enclosingIntRect(filterData->drawingRegion);
 
     // Create the SVGFilter object.
-    bool primitiveBoundingBoxMode = filterElement->primitiveUnitsCurrentValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
-    filterData->shearFreeAbsoluteTransform = AffineTransform();
-    if (!deferredFiltersEnabled)
-        filterData->shearFreeAbsoluteTransform.scale(filterScale.width(), filterScale.height());
-    filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, intDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
+    bool primitiveBoundingBoxMode = filterElement->primitiveUnits()->currentValue()->enumValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
+    filterData->filter = SVGFilter::create(intDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
 
     // Create all relevant filter primitives.
     filterData->builder = buildPrimitives(filterData->filter.get());
@@ -224,166 +219,51 @@ bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*,
 
     lastEffect->determineFilterPrimitiveSubregion(ClipToFilterRegion);
 
-    if (deferredFiltersEnabled) {
-        SkiaImageFilterBuilder builder(context);
-        FloatRect oldBounds = context->getClipBounds();
-        m_objects.set(object, oldBounds);
-        RefPtr<ImageFilter> imageFilter = builder.build(lastEffect, ColorSpaceDeviceRGB);
-        FloatRect boundaries = enclosingIntRect(filterData->boundaries);
-        if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
-            context->save();
-            // Get boundaries in device coords.
-            FloatSize size = context->getCTM().mapSize(boundaries.size());
-            // Compute the scale amount required so that the resulting offscreen is exactly filterResX by filterResY pixels.
-            FloatSize filterResScale(
-                filterElement->filterResX()->currentValue()->value() / size.width(),
-                filterElement->filterResY()->currentValue()->value() / size.height());
-            // Scale the CTM so the primitive is drawn to filterRes.
-            context->translate(boundaries.x(), boundaries.y());
-            context->scale(filterResScale);
-            context->translate(-boundaries.x(), -boundaries.y());
-            // Create a resize filter with the inverse scale.
-            imageFilter = builder.buildResize(1 / filterResScale.width(), 1 / filterResScale.height(), imageFilter.get());
-            // Clip the context so that the offscreen created in beginLayer()
-            // is clipped to filterResX by filerResY. Use Replace mode since
-            // this clip may be larger than the parent device.
-            context->clipRectReplace(boundaries);
-        }
-        context->beginLayer(1, CompositeSourceOver, &boundaries, ColorFilterNone, imageFilter.get());
-        return true;
-    }
-
-    // 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 (filterData->drawingRegion.isEmpty()) {
-        ASSERT(!m_filter.contains(object));
-        filterData->savedContext = context;
-        m_filter.set(object, filterData.release());
-        return false;
-    }
-
-    OwnPtr<ImageBuffer> sourceGraphic;
-    bool isAccelerated = object->document().settings()->acceleratedFiltersEnabled();
-    if (!createImageBuffer(filterData->filter.get(), sourceGraphic, isAccelerated)) {
-        ASSERT(!m_filter.contains(object));
-        filterData->savedContext = context;
-        m_filter.set(object, filterData.release());
-        return false;
-    }
-
-    // Set the rendering mode from the page's settings.
-    filterData->filter->setIsAccelerated(isAccelerated);
-
-    GraphicsContext* sourceGraphicContext = sourceGraphic->context();
-    ASSERT(sourceGraphicContext);
-
-    filterData->sourceGraphicBuffer = sourceGraphic.release();
-    filterData->savedContext = context;
-
-    context = sourceGraphicContext;
-
-    ASSERT(!m_filter.contains(object));
+    FilterData* data = filterData.get();
     m_filter.set(object, filterData.release());
-
+    beginDeferredFilter(context, data);
     return true;
 }
 
-void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*)
+void RenderSVGResourceFilter::finishEffect(RenderObject* object, GraphicsContext*& context)
 {
     ASSERT(object);
     ASSERT(context);
-    ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
-
-    if (object->document().settings()->deferredFiltersEnabled()) {
-        SVGFilterElement* filterElement = toSVGFilterElement(element());
-        if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
-            // Restore the clip bounds before endLayer(), so the filtered
-            // image draw is clipped to the original device bounds, not the
-            // clip we set before the beginLayer() above.
-            FloatRect oldBounds = m_objects.get(object);
-            context->clipRectReplace(oldBounds);
-            context->endLayer();
-            context->restore();
-        } else {
-            context->endLayer();
-        }
-        m_objects.remove(object);
-        return;
-    }
 
     FilterData* filterData = m_filter.get(object);
     if (!filterData)
         return;
 
     switch (filterData->state) {
-    case FilterData::MarkedForRemoval:
-        m_filter.remove(object);
-        return;
-
     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.
+        // applyResource detected a cycle. 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);
-            return;
-        }
-
-        context = filterData->savedContext;
-        filterData->savedContext = 0;
+        endDeferredFilter(context, filterData);
         break;
 
     case FilterData::Built: { } // Empty
     }
 
-    FilterEffect* lastEffect = filterData->builder->lastEffect();
-
-    if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
-        // 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->state != FilterData::Built)
-            filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release());
-
-        // Always true if filterData is just built (filterData->state == FilterData::Built).
-        if (!lastEffect->hasResult()) {
-            filterData->state = FilterData::Applying;
-            lastEffect->apply();
-            lastEffect->correctFilterResultIfNeeded();
-            lastEffect->transformResultColorSpace(ColorSpaceDeviceRGB);
-        }
-        filterData->state = FilterData::Built;
-
-        ImageBuffer* resultImage = lastEffect->asImageBuffer();
-        if (resultImage) {
-            context->drawImageBuffer(resultImage, filterData->filter->mapAbsoluteRectToLocalRect(lastEffect->absolutePaintRect()));
-        }
-    }
-    filterData->sourceGraphicBuffer.clear();
+    drawDeferredFilter(context, filterData, toSVGFilterElement(element()));
+    filterData->state = FilterData::Built;
 }
 
 FloatRect RenderSVGResourceFilter::resourceBoundingBox(const RenderObject* object)
 {
     if (SVGFilterElement* element = toSVGFilterElement(this->element()))
-        return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnitsCurrentValue(), object->objectBoundingBox());
+        return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnits()->currentValue()->enumValue(), object->objectBoundingBox());
 
     return FloatRect();
 }
 
 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
 {
-    if (object->document().settings()->deferredFiltersEnabled()) {
-        markAllClientsForInvalidation(RepaintInvalidation);
-        markAllClientLayersForInvalidation();
-        return;
-    }
-
     FilterMap::iterator it = m_filter.begin();
     FilterMap::iterator end = m_filter.end();
     SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
@@ -403,8 +283,8 @@ void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, co
             return;
         builder->clearResultsRecursive(effect);
 
-        // Repaint the image on the screen.
-        markClientForInvalidation(it->key, RepaintInvalidation);
+        // Issue paint invalidations for the image on the screen.
+        markClientForInvalidation(it->key, PaintInvalidation);
     }
     markAllClientLayersForInvalidation();
 }