summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/svg/SVGRenderingContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/svg/SVGRenderingContext.cpp')
-rw-r--r--Source/WebCore/rendering/svg/SVGRenderingContext.cpp232
1 files changed, 120 insertions, 112 deletions
diff --git a/Source/WebCore/rendering/svg/SVGRenderingContext.cpp b/Source/WebCore/rendering/svg/SVGRenderingContext.cpp
index a0ad33016..67c55967b 100644
--- a/Source/WebCore/rendering/svg/SVGRenderingContext.cpp
+++ b/Source/WebCore/rendering/svg/SVGRenderingContext.cpp
@@ -23,25 +23,21 @@
*/
#include "config.h"
-
-#if ENABLE(SVG)
#include "SVGRenderingContext.h"
#include "BasicShapes.h"
#include "Frame.h"
#include "FrameView.h"
-#include "Page.h"
#include "RenderLayer.h"
#include "RenderSVGImage.h"
#include "RenderSVGResourceClipper.h"
#include "RenderSVGResourceFilter.h"
#include "RenderSVGResourceMasker.h"
#include "RenderView.h"
+#include "SVGLengthContext.h"
#include "SVGResources.h"
#include "SVGResourcesCache.h"
-static int kMaxImageBufferSize = 4096;
-
namespace WebCore {
static inline bool isRenderingMaskImage(const RenderObject& object)
@@ -57,23 +53,22 @@ SVGRenderingContext::~SVGRenderingContext()
ASSERT(m_renderer && m_paintInfo);
-#if ENABLE(FILTERS)
if (m_renderingFlags & EndFilterLayer) {
ASSERT(m_filter);
- m_filter->postApplyResource(*m_renderer, m_paintInfo->context, ApplyToDefaultMode, 0, 0);
- m_paintInfo->context = m_savedContext;
+ GraphicsContext* contextPtr = &m_paintInfo->context();
+ m_filter->postApplyResource(*m_renderer, contextPtr, ApplyToDefaultMode, 0, 0);
+ m_paintInfo->setContext(*m_savedContext);
m_paintInfo->rect = m_savedPaintRect;
}
-#endif
if (m_renderingFlags & EndOpacityLayer)
- m_paintInfo->context->endTransparencyLayer();
+ m_paintInfo->context().endTransparencyLayer();
if (m_renderingFlags & EndShadowLayer)
- m_paintInfo->context->endTransparencyLayer();
+ m_paintInfo->context().endTransparencyLayer();
if (m_renderingFlags & RestoreGraphicsContext)
- m_paintInfo->context->restore();
+ m_paintInfo->context().restore();
}
void SVGRenderingContext::prepareToRenderSVGContent(RenderElement& renderer, PaintInfo& paintInfo, NeedsGraphicsContextSave needsGraphicsContextSave)
@@ -86,80 +81,115 @@ void SVGRenderingContext::prepareToRenderSVGContent(RenderElement& renderer, Pai
m_renderer = &renderer;
m_paintInfo = &paintInfo;
-#if ENABLE(FILTERS)
m_filter = 0;
-#endif
// We need to save / restore the context even if the initialization failed.
if (needsGraphicsContextSave == SaveGraphicsContext) {
- m_paintInfo->context->save();
+ m_paintInfo->context().save();
m_renderingFlags |= RestoreGraphicsContext;
}
- RenderStyle& style = m_renderer->style();
+ auto& style = m_renderer->style();
const SVGRenderStyle& svgStyle = style.svgStyle();
// Setup transparency layers before setting up SVG resources!
bool isRenderingMask = isRenderingMaskImage(*m_renderer);
- float opacity = isRenderingMask ? 1 : style.opacity();
+ // RenderLayer takes care of root opacity.
+ float opacity = (renderer.isSVGRoot() || isRenderingMask) ? 1 : style.opacity();
const ShadowData* shadow = svgStyle.shadow();
- if (opacity < 1 || shadow) {
+ bool hasBlendMode = style.hasBlendMode();
+ bool hasIsolation = style.hasIsolation();
+ bool isolateMaskForBlending = false;
+
+#if ENABLE(CSS_COMPOSITING)
+ if (svgStyle.hasMasker() && is<SVGGraphicsElement>(downcast<SVGElement>(*renderer.element()))) {
+ SVGGraphicsElement& graphicsElement = downcast<SVGGraphicsElement>(*renderer.element());
+ isolateMaskForBlending = graphicsElement.shouldIsolateBlending();
+ }
+#endif
+
+ if (opacity < 1 || shadow || hasBlendMode || isolateMaskForBlending || hasIsolation) {
FloatRect repaintRect = m_renderer->repaintRectInLocalCoordinates();
+ m_paintInfo->context().clip(repaintRect);
+
+ if (opacity < 1 || hasBlendMode || isolateMaskForBlending || hasIsolation) {
+
+ if (hasBlendMode)
+ m_paintInfo->context().setCompositeOperation(m_paintInfo->context().compositeOperation(), style.blendMode());
+
+ m_paintInfo->context().beginTransparencyLayer(opacity);
+
+ if (hasBlendMode)
+ m_paintInfo->context().setCompositeOperation(m_paintInfo->context().compositeOperation(), BlendModeNormal);
- if (opacity < 1) {
- m_paintInfo->context->clip(repaintRect);
- m_paintInfo->context->beginTransparencyLayer(opacity);
m_renderingFlags |= EndOpacityLayer;
}
if (shadow) {
- m_paintInfo->context->clip(repaintRect);
- m_paintInfo->context->setShadow(IntSize(roundToInt(shadow->x()), roundToInt(shadow->y())), shadow->radius(), shadow->color(), style.colorSpace());
- m_paintInfo->context->beginTransparencyLayer(1);
+ m_paintInfo->context().setShadow(IntSize(roundToInt(shadow->x()), roundToInt(shadow->y())), shadow->radius(), shadow->color());
+ m_paintInfo->context().beginTransparencyLayer(1);
m_renderingFlags |= EndShadowLayer;
}
}
ClipPathOperation* clipPathOperation = style.clipPath();
- if (clipPathOperation && clipPathOperation->type() == ClipPathOperation::Shape) {
- ShapeClipPathOperation* clipPath = static_cast<ShapeClipPathOperation*>(clipPathOperation);
- m_paintInfo->context->clipPath(clipPath->pathForReferenceRect(renderer.objectBoundingBox()), clipPath->windRule());
+ if (is<ShapeClipPathOperation>(clipPathOperation)) {
+ auto& clipPath = downcast<ShapeClipPathOperation>(*clipPathOperation);
+ FloatRect referenceBox;
+ if (clipPath.referenceBox() == Stroke)
+ // FIXME: strokeBoundingBox() takes dasharray into account but shouldn't.
+ referenceBox = renderer.strokeBoundingBox();
+ else if (clipPath.referenceBox() == ViewBox && renderer.element()) {
+ FloatSize viewportSize;
+ SVGLengthContext(downcast<SVGElement>(renderer.element())).determineViewport(viewportSize);
+ referenceBox.setWidth(viewportSize.width());
+ referenceBox.setHeight(viewportSize.height());
+ } else
+ referenceBox = renderer.objectBoundingBox();
+ m_paintInfo->context().clipPath(clipPath.pathForReferenceRect(referenceBox), clipPath.windRule());
}
- SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(*m_renderer);
+ auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*m_renderer);
if (!resources) {
-#if ENABLE(FILTERS)
- if (svgStyle.hasFilter())
+ if (style.hasReferenceFilterOnly())
return;
-#endif
+
m_renderingFlags |= RenderingPrepared;
return;
}
if (!isRenderingMask) {
if (RenderSVGResourceMasker* masker = resources->masker()) {
- if (!masker->applyResource(*m_renderer, style, m_paintInfo->context, ApplyToDefaultMode))
+ GraphicsContext* contextPtr = &m_paintInfo->context();
+ bool result = masker->applyResource(*m_renderer, style, contextPtr, ApplyToDefaultMode);
+ m_paintInfo->setContext(*contextPtr);
+ if (!result)
return;
}
}
RenderSVGResourceClipper* clipper = resources->clipper();
if (!clipPathOperation && clipper) {
- if (!clipper->applyResource(*m_renderer, style, m_paintInfo->context, ApplyToDefaultMode))
+ GraphicsContext* contextPtr = &m_paintInfo->context();
+ bool result = clipper->applyResource(*m_renderer, style, contextPtr, ApplyToDefaultMode);
+ m_paintInfo->setContext(*contextPtr);
+ if (!result)
return;
}
-#if ENABLE(FILTERS)
if (!isRenderingMask) {
m_filter = resources->filter();
if (m_filter) {
- m_savedContext = m_paintInfo->context;
+ m_savedContext = &m_paintInfo->context();
m_savedPaintRect = m_paintInfo->rect;
// Return with false here may mean that we don't need to draw the content
// (because it was either drawn before or empty) but we still need to apply the filter.
m_renderingFlags |= EndFilterLayer;
- if (!m_filter->applyResource(*m_renderer, style, m_paintInfo->context, ApplyToDefaultMode))
+ GraphicsContext* contextPtr = &m_paintInfo->context();
+ bool result = m_filter->applyResource(*m_renderer, style, contextPtr, ApplyToDefaultMode);
+ m_paintInfo->setContext(*contextPtr);
+ if (!result)
return;
// Since we're caching the resulting bitmap and do not invalidate it on repaint rect
@@ -169,45 +199,38 @@ void SVGRenderingContext::prepareToRenderSVGContent(RenderElement& renderer, Pai
m_paintInfo->rect = IntRect(m_filter->drawingRegion(m_renderer));
}
}
-#endif
m_renderingFlags |= RenderingPrepared;
}
static AffineTransform& currentContentTransformation()
{
- DEFINE_STATIC_LOCAL(AffineTransform, s_currentContentTransformation, ());
+ static NeverDestroyed<AffineTransform> s_currentContentTransformation;
return s_currentContentTransformation;
}
-float SVGRenderingContext::calculateScreenFontSizeScalingFactor(const RenderObject* renderer)
+float SVGRenderingContext::calculateScreenFontSizeScalingFactor(const RenderObject& renderer)
{
- ASSERT(renderer);
-
- AffineTransform ctm;
- calculateTransformationToOutermostCoordinateSystem(renderer, ctm);
+ AffineTransform ctm = calculateTransformationToOutermostCoordinateSystem(renderer);
return narrowPrecisionToFloat(sqrt((pow(ctm.xScale(), 2) + pow(ctm.yScale(), 2)) / 2));
}
-void SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(const RenderObject* renderer, AffineTransform& absoluteTransform)
+AffineTransform SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(const RenderObject& renderer)
{
- ASSERT(renderer);
- absoluteTransform = currentContentTransformation();
-
- float deviceScaleFactor = 1;
- if (Page* page = renderer->document().page())
- deviceScaleFactor = page->deviceScaleFactor();
+ AffineTransform absoluteTransform = currentContentTransformation();
+ float deviceScaleFactor = renderer.document().deviceScaleFactor();
// Walk up the render tree, accumulating SVG transforms.
- while (renderer) {
- absoluteTransform = renderer->localToParentTransform() * absoluteTransform;
- if (renderer->isSVGRoot())
+ const RenderObject* ancestor = &renderer;
+ while (ancestor) {
+ absoluteTransform = ancestor->localToParentTransform() * absoluteTransform;
+ if (ancestor->isSVGRoot())
break;
- renderer = renderer->parent();
+ ancestor = ancestor->parent();
}
// Continue walking up the layer tree, accumulating CSS transforms.
- RenderLayer* layer = renderer ? renderer->enclosingLayer() : 0;
+ RenderLayer* layer = ancestor ? ancestor->enclosingLayer() : nullptr;
while (layer) {
if (TransformationMatrix* layerTransform = layer->transform())
absoluteTransform = layerTransform->toAffineTransform() * absoluteTransform;
@@ -220,61 +243,60 @@ void SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(con
}
absoluteTransform.scale(deviceScaleFactor);
+ return absoluteTransform;
}
-bool SVGRenderingContext::createImageBuffer(const FloatRect& targetRect, const AffineTransform& absoluteTransform, std::unique_ptr<ImageBuffer>& imageBuffer, ColorSpace colorSpace, RenderingMode renderingMode)
+std::unique_ptr<ImageBuffer> SVGRenderingContext::createImageBuffer(const FloatRect& targetRect, const AffineTransform& absoluteTransform, ColorSpace colorSpace, RenderingMode renderingMode)
{
IntRect paintRect = calculateImageBufferRect(targetRect, absoluteTransform);
// Don't create empty ImageBuffers.
if (paintRect.isEmpty())
- return false;
+ return nullptr;
- IntSize clampedSize = clampedAbsoluteSize(paintRect.size());
- std::unique_ptr<ImageBuffer> image = ImageBuffer::create(clampedSize, 1, colorSpace, renderingMode);
- if (!image)
- return false;
+ FloatSize scale;
+ FloatSize clampedSize = ImageBuffer::clampedSize(paintRect.size(), scale);
- GraphicsContext* imageContext = image->context();
- ASSERT(imageContext);
+ auto imageBuffer = ImageBuffer::create(clampedSize, renderingMode, 1, colorSpace);
+ if (!imageBuffer)
+ return nullptr;
- imageContext->scale(FloatSize(static_cast<float>(clampedSize.width()) / paintRect.width(),
- static_cast<float>(clampedSize.height()) / paintRect.height()));
- imageContext->translate(-paintRect.x(), -paintRect.y());
- imageContext->concatCTM(absoluteTransform);
+ AffineTransform transform;
+ transform.scale(scale).translate(-paintRect.location()).multiply(absoluteTransform);
- imageBuffer = std::move(image);
- return true;
+ GraphicsContext& imageContext = imageBuffer->context();
+ imageContext.concatCTM(transform);
+
+ return imageBuffer;
}
-bool SVGRenderingContext::createImageBufferForPattern(const FloatRect& absoluteTargetRect, const FloatRect& clampedAbsoluteTargetRect, std::unique_ptr<ImageBuffer>& imageBuffer, ColorSpace colorSpace, RenderingMode renderingMode)
+std::unique_ptr<ImageBuffer> SVGRenderingContext::createImageBuffer(const FloatRect& targetRect, const FloatRect& clampedRect, ColorSpace colorSpace, RenderingMode renderingMode)
{
- IntSize imageSize(roundedIntSize(clampedAbsoluteTargetRect.size()));
- IntSize unclampedImageSize(roundedIntSize(absoluteTargetRect.size()));
+ IntSize clampedSize = roundedIntSize(clampedRect.size());
+ IntSize unclampedSize = roundedIntSize(targetRect.size());
// Don't create empty ImageBuffers.
- if (imageSize.isEmpty())
- return false;
+ if (clampedSize.isEmpty())
+ return nullptr;
- std::unique_ptr<ImageBuffer> image = ImageBuffer::create(imageSize, 1, colorSpace, renderingMode);
- if (!image)
- return false;
+ auto imageBuffer = ImageBuffer::create(clampedSize, renderingMode, 1, colorSpace);
+ if (!imageBuffer)
+ return nullptr;
- GraphicsContext* imageContext = image->context();
- ASSERT(imageContext);
+ GraphicsContext& imageContext = imageBuffer->context();
// Compensate rounding effects, as the absolute target rect is using floating-point numbers and the image buffer size is integer.
- imageContext->scale(FloatSize(unclampedImageSize.width() / absoluteTargetRect.width(), unclampedImageSize.height() / absoluteTargetRect.height()));
+ imageContext.scale(FloatSize(unclampedSize.width() / targetRect.width(), unclampedSize.height() / targetRect.height()));
- imageBuffer = std::move(image);
- return true;
+ return imageBuffer;
}
void SVGRenderingContext::renderSubtreeToImageBuffer(ImageBuffer* image, RenderElement& item, const AffineTransform& subtreeContentTransformation)
{
ASSERT(image);
- ASSERT(image->context());
- PaintInfo info(image->context(), IntRect::infiniteRect(), PaintPhaseForeground, PaintBehaviorNormal);
+ // Rendering into a buffer implies we're being used for masking, clipping, patterns or filters. In each of these
+ // cases we don't want to paint the selection.
+ PaintInfo info(image->context(), LayoutRect::infiniteRect(), PaintPhaseForeground, PaintBehaviorSkipSelectionHighlight);
AffineTransform& contentTransformation = currentContentTransformation();
AffineTransform savedContentTransformation = contentTransformation;
@@ -286,18 +308,18 @@ void SVGRenderingContext::renderSubtreeToImageBuffer(ImageBuffer* image, RenderE
contentTransformation = savedContentTransformation;
}
-void SVGRenderingContext::clipToImageBuffer(GraphicsContext* context, const AffineTransform& absoluteTransform, const FloatRect& targetRect, std::unique_ptr<ImageBuffer>& imageBuffer, bool safeToClear)
+void SVGRenderingContext::clipToImageBuffer(GraphicsContext& context, const AffineTransform& absoluteTransform, const FloatRect& targetRect, std::unique_ptr<ImageBuffer>& imageBuffer, bool safeToClear)
{
- ASSERT(context);
- ASSERT(imageBuffer);
+ if (!imageBuffer)
+ return;
FloatRect absoluteTargetRect = calculateImageBufferRect(targetRect, absoluteTransform);
// The mask image has been created in the absolute coordinate space, as the image should not be scaled.
// So the actual masking process has to be done in the absolute coordinate space as well.
- context->concatCTM(absoluteTransform.inverse());
- context->clipToImageBuffer(imageBuffer.get(), absoluteTargetRect);
- context->concatCTM(absoluteTransform);
+ context.concatCTM(absoluteTransform.inverse().value_or(AffineTransform()));
+ context.clipToImageBuffer(*imageBuffer, absoluteTargetRect);
+ context.concatCTM(absoluteTransform);
// When nesting resources, with objectBoundingBox as content unit types, there's no use in caching the
// resulting image buffer as the parent resource already caches the result.
@@ -305,18 +327,6 @@ void SVGRenderingContext::clipToImageBuffer(GraphicsContext* context, const Affi
imageBuffer.reset();
}
-FloatRect SVGRenderingContext::clampedAbsoluteTargetRect(const FloatRect& absoluteTargetRect)
-{
- const FloatSize maxImageBufferSize(kMaxImageBufferSize, kMaxImageBufferSize);
- return FloatRect(absoluteTargetRect.location(), absoluteTargetRect.size().shrunkTo(maxImageBufferSize));
-}
-
-IntSize SVGRenderingContext::clampedAbsoluteSize(const IntSize& absoluteSize)
-{
- const IntSize maxImageBufferSize(kMaxImageBufferSize, kMaxImageBufferSize);
- return absoluteSize.shrunkTo(maxImageBufferSize);
-}
-
void SVGRenderingContext::clear2DRotation(AffineTransform& transform)
{
AffineTransform::DecomposedType decomposition;
@@ -328,12 +338,12 @@ void SVGRenderingContext::clear2DRotation(AffineTransform& transform)
bool SVGRenderingContext::bufferForeground(std::unique_ptr<ImageBuffer>& imageBuffer)
{
ASSERT(m_paintInfo);
- ASSERT(m_renderer->isSVGImage());
+ ASSERT(is<RenderSVGImage>(*m_renderer));
FloatRect boundingBox = m_renderer->objectBoundingBox();
// Invalidate an existing buffer if the scale is not correct.
if (imageBuffer) {
- AffineTransform transform = m_paintInfo->context->getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
+ AffineTransform transform = m_paintInfo->context().getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
IntSize expandedBoundingBox = expandedIntSize(boundingBox.size());
IntSize bufferSize(static_cast<int>(ceil(expandedBoundingBox.width() * transform.xScale())), static_cast<int>(ceil(expandedBoundingBox.height() * transform.yScale())));
if (bufferSize != imageBuffer->internalSize())
@@ -342,20 +352,18 @@ bool SVGRenderingContext::bufferForeground(std::unique_ptr<ImageBuffer>& imageBu
// Create a new buffer and paint the foreground into it.
if (!imageBuffer) {
- if ((imageBuffer = m_paintInfo->context->createCompatibleBuffer(expandedIntSize(boundingBox.size()), true))) {
- GraphicsContext* bufferedRenderingContext = imageBuffer->context();
- bufferedRenderingContext->translate(-boundingBox.x(), -boundingBox.y());
+ if ((imageBuffer = ImageBuffer::createCompatibleBuffer(expandedIntSize(boundingBox.size()), ColorSpaceSRGB, m_paintInfo->context()))) {
+ GraphicsContext& bufferedRenderingContext = imageBuffer->context();
+ bufferedRenderingContext.translate(-boundingBox.x(), -boundingBox.y());
PaintInfo bufferedInfo(*m_paintInfo);
- bufferedInfo.context = bufferedRenderingContext;
- toRenderSVGImage(m_renderer)->paintForeground(bufferedInfo);
+ bufferedInfo.setContext(bufferedRenderingContext);
+ downcast<RenderSVGImage>(*m_renderer).paintForeground(bufferedInfo);
} else
return false;
}
- m_paintInfo->context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, boundingBox);
+ m_paintInfo->context().drawImageBuffer(*imageBuffer, boundingBox);
return true;
}
}
-
-#endif