bool RenderSVGResourceClipper::applyResource(RenderElement& renderer, const RenderStyle&, GraphicsContext*& context, unsigned short resourceMode) { ASSERT(context); ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); return applyClippingToContext(renderer, renderer.objectBoundingBox(), renderer.repaintRectInLocalCoordinates(), context); }
bool SVGRenderSupport::pointInClippingArea(const RenderElement& renderer, const FloatPoint& point) { // We just take clippers into account to determine if a point is on the node. The Specification may // change later and we also need to check maskers. SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer); if (!resources) return true; if (RenderSVGResourceClipper* clipper = resources->clipper()) return clipper->hitTestClipContent(renderer.objectBoundingBox(), point); return true; }
bool RenderSVGResourcePattern::applyResource(RenderElement& renderer, const RenderStyle& style, GraphicsContext*& context, unsigned short resourceMode) { ASSERT(context); ASSERT(resourceMode != ApplyToDefaultMode); // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified, // then the given effect (e.g. a gradient or a filter) will be ignored. FloatRect objectBoundingBox = renderer.objectBoundingBox(); if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty()) return false; PatternData* patternData = buildPattern(renderer, resourceMode, *context); if (!patternData) return false; // Draw pattern context->save(); const SVGRenderStyle& svgStyle = style.svgStyle(); if (resourceMode & ApplyToFillMode) { context->setAlpha(svgStyle.fillOpacity()); context->setFillPattern(*patternData->pattern); context->setFillRule(svgStyle.fillRule()); } else if (resourceMode & ApplyToStrokeMode) { if (svgStyle.vectorEffect() == VE_NON_SCALING_STROKE) patternData->pattern->setPatternSpaceTransform(transformOnNonScalingStroke(&renderer, patternData->transform)); context->setAlpha(svgStyle.strokeOpacity()); context->setStrokePattern(*patternData->pattern); SVGRenderSupport::applyStrokeStyleToContext(context, style, renderer); } if (resourceMode & ApplyToTextMode) { if (resourceMode & ApplyToFillMode) { context->setTextDrawingMode(TextModeFill); #if USE(CG) context->applyFillPattern(); #endif } else if (resourceMode & ApplyToStrokeMode) { context->setTextDrawingMode(TextModeStroke); #if USE(CG) context->applyStrokePattern(); #endif } } return true; }
bool RenderSVGResourcePattern::buildTileImageTransform(RenderElement& renderer, const PatternAttributes& attributes, const SVGPatternElement& patternElement, FloatRect& patternBoundaries, AffineTransform& tileImageTransform) const { FloatRect objectBoundingBox = renderer.objectBoundingBox(); patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement); if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0) return false; AffineTransform viewBoxCTM = SVGFitToViewBox::viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height()); // Apply viewBox/objectBoundingBox transformations. if (!viewBoxCTM.isIdentity()) tileImageTransform = viewBoxCTM; else if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height()); return true; }
void SVGRenderingContext::prepareToRenderSVGContent(RenderElement& renderer, PaintInfo& paintInfo, NeedsGraphicsContextSave needsGraphicsContextSave) { #ifndef NDEBUG // This function must not be called twice! ASSERT(!(m_renderingFlags & PrepareToRenderSVGContentWasCalled)); m_renderingFlags |= PrepareToRenderSVGContentWasCalled; #endif m_renderer = &renderer; m_paintInfo = &paintInfo; m_filter = 0; // We need to save / restore the context even if the initialization failed. if (needsGraphicsContextSave == SaveGraphicsContext) { m_paintInfo->context().save(); m_renderingFlags |= RestoreGraphicsContext; } auto& style = m_renderer->style(); const SVGRenderStyle& svgStyle = style.svgStyle(); // Setup transparency layers before setting up SVG resources! bool isRenderingMask = isRenderingMaskImage(*m_renderer); // RenderLayer takes care of root opacity. float opacity = (renderer.isSVGRoot() || isRenderingMask) ? 1 : style.opacity(); const ShadowData* shadow = svgStyle.shadow(); bool hasBlendMode = style.hasBlendMode(); bool hasIsolation = style.hasIsolation(); bool isolateMaskForBlending = false; #if ENABLE(CSS_COMPOSITING) if (svgStyle.hasMasker() && is<SVGGraphicsElement>(downcast<SVGElement>(*renderer.element()))) { SVGGraphicsElement& graphicsElement = downcast<SVGGraphicsElement>(*renderer.element()); isolateMaskForBlending = graphicsElement.shouldIsolateBlending(); } #endif if (opacity < 1 || shadow || hasBlendMode || isolateMaskForBlending || hasIsolation) { FloatRect repaintRect = m_renderer->repaintRectInLocalCoordinates(); m_paintInfo->context().clip(repaintRect); if (opacity < 1 || hasBlendMode || isolateMaskForBlending || hasIsolation) { if (hasBlendMode) m_paintInfo->context().setCompositeOperation(m_paintInfo->context().compositeOperation(), style.blendMode()); m_paintInfo->context().beginTransparencyLayer(opacity); if (hasBlendMode) m_paintInfo->context().setCompositeOperation(m_paintInfo->context().compositeOperation(), BlendModeNormal); m_renderingFlags |= EndOpacityLayer; } if (shadow) { m_paintInfo->context().setShadow(IntSize(roundToInt(shadow->x()), roundToInt(shadow->y())), shadow->radius(), shadow->color()); m_paintInfo->context().beginTransparencyLayer(1); m_renderingFlags |= EndShadowLayer; } } ClipPathOperation* clipPathOperation = style.clipPath(); if (is<ShapeClipPathOperation>(clipPathOperation)) { auto& clipPath = downcast<ShapeClipPathOperation>(*clipPathOperation); FloatRect referenceBox; if (clipPath.referenceBox() == Stroke) // FIXME: strokeBoundingBox() takes dasharray into account but shouldn't. referenceBox = renderer.strokeBoundingBox(); else if (clipPath.referenceBox() == ViewBox && renderer.element()) { FloatSize viewportSize; SVGLengthContext(downcast<SVGElement>(renderer.element())).determineViewport(viewportSize); referenceBox.setWidth(viewportSize.width()); referenceBox.setHeight(viewportSize.height()); } else referenceBox = renderer.objectBoundingBox(); m_paintInfo->context().clipPath(clipPath.pathForReferenceRect(referenceBox), clipPath.windRule()); } auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*m_renderer); if (!resources) { if (style.hasReferenceFilterOnly()) return; m_renderingFlags |= RenderingPrepared; return; } if (!isRenderingMask) { if (RenderSVGResourceMasker* masker = resources->masker()) { GraphicsContext* contextPtr = &m_paintInfo->context(); bool result = masker->applyResource(*m_renderer, style, contextPtr, ApplyToDefaultMode); m_paintInfo->setContext(*contextPtr); if (!result) return; } } RenderSVGResourceClipper* clipper = resources->clipper(); if (!clipPathOperation && clipper) { GraphicsContext* contextPtr = &m_paintInfo->context(); bool result = clipper->applyResource(*m_renderer, style, contextPtr, ApplyToDefaultMode); m_paintInfo->setContext(*contextPtr); if (!result) return; } if (!isRenderingMask) { m_filter = resources->filter(); if (m_filter) { m_savedContext = &m_paintInfo->context(); m_savedPaintRect = m_paintInfo->rect; // Return with false here may mean that we don't need to draw the content // (because it was either drawn before or empty) but we still need to apply the filter. m_renderingFlags |= EndFilterLayer; GraphicsContext* contextPtr = &m_paintInfo->context(); bool result = m_filter->applyResource(*m_renderer, style, contextPtr, ApplyToDefaultMode); m_paintInfo->setContext(*contextPtr); if (!result) return; // Since we're caching the resulting bitmap and do not invalidate it on repaint rect // changes, we need to paint the whole filter region. Otherwise, elements not visible // at the time of the initial paint (due to scrolling, window size, etc.) will never // be drawn. m_paintInfo->rect = IntRect(m_filter->drawingRegion(m_renderer)); } } m_renderingFlags |= RenderingPrepared; }
bool RenderSVGResourceFilter::applyResource(RenderElement& renderer, const RenderStyle&, GraphicsContext*& context, unsigned short resourceMode) { ASSERT(context); ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); 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. } auto filterData = std::make_unique<FilterData>(); FloatRect targetBoundingBox = renderer.objectBoundingBox(); filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(&filterElement(), filterElement().filterUnits(), targetBoundingBox); if (filterData->boundaries.isEmpty()) return false; // Determine absolute transformation matrix for filter. AffineTransform absoluteTransform; SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(renderer, 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); 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; 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); static const unsigned maxTotalOfEffectInputs = 100; FilterEffect* lastEffect = filterData->builder->lastEffect(); if (!lastEffect || lastEffect->totalNumberOfEffectInputs() > maxTotalOfEffectInputs) 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 (filterData->drawingRegion.isEmpty()) { ASSERT(!m_filter.contains(&renderer)); filterData->savedContext = context; m_filter.set(&renderer, WTF::move(filterData)); 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); 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(&renderer, WTF::move(filterData)); return false; } // Set the rendering mode from the page's settings. filterData->filter->setRenderingMode(renderingMode); GraphicsContext* sourceGraphicContext = sourceGraphic->context(); ASSERT(sourceGraphicContext); filterData->sourceGraphicBuffer = WTF::move(sourceGraphic); filterData->savedContext = context; context = sourceGraphicContext; ASSERT(!m_filter.contains(&renderer)); m_filter.set(&renderer, WTF::move(filterData)); return true; }