FloatRect FilterEffect::determineFilterPrimitiveSubregion(DetermineSubregionFlags flags) { Filter* filter = this->filter(); ASSERT(filter); // FETile, FETurbulence, FEFlood don't have input effects, take the filter region as unite rect. FloatRect subregion; if (unsigned numberOfInputEffects = inputEffects().size()) { subregion = inputEffect(0)->determineFilterPrimitiveSubregion(flags); for (unsigned i = 1; i < numberOfInputEffects; ++i) subregion.unite(inputEffect(i)->determineFilterPrimitiveSubregion(flags)); } else { subregion = filter->filterRegion(); } // After calling determineFilterPrimitiveSubregion on the target effect, reset the subregion again for <feTile>. if (filterEffectType() == FilterEffectTypeTile) subregion = filter->filterRegion(); if (flags & MapRectForward) { // mapRect works on absolute rectangles. subregion = filter->mapAbsoluteRectToLocalRect(mapRect( filter->mapLocalRectToAbsoluteRect(subregion))); } FloatRect boundaries = effectBoundaries(); if (hasX()) subregion.setX(boundaries.x()); if (hasY()) subregion.setY(boundaries.y()); if (hasWidth()) subregion.setWidth(boundaries.width()); if (hasHeight()) subregion.setHeight(boundaries.height()); setFilterPrimitiveSubregion(subregion); FloatRect absoluteSubregion = filter->mapLocalRectToAbsoluteRect(subregion); // Clip every filter effect to the filter region. if (flags & ClipToFilterRegion) { absoluteSubregion.intersect(filter->absoluteFilterRegion()); } setMaxEffectRect(absoluteSubregion); return subregion; }
void FETile::apply() { // FIXME: See bug 47315. This is a hack to work around a compile failure, but is incorrect behavior otherwise. #if ENABLE(SVG) FilterEffect* in = inputEffect(0); in->apply(); if (!in->resultImage()) return; GraphicsContext* filterContext = effectContext(); if (!filterContext) return; setIsAlphaImage(in->isAlphaImage()); // Source input needs more attention. It has the size of the filterRegion but gives the // size of the cutted sourceImage back. This is part of the specification and optimization. FloatRect tileRect = in->maxEffectRect(); FloatPoint inMaxEffectLocation = tileRect.location(); FloatPoint maxEffectLocation = maxEffectRect().location(); if (in->filterEffectType() == FilterEffectTypeSourceInput) { Filter* filter = this->filter(); tileRect = filter->filterRegion(); tileRect.scale(filter->filterResolution().width(), filter->filterResolution().height()); } OwnPtr<ImageBuffer> tileImage; if (!SVGImageBufferTools::createImageBuffer(tileRect, tileRect, tileImage, ColorSpaceDeviceRGB)) return; GraphicsContext* tileImageContext = tileImage->context(); tileImageContext->translate(-inMaxEffectLocation.x(), -inMaxEffectLocation.y()); tileImageContext->drawImageBuffer(in->resultImage(), ColorSpaceDeviceRGB, in->absolutePaintRect().location()); RefPtr<Pattern> pattern = Pattern::create(tileImage->copyImage(), true, true); AffineTransform patternTransform; patternTransform.translate(inMaxEffectLocation.x() - maxEffectLocation.x(), inMaxEffectLocation.y() - maxEffectLocation.y()); pattern->setPatternSpaceTransform(patternTransform); filterContext->setFillPattern(pattern); filterContext->fillRect(FloatRect(FloatPoint(), absolutePaintRect().size())); #endif }
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; } SVGFilterGraphNodeMap* nodeMap = SVGFilterGraphNodeMap::create(); FilterEffectBuilder builder(nullptr, object.objectBoundingBox(), 1); Filter* filter = builder.buildReferenceFilter( toSVGFilterElement(*m_filter.element()), nullptr, nodeMap); if (!filter || !filter->lastEffect()) return nullptr; IntRect sourceRegion = enclosingIntRect( intersection(filter->filterRegion(), object.strokeBoundingBox())); filter->getSourceGraphic()->setSourceRect(sourceRegion); FilterData* filterData = FilterData::create(); filterData->lastEffect = filter->lastEffect(); filterData->nodeMap = nodeMap; // TODO(pdr): Can this be moved out of painter? m_filter.setFilterDataForLayoutObject(const_cast<LayoutObject*>(&object), filterData); return recordingContext.beginContent(filterData); }
void SVGFilterRecordingContext::endContent(FilterData* filterData) { DCHECK_EQ(filterData->m_state, FilterData::RecordingContent); Filter* filter = filterData->lastEffect->getFilter(); SourceGraphic* sourceGraphic = filter->getSourceGraphic(); DCHECK(sourceGraphic); // Use the context that contains the filtered content. DCHECK(m_paintController); DCHECK(m_context); m_context->beginRecording(filter->filterRegion()); m_paintController->commitNewDisplayItems(); m_paintController->paintArtifact().replay(*m_context); SkiaImageFilterBuilder::buildSourceGraphic(sourceGraphic, m_context->endRecording()); // Content is cached by the source graphic so temporaries can be freed. m_paintController = nullptr; m_context = nullptr; filterData->m_state = FilterData::ReadyToPaint; }