SVGPaintServer LayoutSVGResourcePattern::preparePaintServer(const LayoutObject& object) { clearInvalidationMask(); SVGPatternElement* patternElement = toSVGPatternElement(element()); if (!patternElement) return SVGPaintServer::invalid(); if (m_shouldCollectPatternAttributes) { patternElement->synchronizeAnimatedSVGAttribute(anyQName()); #if ENABLE(OILPAN) m_attributesWrapper->set(PatternAttributes()); #else m_attributes = PatternAttributes(); #endif patternElement->collectPatternAttributes(mutableAttributes()); 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 (attributes().patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty()) return SVGPaintServer::invalid(); PatternData* patternData = patternForLayoutObject(object); if (!patternData || !patternData->pattern) return SVGPaintServer::invalid(); patternData->pattern->setPatternSpaceTransform(patternData->transform); return SVGPaintServer(patternData->pattern); }
PatternAttributes SVGPatternElement::collectPatternProperties() const { PatternAttributes attributes; HashSet<const SVGPatternElement*> processedPatterns; const SVGPatternElement* current = this; while (current) { if (!attributes.hasX() && current->hasAttribute(SVGNames::xAttr)) attributes.setX(current->x()); if (!attributes.hasY() && current->hasAttribute(SVGNames::yAttr)) attributes.setY(current->y()); if (!attributes.hasWidth() && current->hasAttribute(SVGNames::widthAttr)) attributes.setWidth(current->width()); if (!attributes.hasHeight() && current->hasAttribute(SVGNames::heightAttr)) attributes.setHeight(current->height()); if (!attributes.hasBoundingBoxMode() && current->hasAttribute(SVGNames::patternUnitsAttr)) attributes.setBoundingBoxMode(current->patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX); if (!attributes.hasBoundingBoxModeContent() && current->hasAttribute(SVGNames::patternContentUnitsAttr)) attributes.setBoundingBoxModeContent(current->patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX); if (!attributes.hasPatternTransform() && current->hasAttribute(SVGNames::patternTransformAttr)) attributes.setPatternTransform(current->patternTransform()->consolidate().matrix()); if (!attributes.hasPatternContentElement() && current->hasChildNodes()) attributes.setPatternContentElement(current); processedPatterns.add(current); // Respect xlink:href, take attributes from referenced element Node* refNode = ownerDocument()->getElementById(SVGURIReference::getTarget(current->href())); if (refNode && refNode->hasTagName(SVGNames::patternTag)) { current = static_cast<const SVGPatternElement*>(const_cast<const Node*>(refNode)); // Cycle detection if (processedPatterns.contains(current)) return PatternAttributes(); } else current = 0; } return attributes; }
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() 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; }
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; }
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(); }