AffineTransform RenderSVGResourceContainer::computeResourceSpaceTransform(RenderObject* object, const AffineTransform& baseTransform, const SVGRenderStyle* svgStyle, unsigned short resourceMode) { AffineTransform computedSpaceTransform = baseTransform; if (resourceMode & ApplyToTextMode) { // Depending on the font scaling factor, we may need to apply an // additional transform (scale-factor) the paintserver, since text // painting removes the scale factor from the context. (See // SVGInlineTextBox::paintTextWithShadows.) AffineTransform additionalTextTransformation; if (shouldTransformOnTextPainting(object, additionalTextTransformation)) computedSpaceTransform = additionalTextTransformation * computedSpaceTransform; } if (resourceMode & ApplyToStrokeMode) { // Non-scaling stroke needs to reset the transform back to the host transform. if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE) computedSpaceTransform = transformOnNonScalingStroke(object, computedSpaceTransform); } return computedSpaceTransform; }
PatternData* RenderSVGResourcePattern::buildPattern(RenderElement& renderer, unsigned short resourceMode, GraphicsContext& context) { PatternData* currentData = m_patternMap.get(&renderer); if (currentData && currentData->pattern) return currentData; if (m_shouldCollectPatternAttributes) { patternElement().synchronizeAnimatedSVGAttribute(anyQName()); m_attributes = PatternAttributes(); patternElement().collectPatternAttributes(m_attributes); m_shouldCollectPatternAttributes = false; } // If we couldn't determine the pattern content element root, stop here. if (!m_attributes.patternContentElement()) return nullptr; // An empty viewBox disables rendering. if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty()) return nullptr; // Compute all necessary transformations to build the tile image & the pattern. FloatRect tileBoundaries; AffineTransform tileImageTransform; if (!buildTileImageTransform(renderer, m_attributes, patternElement(), tileBoundaries, tileImageTransform)) return nullptr; AffineTransform absoluteTransformIgnoringRotation = SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(renderer); // Ignore 2D rotation, as it doesn't affect the size of the tile. SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation); FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries); FloatRect clampedAbsoluteTileBoundaries; // Scale the tile size to match the scale level of the patternTransform. absoluteTileBoundaries.scale(static_cast<float>(m_attributes.patternTransform().xScale()), static_cast<float>(m_attributes.patternTransform().yScale())); // Build tile image. auto tileImage = createTileImage(m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform, clampedAbsoluteTileBoundaries, context.isAcceleratedContext() ? Accelerated : Unaccelerated); if (!tileImage) return nullptr; RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore); if (!copiedImage) return nullptr; // Build pattern. auto patternData = std::make_unique<PatternData>(); patternData->pattern = Pattern::create(copiedImage, true, true); // Compute pattern space transformation. const IntSize tileImageSize = tileImage->logicalSize(); patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y()); patternData->transform.scale(tileBoundaries.width() / tileImageSize.width(), tileBoundaries.height() / tileImageSize.height()); AffineTransform patternTransform = m_attributes.patternTransform(); if (!patternTransform.isIdentity()) patternData->transform = patternTransform * patternData->transform; // Account for text drawing resetting the context to non-scaled, see SVGInlineTextBox::paintTextWithShadows. if (resourceMode & ApplyToTextMode) { AffineTransform additionalTextTransformation; if (shouldTransformOnTextPainting(renderer, additionalTextTransformation)) patternData->transform *= additionalTextTransformation; } patternData->pattern->setPatternSpaceTransform(patternData->transform); // Various calls above may trigger invalidations in some fringe cases (ImageBuffer allocation // failures in the SVG image cache for example). To avoid having our PatternData deleted by // removeAllClientsFromCache(), we only make it visible in the cache at the very end. return m_patternMap.set(&renderer, WTF::move(patternData)).iterator->value.get(); }
bool RenderSVGResourceGradient::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode) { ASSERT(object); ASSERT(style); ASSERT(context); ASSERT(resourceMode != ApplyToDefaultMode); // Be sure to synchronize all SVG properties on the gradientElement _before_ processing any further. // Otherwhise the call to collectGradientAttributes() in createTileImage(), may cause the SVG DOM property // synchronization to kick in, which causes removeAllClientsFromCache() to be called, which in turn deletes our // GradientData object! Leaving out the line below will cause svg/dynamic-updates/SVG*GradientElement-svgdom* to crash. SVGGradientElement* gradientElement = static_cast<SVGGradientElement*>(node()); if (!gradientElement) return false; if (m_shouldCollectGradientAttributes) { gradientElement->synchronizeAnimatedSVGAttribute(anyQName()); if (!collectGradientAttributes(gradientElement)) return false; m_shouldCollectGradientAttributes = false; } // 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 = object->objectBoundingBox(); if (gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty()) return false; OwnPtr<GradientData>& gradientData = m_gradientMap.add(object, nullptr).iterator->value; if (!gradientData) gradientData = adoptPtr(new GradientData); bool isPaintingText = resourceMode & ApplyToTextMode; // Create gradient object if (!gradientData->gradient) { buildGradient(gradientData.get()); // CG platforms will handle the gradient space transform for text after applying the // resource, so don't apply it here. For non-CG platforms, we want the text bounding // box applied to the gradient space transform now, so the gradient shader can use it. #if USE(CG) if (gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && !objectBoundingBox.isEmpty() && !isPaintingText) { #else if (gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && !objectBoundingBox.isEmpty()) { #endif gradientData->userspaceTransform.translate(objectBoundingBox.x(), objectBoundingBox.y()); gradientData->userspaceTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); } AffineTransform gradientTransform; calculateGradientTransform(gradientTransform); gradientData->userspaceTransform *= gradientTransform; if (isPaintingText) { // Depending on font scaling factor, we may need to rescale the gradient here since // text painting removes the scale factor from the context. AffineTransform additionalTextTransform; if (shouldTransformOnTextPainting(object, additionalTextTransform)) gradientData->userspaceTransform *= additionalTextTransform; } gradientData->gradient->setGradientSpaceTransform(gradientData->userspaceTransform); } if (!gradientData->gradient) return false; // Draw gradient context->save(); if (isPaintingText) { #if USE(CG) if (!createMaskAndSwapContextForTextGradient(context, m_savedContext, m_imageBuffer, object)) { context->restore(); return false; } #endif context->setTextDrawingMode(resourceMode & ApplyToFillMode ? TextModeFill : TextModeStroke); } const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); if (resourceMode & ApplyToFillMode) { context->setAlpha(svgStyle->fillOpacity()); context->setFillGradient(gradientData->gradient); context->setFillRule(svgStyle->fillRule()); } else if (resourceMode & ApplyToStrokeMode) { if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE) gradientData->gradient->setGradientSpaceTransform(transformOnNonScalingStroke(object, gradientData->userspaceTransform)); context->setAlpha(svgStyle->strokeOpacity()); context->setStrokeGradient(gradientData->gradient); SVGRenderSupport::applyStrokeStyleToContext(context, style, object); } return true; } void RenderSVGResourceGradient::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path* path, const RenderSVGShape* shape) { ASSERT(context); ASSERT(resourceMode != ApplyToDefaultMode); if (resourceMode & ApplyToTextMode) { #if USE(CG) // CG requires special handling for gradient on text GradientData* gradientData; if (m_savedContext && (gradientData = m_gradientMap.get(object))) { // Restore on-screen drawing context context = m_savedContext; m_savedContext = 0; AffineTransform gradientTransform; calculateGradientTransform(gradientTransform); FloatRect targetRect; gradientData->gradient->setGradientSpaceTransform(clipToTextMask(context, m_imageBuffer, targetRect, object, gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, gradientTransform)); context->setFillGradient(gradientData->gradient); context->fillRect(targetRect); m_imageBuffer.clear(); } #else UNUSED_PARAM(object); #endif } else { if (resourceMode & ApplyToFillMode) { if (path) context->fillPath(*path); else if (shape) shape->fillShape(context); } if (resourceMode & ApplyToStrokeMode) { if (path) context->strokePath(*path); else if (shape) shape->strokeShape(context); } } context->restore(); }
bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode) { ASSERT(object); ASSERT(style); ASSERT(context); ASSERT(resourceMode != ApplyToDefaultMode); // Be sure to synchronize all SVG properties on the patternElement _before_ processing any further. // Otherwhise the call to collectPatternAttributes() below, may cause the SVG DOM property // synchronization to kick in, which causes removeAllClientsFromCache() to be called, which in turn deletes our // PatternData object! Leaving out the line below will cause svg/dynamic-updates/SVGPatternElement-svgdom* to crash. SVGPatternElement* patternElement = static_cast<SVGPatternElement*>(node()); if (!patternElement) return false; if (m_shouldCollectPatternAttributes) { patternElement->updateAnimatedSVGAttribute(anyQName()); m_attributes = PatternAttributes(); patternElement->collectPatternAttributes(m_attributes); m_shouldCollectPatternAttributes = false; } // 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 = object->objectBoundingBox(); if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty()) return false; OwnPtr<PatternData>& patternData = m_patternMap.add(object, nullptr).iterator->second; if (!patternData) patternData = adoptPtr(new PatternData); if (!patternData->pattern) { // If we couldn't determine the pattern content element root, stop here. if (!m_attributes.patternContentElement()) return false; // Compute all necessary transformations to build the tile image & the pattern. FloatRect tileBoundaries; AffineTransform tileImageTransform; if (!buildTileImageTransform(object, m_attributes, patternElement, tileBoundaries, tileImageTransform)) return false; AffineTransform absoluteTransformIgnoringRotation; SVGRenderingContext::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransformIgnoringRotation); // Ignore 2D rotation, as it doesn't affect the size of the tile. SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation); FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries); FloatRect clampedAbsoluteTileBoundaries; // Scale the tile size to match the scale level of the patternTransform. absoluteTileBoundaries.scale(static_cast<float>(m_attributes.patternTransform().xScale()), static_cast<float>(m_attributes.patternTransform().yScale())); // Build tile image. OwnPtr<ImageBuffer> tileImage = createTileImage(m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform, clampedAbsoluteTileBoundaries); if (!tileImage) return false; RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore); if (!copiedImage) return false; // Build pattern. patternData->pattern = Pattern::create(copiedImage, true, true); if (!patternData->pattern) return false; // Compute pattern space transformation. patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y()); patternData->transform.scale(tileBoundaries.width() / clampedAbsoluteTileBoundaries.width(), tileBoundaries.height() / clampedAbsoluteTileBoundaries.height()); AffineTransform patternTransform = m_attributes.patternTransform(); if (!patternTransform.isIdentity()) patternData->transform = patternTransform * patternData->transform; // Account for text drawing resetting the context to non-scaled, see SVGInlineTextBox::paintTextWithShadows. if (resourceMode & ApplyToTextMode) { AffineTransform additionalTextTransformation; if (shouldTransformOnTextPainting(object, additionalTextTransformation)) patternData->transform *= additionalTextTransformation; } patternData->pattern->setPatternSpaceTransform(patternData->transform); } // Draw pattern context->save(); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(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(object, patternData->transform)); context->setAlpha(svgStyle->strokeOpacity()); context->setStrokePattern(patternData->pattern); SVGRenderSupport::applyStrokeStyleToContext(context, style, object); } 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 RenderSVGResourceGradient::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode) { ASSERT(object); ASSERT(style); ASSERT(context); ASSERT(resourceMode != ApplyToDefaultMode); clearInvalidationMask(); // Be sure to synchronize all SVG properties on the gradientElement _before_ processing any further. // Otherwhise the call to collectGradientAttributes() in createTileImage(), may cause the SVG DOM property // synchronization to kick in, which causes removeAllClientsFromCache() to be called, which in turn deletes our // GradientData object! Leaving out the line below will cause svg/dynamic-updates/SVG*GradientElement-svgdom* to crash. SVGGradientElement* gradientElement = toSVGGradientElement(element()); if (!gradientElement) return false; if (m_shouldCollectGradientAttributes) { gradientElement->synchronizeAnimatedSVGAttribute(anyQName()); if (!collectGradientAttributes(gradientElement)) return false; m_shouldCollectGradientAttributes = false; } // 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 = object->objectBoundingBox(); if (gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty()) return false; OwnPtr<GradientData>& gradientData = m_gradientMap.add(object, nullptr).iterator->value; if (!gradientData) gradientData = adoptPtr(new GradientData); bool isPaintingText = resourceMode & ApplyToTextMode; // Create gradient object if (!gradientData->gradient) { buildGradient(gradientData.get()); // We want the text bounding box applied to the gradient space transform now, so the gradient shader can use it. if (gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && !objectBoundingBox.isEmpty()) { gradientData->userspaceTransform.translate(objectBoundingBox.x(), objectBoundingBox.y()); gradientData->userspaceTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); } AffineTransform gradientTransform; calculateGradientTransform(gradientTransform); gradientData->userspaceTransform *= gradientTransform; if (isPaintingText) { // Depending on font scaling factor, we may need to rescale the gradient here since // text painting removes the scale factor from the context. AffineTransform additionalTextTransform; if (shouldTransformOnTextPainting(object, additionalTextTransform)) gradientData->userspaceTransform *= additionalTextTransform; } gradientData->gradient->setGradientSpaceTransform(gradientData->userspaceTransform); } if (!gradientData->gradient) return false; // Draw gradient context->save(); if (isPaintingText) context->setTextDrawingMode(resourceMode & ApplyToFillMode ? TextModeFill : TextModeStroke); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); if (resourceMode & ApplyToFillMode) { context->setAlpha(svgStyle->fillOpacity()); context->setFillGradient(gradientData->gradient); context->setFillRule(svgStyle->fillRule()); } else if (resourceMode & ApplyToStrokeMode) { if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE) gradientData->gradient->setGradientSpaceTransform(transformOnNonScalingStroke(object, gradientData->userspaceTransform)); context->setAlpha(svgStyle->strokeOpacity()); context->setStrokeGradient(gradientData->gradient); SVGRenderSupport::applyStrokeStyleToContext(context, style, object); } return true; }