std::unique_ptr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes, const FloatRect& tileBoundaries, const FloatRect& absoluteTileBoundaries, const AffineTransform& tileImageTransform, FloatRect& clampedAbsoluteTileBoundaries, RenderingMode renderingMode) const { clampedAbsoluteTileBoundaries = ImageBuffer::clampedRect(absoluteTileBoundaries); auto tileImage = SVGRenderingContext::createImageBuffer(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, ColorSpaceDeviceRGB, renderingMode); if (!tileImage) return nullptr; GraphicsContext& tileImageContext = tileImage->context(); // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation). tileImageContext.scale(FloatSize(clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(), clampedAbsoluteTileBoundaries.height() / tileBoundaries.height())); // Apply tile image transformations. if (!tileImageTransform.isIdentity()) tileImageContext.concatCTM(tileImageTransform); AffineTransform contentTransformation; if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) contentTransformation = tileImageTransform; // Draw the content into the ImageBuffer. for (auto& child : childrenOfType<SVGElement>(*attributes.patternContentElement())) { if (!child.renderer()) continue; if (child.renderer()->needsLayout()) return nullptr; SVGRenderingContext::renderSubtreeToImageBuffer(tileImage.get(), *child.renderer(), contentTransformation); } return tileImage; }
FloatRect RenderSVGResourcePattern::calculatePatternBoundariesIncludingOverflow(PatternAttributes& attributes, const FloatRect& objectBoundingBox, const AffineTransform& viewBoxCTM, const FloatRect& patternBoundaries) const { // Eventually calculate the pattern content boundaries (only needed with overflow="visible"). FloatRect patternContentBoundaries; const RenderStyle* style = this->style(); if (style->overflowX() == OVISIBLE && style->overflowY() == OVISIBLE) { for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) { if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyledTransformable() || !node->renderer()) continue; patternContentBoundaries.unite(node->renderer()->repaintRectInLocalCoordinates()); } } if (patternContentBoundaries.isEmpty()) return patternBoundaries; FloatRect patternBoundariesIncludingOverflow = patternBoundaries; // Respect objectBoundingBoxMode for patternContentUnits, if viewBox is not set. if (!viewBoxCTM.isIdentity()) patternContentBoundaries = viewBoxCTM.mapRect(patternContentBoundaries); else if (attributes.boundingBoxModeContent()) patternContentBoundaries = FloatRect(patternContentBoundaries.x() * objectBoundingBox.width(), patternContentBoundaries.y() * objectBoundingBox.height(), patternContentBoundaries.width() * objectBoundingBox.width(), patternContentBoundaries.height() * objectBoundingBox.height()); patternBoundariesIncludingOverflow.unite(patternContentBoundaries); return patternBoundariesIncludingOverflow; }
static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes, const FloatRect& objectBoundingBox, const SVGPatternElement* patternElement) { ASSERT(patternElement); return SVGLengthContext::resolveRectangle(patternElement, attributes.patternUnits(), objectBoundingBox, attributes.x(), attributes.y(), attributes.width(), attributes.height()); }
PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(RenderObject* object, const PatternAttributes& attributes, const FloatRect& tileBoundaries, const FloatRect& absoluteTileBoundaries, const AffineTransform& tileImageTransform) const { ASSERT(object); // Clamp tile image size against SVG viewport size, as last resort, to avoid allocating huge image buffers. FloatRect contentBoxRect = SVGRenderSupport::findTreeRootObject(object)->contentBoxRect(); FloatRect clampedAbsoluteTileBoundaries = absoluteTileBoundaries; if (clampedAbsoluteTileBoundaries.width() > contentBoxRect.width()) clampedAbsoluteTileBoundaries.setWidth(contentBoxRect.width()); if (clampedAbsoluteTileBoundaries.height() > contentBoxRect.height()) clampedAbsoluteTileBoundaries.setHeight(contentBoxRect.height()); OwnPtr<ImageBuffer> tileImage; if (!SVGImageBufferTools::createImageBuffer(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, ColorSpaceDeviceRGB)) return PassOwnPtr<ImageBuffer>(); GraphicsContext* tileImageContext = tileImage->context(); ASSERT(tileImageContext); // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation). tileImageContext->scale(FloatSize(absoluteTileBoundaries.width() / tileBoundaries.width(), absoluteTileBoundaries.height() / tileBoundaries.height())); // Apply tile image transformations. if (!tileImageTransform.isIdentity()) tileImageContext->concatCTM(tileImageTransform); AffineTransform contentTransformation; if (attributes.boundingBoxModeContent()) contentTransformation = tileImageTransform; // Draw the content into the ImageBuffer. for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) { if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer()) continue; SVGImageBufferTools::renderSubtreeToImageBuffer(tileImage.get(), node->renderer(), contentTransformation); } return tileImage.release(); }
static inline FloatRect calculatePatternBoundaries(PatternAttributes& attributes, const FloatRect& objectBoundingBox, const SVGPatternElement* patternElement) { if (attributes.boundingBoxMode()) return FloatRect(attributes.x().valueAsPercentage() * objectBoundingBox.width(), attributes.y().valueAsPercentage() * objectBoundingBox.height(), attributes.width().valueAsPercentage() * objectBoundingBox.width(), attributes.height().valueAsPercentage() * objectBoundingBox.height()); return FloatRect(attributes.x().value(patternElement), attributes.y().value(patternElement), attributes.width().value(patternElement), attributes.height().value(patternElement)); }
PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes, const FloatRect& tileBoundaries, const FloatRect& absoluteTileBoundaries, const AffineTransform& tileImageTransform) const { FloatRect clampedAbsoluteTileBoundaries = SVGRenderingContext::clampedAbsoluteTargetRect(absoluteTileBoundaries); IntSize imageSize(roundedIntSize(clampedAbsoluteTileBoundaries.size())); if (imageSize.isEmpty()) return nullptr; OwnPtr<ImageBuffer> tileImage = ImageBuffer::create(imageSize); if (!tileImage) return nullptr; GraphicsContext* tileImageContext = tileImage->context(); ASSERT(tileImageContext); IntSize unclampedImageSize(roundedIntSize(absoluteTileBoundaries.size())); tileImageContext->scale(unclampedImageSize.width() / absoluteTileBoundaries.width(), unclampedImageSize.height() / absoluteTileBoundaries.height()); // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation). tileImageContext->scale( clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(), clampedAbsoluteTileBoundaries.height() / tileBoundaries.height()); // Apply tile image transformations. if (!tileImageTransform.isIdentity()) tileImageContext->concatCTM(tileImageTransform); AffineTransform contentTransformation; if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) contentTransformation = tileImageTransform; // Draw the content into the ImageBuffer. for (Element* element = ElementTraversal::firstWithin(*attributes.patternContentElement()); element; element = ElementTraversal::nextSibling(*element)) { if (!element->isSVGElement() || !element->renderer()) continue; if (element->renderer()->needsLayout()) return nullptr; SVGRenderingContext::renderSubtree(tileImage->context(), element->renderer(), contentTransformation); } return tileImage.release(); }
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; }
PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes, const FloatRect& tileBoundaries, const FloatRect& absoluteTileBoundaries, const AffineTransform& tileImageTransform, FloatRect& clampedAbsoluteTileBoundaries) const { clampedAbsoluteTileBoundaries = SVGRenderingContext::clampedAbsoluteTargetRect(absoluteTileBoundaries); OwnPtr<ImageBuffer> tileImage; if (!SVGRenderingContext::createImageBufferForPattern(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, ColorSpaceDeviceRGB, Unaccelerated)) return nullptr; GraphicsContext* tileImageContext = tileImage->context(); ASSERT(tileImageContext); // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation). tileImageContext->scale(FloatSize(clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(), clampedAbsoluteTileBoundaries.height() / tileBoundaries.height())); // Apply tile image transformations. if (!tileImageTransform.isIdentity()) tileImageContext->concatCTM(tileImageTransform); AffineTransform contentTransformation; if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) contentTransformation = tileImageTransform; // Draw the content into the ImageBuffer. for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) { if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer()) continue; if (node->renderer()->needsLayout()) return nullptr; SVGRenderingContext::renderSubtreeToImageBuffer(tileImage.get(), node->renderer(), contentTransformation); } return tileImage.release(); }
std::unique_ptr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes, const FloatRect& tileBoundaries, const FloatRect& absoluteTileBoundaries, const AffineTransform& tileImageTransform, FloatRect& clampedAbsoluteTileBoundaries) const { clampedAbsoluteTileBoundaries = SVGRenderingContext::clampedAbsoluteTargetRect(absoluteTileBoundaries); std::unique_ptr<ImageBuffer> tileImage; if (!SVGRenderingContext::createImageBufferForPattern(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, ColorSpaceDeviceRGB, Unaccelerated)) return nullptr; GraphicsContext* tileImageContext = tileImage->context(); ASSERT(tileImageContext); // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation). tileImageContext->scale(FloatSize(clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(), clampedAbsoluteTileBoundaries.height() / tileBoundaries.height())); // Apply tile image transformations. if (!tileImageTransform.isIdentity()) tileImageContext->concatCTM(tileImageTransform); AffineTransform contentTransformation; if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) contentTransformation = tileImageTransform; // Draw the content into the ImageBuffer. auto children = childrenOfType<SVGElement>(*attributes.patternContentElement()); for (auto it = children.begin(), end = children.end(); it != end; ++it) { const SVGElement& child = *it; if (!child.renderer()) continue; if (child.renderer()->needsLayout()) return nullptr; SVGRenderingContext::renderSubtreeToImageBuffer(tileImage.get(), *child.renderer(), contentTransformation); } return tileImage; }
bool RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer, const PatternAttributes& attributes, const SVGPatternElement* patternElement, FloatRect& patternBoundaries, AffineTransform& tileImageTransform) const { ASSERT(renderer); ASSERT(patternElement); FloatRect objectBoundingBox = renderer->objectBoundingBox(); patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement); if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0) return false; AffineTransform viewBoxCTM = patternElement->viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height()); // Apply viewBox/objectBoundingBox transformations. if (!viewBoxCTM.isIdentity()) tileImageTransform = viewBoxCTM; else if (attributes.boundingBoxModeContent()) tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height()); return true; }
PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(PatternData* patternData, const SVGPatternElement* patternElement, RenderObject* object) const { PatternAttributes attributes = patternElement->collectPatternProperties(); // If we couldn't determine the pattern content element root, stop here. if (!attributes.patternContentElement()) return 0; FloatRect objectBoundingBox = object->objectBoundingBox(); FloatRect patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement); AffineTransform patternTransform = attributes.patternTransform(); AffineTransform viewBoxCTM = patternElement->viewBoxToViewTransform(patternElement->viewBox(), patternElement->preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height()); FloatRect patternBoundariesIncludingOverflow = calculatePatternBoundariesIncludingOverflow(attributes, objectBoundingBox, viewBoxCTM, patternBoundaries); IntSize imageSize(lroundf(patternBoundariesIncludingOverflow.width()), lroundf(patternBoundariesIncludingOverflow.height())); // FIXME: We should be able to clip this more, needs investigation clampImageBufferSizeToViewport(object->document()->view(), imageSize); // Don't create ImageBuffers with image size of 0 if (imageSize.isEmpty()) return 0; OwnPtr<ImageBuffer> tileImage = ImageBuffer::create(imageSize); GraphicsContext* context = tileImage->context(); ASSERT(context); context->save(); // Translate to pattern start origin if (patternBoundariesIncludingOverflow.location() != patternBoundaries.location()) { context->translate(patternBoundaries.x() - patternBoundariesIncludingOverflow.x(), patternBoundaries.y() - patternBoundariesIncludingOverflow.y()); patternBoundaries.setLocation(patternBoundariesIncludingOverflow.location()); } // Process viewBox or boundingBoxModeContent correction if (!viewBoxCTM.isIdentity()) context->concatCTM(viewBoxCTM); else if (attributes.boundingBoxModeContent()) { context->translate(objectBoundingBox.x(), objectBoundingBox.y()); context->scale(FloatSize(objectBoundingBox.width(), objectBoundingBox.height())); } // Render subtree into ImageBuffer for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) { if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer()) continue; renderSubtreeToImage(tileImage.get(), node->renderer()); } patternData->boundaries = patternBoundaries; // Compute pattern transformation patternData->transform.translate(patternBoundaries.x(), patternBoundaries.y()); patternData->transform.multiply(patternTransform); context->restore(); return tileImage.release(); }
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; }
static void setPatternAttributes(const SVGPatternElement* element, PatternAttributes& attributes) { if (!attributes.hasX() && element->x()->isSpecified()) attributes.setX(element->x()->currentValue()); if (!attributes.hasY() && element->y()->isSpecified()) attributes.setY(element->y()->currentValue()); if (!attributes.hasWidth() && element->width()->isSpecified()) attributes.setWidth(element->width()->currentValue()); if (!attributes.hasHeight() && element->height()->isSpecified()) attributes.setHeight(element->height()->currentValue()); if (!attributes.hasViewBox() && element->viewBox()->isSpecified() && element->viewBox()->currentValue()->isValid()) attributes.setViewBox(element->viewBox()->currentValue()->value()); if (!attributes.hasPreserveAspectRatio() && element->preserveAspectRatio()->isSpecified()) attributes.setPreserveAspectRatio(element->preserveAspectRatio()->currentValue()); if (!attributes.hasPatternUnits() && element->patternUnits()->isSpecified()) attributes.setPatternUnits(element->patternUnits()->currentValue()->enumValue()); if (!attributes.hasPatternContentUnits() && element->patternContentUnits()->isSpecified()) attributes.setPatternContentUnits(element->patternContentUnits()->currentValue()->enumValue()); if (!attributes.hasPatternTransform() && element->patternTransform()->isSpecified()) { AffineTransform transform; element->patternTransform()->currentValue()->concatenate(transform); attributes.setPatternTransform(transform); } if (!attributes.hasPatternContentElement() && ElementTraversal::firstWithin(*element)) attributes.setPatternContentElement(element); }
void SVGPatternElement::collectPatternAttributes(PatternAttributes& attributes) const { if (!attributes.hasX() && hasAttribute(SVGNames::xAttr)) attributes.setX(x()); if (!attributes.hasY() && hasAttribute(SVGNames::yAttr)) attributes.setY(y()); if (!attributes.hasWidth() && hasAttribute(SVGNames::widthAttr)) attributes.setWidth(width()); if (!attributes.hasHeight() && hasAttribute(SVGNames::heightAttr)) attributes.setHeight(height()); if (!attributes.hasViewBox() && hasAttribute(SVGNames::viewBoxAttr) && viewBoxIsValid()) attributes.setViewBox(viewBox()); if (!attributes.hasPreserveAspectRatio() && hasAttribute(SVGNames::preserveAspectRatioAttr)) attributes.setPreserveAspectRatio(preserveAspectRatio()); if (!attributes.hasPatternUnits() && hasAttribute(SVGNames::patternUnitsAttr)) attributes.setPatternUnits(patternUnits()); if (!attributes.hasPatternContentUnits() && hasAttribute(SVGNames::patternContentUnitsAttr)) attributes.setPatternContentUnits(patternContentUnits()); if (!attributes.hasPatternTransform() && hasAttribute(SVGNames::patternTransformAttr)) { AffineTransform transform; patternTransform().concatenate(transform); attributes.setPatternTransform(transform); } if (!attributes.hasPatternContentElement() && childElementCount()) attributes.setPatternContentElement(this); }
static void setPatternAttributes(const SVGPatternElement& element, PatternAttributes& attributes) { if (!attributes.hasX() && element.hasAttribute(SVGNames::xAttr)) attributes.setX(element.x()); if (!attributes.hasY() && element.hasAttribute(SVGNames::yAttr)) attributes.setY(element.y()); if (!attributes.hasWidth() && element.hasAttribute(SVGNames::widthAttr)) attributes.setWidth(element.width()); if (!attributes.hasHeight() && element.hasAttribute(SVGNames::heightAttr)) attributes.setHeight(element.height()); if (!attributes.hasViewBox() && element.hasAttribute(SVGNames::viewBoxAttr) && element.viewBoxIsValid()) attributes.setViewBox(element.viewBox()); if (!attributes.hasPreserveAspectRatio() && element.hasAttribute(SVGNames::preserveAspectRatioAttr)) attributes.setPreserveAspectRatio(element.preserveAspectRatio()); if (!attributes.hasPatternUnits() && element.hasAttribute(SVGNames::patternUnitsAttr)) attributes.setPatternUnits(element.patternUnits()); if (!attributes.hasPatternContentUnits() && element.hasAttribute(SVGNames::patternContentUnitsAttr)) attributes.setPatternContentUnits(element.patternContentUnits()); if (!attributes.hasPatternTransform() && element.hasAttribute(SVGNames::patternTransformAttr)) { AffineTransform transform; element.patternTransform().concatenate(transform); attributes.setPatternTransform(transform); } if (!attributes.hasPatternContentElement() && element.childElementCount()) attributes.setPatternContentElement(&element); }
void writeSVGResourceContainer(TextStream& ts, const RenderObject& object, int indent) { writeStandardPrefix(ts, object, indent); Element* element = static_cast<Element*>(object.node()); const AtomicString& id = element->getIdAttribute(); writeNameAndQuotedValue(ts, "id", id); RenderSVGResourceContainer* resource = const_cast<RenderObject&>(object).toRenderSVGResourceContainer(); ASSERT(resource); if (resource->resourceType() == MaskerResourceType) { RenderSVGResourceMasker* masker = static_cast<RenderSVGResourceMasker*>(resource); writeNameValuePair(ts, "maskUnits", masker->maskUnits()); writeNameValuePair(ts, "maskContentUnits", masker->maskContentUnits()); ts << "\n"; #if ENABLE(FILTERS) } else if (resource->resourceType() == FilterResourceType) { RenderSVGResourceFilter* filter = static_cast<RenderSVGResourceFilter*>(resource); writeNameValuePair(ts, "filterUnits", filter->filterUnits()); writeNameValuePair(ts, "primitiveUnits", filter->primitiveUnits()); ts << "\n"; if (RefPtr<SVGFilterBuilder> builder = filter->buildPrimitives()) { if (FilterEffect* lastEffect = builder->lastEffect()) lastEffect->externalRepresentation(ts, indent + 1); } #endif } else if (resource->resourceType() == ClipperResourceType) { RenderSVGResourceClipper* clipper = static_cast<RenderSVGResourceClipper*>(resource); writeNameValuePair(ts, "clipPathUnits", clipper->clipPathUnits()); ts << "\n"; } else if (resource->resourceType() == MarkerResourceType) { RenderSVGResourceMarker* marker = static_cast<RenderSVGResourceMarker*>(resource); writeNameValuePair(ts, "markerUnits", marker->markerUnits()); ts << " [ref at " << marker->referencePoint() << "]"; ts << " [angle="; if (marker->angle() == -1) ts << "auto" << "]\n"; else ts << marker->angle() << "]\n"; } else if (resource->resourceType() == PatternResourceType) { RenderSVGResourcePattern* pattern = static_cast<RenderSVGResourcePattern*>(resource); // Dump final results that are used for rendering. No use in asking SVGPatternElement for its patternUnits(), as it may // link to other patterns using xlink:href, we need to build the full inheritance chain, aka. collectPatternProperties() PatternAttributes attributes = static_cast<SVGPatternElement*>(pattern->node())->collectPatternProperties(); writeNameValuePair(ts, "patternUnits", boundingBoxModeString(attributes.boundingBoxMode())); writeNameValuePair(ts, "patternContentUnits", boundingBoxModeString(attributes.boundingBoxModeContent())); AffineTransform transform = attributes.patternTransform(); if (!transform.isIdentity()) ts << " [patternTransform=" << transform << "]"; ts << "\n"; } else if (resource->resourceType() == LinearGradientResourceType) { RenderSVGResourceLinearGradient* gradient = static_cast<RenderSVGResourceLinearGradient*>(resource); // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties() SVGLinearGradientElement* linearGradientElement = static_cast<SVGLinearGradientElement*>(gradient->node()); LinearGradientAttributes attributes = linearGradientElement->collectGradientProperties(); writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.boundingBoxMode()); FloatPoint startPoint; FloatPoint endPoint; linearGradientElement->calculateStartEndPoints(attributes, startPoint, endPoint); ts << " [start=" << startPoint << "] [end=" << endPoint << "]\n"; } else if (resource->resourceType() == RadialGradientResourceType) { RenderSVGResourceRadialGradient* gradient = static_cast<RenderSVGResourceRadialGradient*>(resource); // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties() SVGRadialGradientElement* radialGradientElement = static_cast<SVGRadialGradientElement*>(gradient->node()); RadialGradientAttributes attributes = radialGradientElement->collectGradientProperties(); writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.boundingBoxMode()); FloatPoint focalPoint; FloatPoint centerPoint; float radius; radialGradientElement->calculateFocalCenterPointsAndRadius(attributes, focalPoint, centerPoint, radius); ts << " [center=" << centerPoint << "] [focal=" << focalPoint << "] [radius=" << radius << "]\n"; } else ts << "\n"; writeChildren(ts, object, indent); }
void writeSVGResourceContainer(TextStream& ts, const RenderObject& object, int indent) { writeStandardPrefix(ts, object, indent); Element* element = toElement(object.node()); const AtomicString& id = element->getIdAttribute(); writeNameAndQuotedValue(ts, "id", id); RenderSVGResourceContainer* resource = const_cast<RenderObject&>(object).toRenderSVGResourceContainer(); ASSERT(resource); if (resource->resourceType() == MaskerResourceType) { RenderSVGResourceMasker* masker = toRenderSVGResourceMasker(resource); writeNameValuePair(ts, "maskUnits", masker->maskUnits()); writeNameValuePair(ts, "maskContentUnits", masker->maskContentUnits()); ts << "\n"; } else if (resource->resourceType() == FilterResourceType) { RenderSVGResourceFilter* filter = toRenderSVGResourceFilter(resource); writeNameValuePair(ts, "filterUnits", filter->filterUnits()); writeNameValuePair(ts, "primitiveUnits", filter->primitiveUnits()); ts << "\n"; // Creating a placeholder filter which is passed to the builder. FloatRect dummyRect; RefPtr<SVGFilter> dummyFilter = SVGFilter::create(AffineTransform(), dummyRect, dummyRect, dummyRect, true); if (RefPtr<SVGFilterBuilder> builder = filter->buildPrimitives(dummyFilter.get())) { if (FilterEffect* lastEffect = builder->lastEffect()) lastEffect->externalRepresentation(ts, indent + 1); } } else if (resource->resourceType() == ClipperResourceType) { writeNameValuePair(ts, "clipPathUnits", toRenderSVGResourceClipper(resource)->clipPathUnits()); ts << "\n"; } else if (resource->resourceType() == MarkerResourceType) { RenderSVGResourceMarker* marker = toRenderSVGResourceMarker(resource); writeNameValuePair(ts, "markerUnits", marker->markerUnits()); ts << " [ref at " << marker->referencePoint() << "]"; ts << " [angle="; if (marker->angle() == -1) ts << "auto" << "]\n"; else ts << marker->angle() << "]\n"; } else if (resource->resourceType() == PatternResourceType) { RenderSVGResourcePattern* pattern = static_cast<RenderSVGResourcePattern*>(resource); // Dump final results that are used for rendering. No use in asking SVGPatternElement for its patternUnits(), as it may // link to other patterns using xlink:href, we need to build the full inheritance chain, aka. collectPatternProperties() PatternAttributes attributes; toSVGPatternElement(pattern->element())->collectPatternAttributes(attributes); writeNameValuePair(ts, "patternUnits", attributes.patternUnits()); writeNameValuePair(ts, "patternContentUnits", attributes.patternContentUnits()); AffineTransform transform = attributes.patternTransform(); if (!transform.isIdentity()) ts << " [patternTransform=" << transform << "]"; ts << "\n"; } else if (resource->resourceType() == LinearGradientResourceType) { RenderSVGResourceLinearGradient* gradient = static_cast<RenderSVGResourceLinearGradient*>(resource); // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties() LinearGradientAttributes attributes; toSVGLinearGradientElement(gradient->element())->collectGradientAttributes(attributes); writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.gradientUnits()); ts << " [start=" << gradient->startPoint(attributes) << "] [end=" << gradient->endPoint(attributes) << "]\n"; } else if (resource->resourceType() == RadialGradientResourceType) { RenderSVGResourceRadialGradient* gradient = toRenderSVGResourceRadialGradient(resource); // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties() RadialGradientAttributes attributes; toSVGRadialGradientElement(gradient->element())->collectGradientAttributes(attributes); writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.gradientUnits()); FloatPoint focalPoint = gradient->focalPoint(attributes); FloatPoint centerPoint = gradient->centerPoint(attributes); float radius = gradient->radius(attributes); float focalRadius = gradient->focalRadius(attributes); ts << " [center=" << centerPoint << "] [focal=" << focalPoint << "] [radius=" << radius << "] [focalRadius=" << focalRadius << "]\n"; } else ts << "\n"; writeChildren(ts, object, indent); }
void SVGPatternElement::collectPatternAttributes(PatternAttributes& attributes) const { 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.hasViewBox() && current->hasAttribute(SVGNames::viewBoxAttr)) attributes.setViewBox(current->viewBox()); if (!attributes.hasPreserveAspectRatio() && current->hasAttribute(SVGNames::preserveAspectRatioAttr)) attributes.setPreserveAspectRatio(current->preserveAspectRatio()); 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)) { AffineTransform transform; current->patternTransform().concatenate(transform); attributes.setPatternTransform(transform); } 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)) { current = 0; break; } } else current = 0; } }