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; }
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(); }
PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes, const FloatRect& tileBoundaries, const FloatRect& absoluteTileBoundaries, const AffineTransform& tileImageTransform, FloatRect& clampedAbsoluteTileBoundaries) const { clampedAbsoluteTileBoundaries = SVGImageBufferTools::clampedAbsoluteTargetRect(absoluteTileBoundaries); OwnPtr<ImageBuffer> tileImage; if (!SVGImageBufferTools::createImageBuffer(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, ColorSpaceDeviceRGB)) 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.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(); }
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(); }
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); }