bool RenderSVGResourceFilter::prepareEffect(RenderObject* object, GraphicsContext*& context) { ASSERT(object); ASSERT(context); clearInvalidationMask(); if (m_filter.contains(object)) { FilterData* filterData = m_filter.get(object); if (filterData->state == FilterData::PaintingSource) filterData->state = FilterData::CycleDetected; return false; // Already built, or we're in a cycle. Regardless, just do nothing more now. } OwnPtrWillBeRawPtr<FilterData> filterData = FilterData::create(); FloatRect targetBoundingBox = object->objectBoundingBox(); SVGFilterElement* filterElement = toSVGFilterElement(element()); filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits()->currentValue()->enumValue(), targetBoundingBox); if (filterData->boundaries.isEmpty()) return false; // Create the SVGFilter object. FloatRect drawingRegion = object->strokeBoundingBox(); drawingRegion.intersect(filterData->boundaries); bool primitiveBoundingBoxMode = filterElement->primitiveUnits()->currentValue()->enumValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; filterData->filter = SVGFilter::create(enclosingIntRect(drawingRegion), targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode); // Create all relevant filter primitives. filterData->builder = buildPrimitives(filterData->filter.get()); if (!filterData->builder) return false; FilterEffect* lastEffect = filterData->builder->lastEffect(); if (!lastEffect) return false; lastEffect->determineFilterPrimitiveSubregion(ClipToFilterRegion); FilterData* data = filterData.get(); m_filter.set(object, filterData.release()); beginDeferredFilter(context, data); return true; }
bool RenderSVGResourceFilter::applyResource(RenderObject* object, 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 } OwnPtr<FilterData> filterData(adoptPtr(new FilterData)); FloatRect targetBoundingBox = object->objectBoundingBox(); SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node()); filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits(), targetBoundingBox); if (filterData->boundaries.isEmpty()) return false; // Determine absolute transformation matrix for filter. AffineTransform absoluteTransform; SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform); if (!absoluteTransform.isInvertible()) return false; // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile. filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), 0, 0); // 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); // Create the SVGFilter object. 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. filterData->builder = buildPrimitives(filterData->filter.get()); if (!filterData->builder) return false; // 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 (scale.isEmpty()) return false; // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize. FloatRect tempSourceRect = absoluteDrawingRegion; tempSourceRect.scale(scale.width(), scale.height()); fitsInMaximumImageSize(tempSourceRect.size(), scale); // Set the scale level in SVGFilter. filterData->filter->setFilterResolution(scale); FilterEffect* lastEffect = filterData->builder->lastEffect(); if (!lastEffect) return false; RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect); FloatRect subRegion = lastEffect->maxEffectRect(); // At least one FilterEffect has a too big image size, // recalculate the effect sizes with new scale factors. if (!fitsInMaximumImageSize(subRegion.size(), scale)) { filterData->filter->setFilterResolution(scale); RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect); } // 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)); filterData->savedContext = context; m_filter.set(object, filterData.leakPtr()); return false; } // Change the coordinate transformation applied to the filtered element to reflect the resolution of the filter. AffineTransform effectiveTransform; effectiveTransform.scale(scale.width(), scale.height()); effectiveTransform.multiply(filterData->shearFreeAbsoluteTransform); OwnPtr<ImageBuffer> sourceGraphic; RenderingMode renderingMode = object->document()->page()->settings()->acceleratedFiltersEnabled() ? Accelerated : Unaccelerated; if (!SVGImageBufferTools::createImageBuffer(drawingRegion, effectiveTransform, sourceGraphic, ColorSpaceLinearRGB, renderingMode)) { ASSERT(!m_filter.contains(object)); filterData->savedContext = context; m_filter.set(object, filterData.leakPtr()); return false; } // Set the rendering mode from the page's settings. filterData->filter->setRenderingMode(renderingMode); GraphicsContext* sourceGraphicContext = sourceGraphic->context(); ASSERT(sourceGraphicContext); filterData->sourceGraphicBuffer = sourceGraphic.release(); filterData->savedContext = context; context = sourceGraphicContext; ASSERT(!m_filter.contains(object)); m_filter.set(object, filterData.leakPtr()); return true; }
GraphicsContext* SVGFilterPainter::prepareEffect(const LayoutObject& object, SVGFilterRecordingContext& recordingContext) { m_filter.clearInvalidationMask(); if (FilterData* filterData = m_filter.getFilterDataForLayoutObject(&object)) { // If the filterData already exists we do not need to record the content // to be filtered. This can occur if the content was previously recorded // or we are in a cycle. if (filterData->m_state == FilterData::PaintingFilter) filterData->m_state = FilterData::PaintingFilterCycleDetected; if (filterData->m_state == FilterData::RecordingContent) filterData->m_state = FilterData::RecordingContentCycleDetected; return nullptr; } OwnPtrWillBeRawPtr<FilterData> filterData = FilterData::create(); FloatRect referenceBox = object.objectBoundingBox(); SVGFilterElement* filterElement = toSVGFilterElement(m_filter.element()); FloatRect filterRegion = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits()->currentValue()->enumValue(), referenceBox); if (filterRegion.isEmpty()) return nullptr; // Create the SVGFilter object. bool primitiveBoundingBoxMode = filterElement->primitiveUnits()->currentValue()->enumValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; Filter::UnitScaling unitScaling = primitiveBoundingBoxMode ? Filter::BoundingBox : Filter::UserSpace; filterData->filter = Filter::create(referenceBox, filterRegion, 1, unitScaling); filterData->nodeMap = SVGFilterGraphNodeMap::create(); IntRect sourceRegion = enclosingIntRect(intersection(filterRegion, object.strokeBoundingBox())); filterData->filter->sourceGraphic()->setSourceRect(sourceRegion); // Create all relevant filter primitives. SVGFilterBuilder builder(filterData->filter->sourceGraphic(), filterData->nodeMap.get()); builder.buildGraph(filterData->filter.get(), *filterElement, referenceBox); FilterEffect* lastEffect = builder.lastEffect(); if (!lastEffect) return nullptr; lastEffect->determineFilterPrimitiveSubregion(ClipToFilterRegion); filterData->filter->setLastEffect(lastEffect); FilterData* data = filterData.get(); // TODO(pdr): Can this be moved out of painter? m_filter.setFilterDataForLayoutObject(const_cast<LayoutObject*>(&object), filterData.release()); return recordingContext.beginContent(data); }