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 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() in createTileImage(), may cause the SVG DOM property // synchronization to kick in, which causes invalidateClients() 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; patternElement->updateAnimatedSVGAttribute(anyQName()); if (!m_pattern.contains(object)) m_pattern.set(object, new PatternData); PatternData* patternData = m_pattern.get(object); if (!patternData->pattern) { // Create tile image OwnPtr<ImageBuffer> tileImage = createTileImage(patternData, patternElement, object); if (!tileImage) return false; // Create pattern object buildPattern(patternData, tileImage.release()); if (!patternData->pattern) return false; 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); applyStrokeStyleToContext(context, style, object); } if (resourceMode & ApplyToTextMode) { if (resourceMode & ApplyToFillMode) { context->setTextDrawingMode(cTextFill); #if PLATFORM(CG) context->applyFillPattern(); #endif } else if (resourceMode & ApplyToStrokeMode) { context->setTextDrawingMode(cTextStroke); #if PLATFORM(CG) context->applyStrokePattern(); #endif } } return true; }
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 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.boundingBoxMode() && objectBoundingBox.isEmpty()) return false; if (!m_pattern.contains(object)) m_pattern.set(object, new PatternData); PatternData* patternData = m_pattern.get(object); 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 absoluteTransform; SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform); FloatRect absoluteTileBoundaries = absoluteTransform.mapRect(tileBoundaries); // Build tile image. OwnPtr<ImageBuffer> tileImage = createTileImage(object, m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform); if (!tileImage) return false; RefPtr<Image> copiedImage = tileImage->copyImage(); 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() / absoluteTileBoundaries.width(), tileBoundaries.height() / absoluteTileBoundaries.height()); AffineTransform patternTransform = m_attributes.patternTransform(); if (!patternTransform.isIdentity()) patternData->transform = patternTransform * patternData->transform; 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 PLATFORM(CG) context->applyFillPattern(); #endif } else if (resourceMode & ApplyToStrokeMode) { context->setTextDrawingMode(TextModeStroke); #if PLATFORM(CG) context->applyStrokePattern(); #endif } } return true; }
PatternData* RenderSVGResourcePattern::buildPattern(RenderObject* object, unsigned short resourceMode) { ASSERT(object); PatternData* currentData = m_patternMap.get(object); if (currentData && currentData->pattern) return currentData; SVGPatternElement* patternElement = toSVGPatternElement(element()); if (!patternElement) return 0; 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 0; // An empty viewBox disables rendering. if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty()) return 0; // Compute all necessary transformations to build the tile image & the pattern. FloatRect tileBoundaries; AffineTransform tileImageTransform; if (!buildTileImageTransform(object, m_attributes, patternElement, tileBoundaries, tileImageTransform)) return 0; AffineTransform absoluteTransformIgnoringRotation; SVGRenderingContext::calculateDeviceSpaceTransformation(object, absoluteTransformIgnoringRotation); // Ignore 2D rotation, as it doesn't affect the size of the tile. SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation); FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries); // 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); if (!tileImage) return 0; RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore); if (!copiedImage) return 0; // Build pattern. OwnPtr<PatternData> patternData = adoptPtr(new PatternData); patternData->pattern = Pattern::create(copiedImage, true, true); // Compute pattern space transformation. const IntSize tileImageSize = tileImage->size(); 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; // 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(object, patternData.release()).storedValue->value.get(); }