Skip to content
Permalink
Browse files
[GPU Process] REGRESSION: Drawing a large SVG image on a canvas may t…
…ake too much memory

https://bugs.webkit.org/show_bug.cgi?id=230886
rdar://83628607

Reviewed by Simon Fraser.

Source/WebCore:

For the GPUProcess rendering on a canvas, we have to draw the SVGImage to
a temporary ImageBuffer, get a NativeImage from this ImageBuffer and send
it to GPUProcess through a DrawNativeImage display list item.

The fix is:

1. Make sure the size of temporary ImageBuffer is scaled to the Graphics
   Context CTM.
2. Clamp the scaled size to the MaxClampedArea. So ImageBuffer::create()
   returns a valid ImageBuffer.
3. Scale the destination GraphicsContext to the reciprocal of the scaling
   factor before drawing the NativeImage.

Test: fast/canvas/canvas-draw-large-svg-image.html

* svg/graphics/SVGImage.cpp:
(WebCore::SVGImage::drawAsNativeImage):

LayoutTests:

* fast/canvas/canvas-draw-large-svg-image-expected.html: Added.
* fast/canvas/canvas-draw-large-svg-image.html: Added.


Canonical link: https://commits.webkit.org/243449@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@284740 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
shallawa committed Oct 23, 2021
1 parent 0959ef4 commit 1524fd279f18eb07fdb99927f3c55b453e75292d
@@ -1,3 +1,14 @@
2021-10-22 Said Abou-Hallawa <said@apple.com>

[GPU Process] REGRESSION: Drawing a large SVG image on a canvas may take too much memory
https://bugs.webkit.org/show_bug.cgi?id=230886
rdar://83628607

Reviewed by Simon Fraser.

* fast/canvas/canvas-draw-large-svg-image-expected.html: Added.
* fast/canvas/canvas-draw-large-svg-image.html: Added.

2021-10-22 Gabriel Nava Marino <gnavamarino@apple.com>

Check if start and end positions are still valid after updating them through mergeStartWithPreviousIfIdentical
@@ -0,0 +1,11 @@
<body>
<canvas id="canvas" width="100" height="100">
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

const { width, height } = canvas;
ctx.fillStyle = "green";
ctx.fillRect(0, 0, width, height);
</script>
</body>
@@ -0,0 +1,34 @@
<body>
<canvas id="canvas" width="100" height="100">
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

const { width, height } = canvas;
ctx.fillStyle = "red";
ctx.fillRect(0, 0, width, height);

function drawImage(image, scale) {
ctx.save();

ctx.scale(1 / scale, 1 / scale);
ctx.drawImage(image, 0, 0, 1000, 1000, 0, 0, width * scale, height * scale);

ctx.restore();
}

if (window.testRunner)
testRunner.waitUntilDone();

const image = new Image();
image.onload = (() => {
drawImage(image, 500);
if (window.testRunner)
testRunner.notifyDone();
});
image.src = `data:image/svg+xml,
<svg xmlns='http://www.w3.org/2000/svg' width='1000' height='1000'>
<rect width='1000' height='1000' fill='green'/>
</svg>`;
</script>
</body>
@@ -1,3 +1,29 @@
2021-10-22 Said Abou-Hallawa <said@apple.com>

[GPU Process] REGRESSION: Drawing a large SVG image on a canvas may take too much memory
https://bugs.webkit.org/show_bug.cgi?id=230886
rdar://83628607

Reviewed by Simon Fraser.

For the GPUProcess rendering on a canvas, we have to draw the SVGImage to
a temporary ImageBuffer, get a NativeImage from this ImageBuffer and send
it to GPUProcess through a DrawNativeImage display list item.

The fix is:

1. Make sure the size of temporary ImageBuffer is scaled to the Graphics
Context CTM.
2. Clamp the scaled size to the MaxClampedArea. So ImageBuffer::create()
returns a valid ImageBuffer.
3. Scale the destination GraphicsContext to the reciprocal of the scaling
factor before drawing the NativeImage.

Test: fast/canvas/canvas-draw-large-svg-image.html

* svg/graphics/SVGImage.cpp:
(WebCore::SVGImage::drawAsNativeImage):

2021-10-22 Gabriel Nava Marino <gnavamarino@apple.com>

https://bugs.webkit.org/show_bug.cgi?id=232177
@@ -361,7 +361,25 @@ ImageDrawResult SVGImage::drawAsNativeImage(GraphicsContext& context, const Floa
{
ASSERT(!context.hasPlatformContext());

auto rectInNativeImage = FloatRect { { }, destination.size() };
auto transform = context.getCTM();
if (!transform.isInvertible())
return ImageDrawResult::DidNothing;

// Consider the scaling of the context only.
auto contextScale = FloatSize(transform.xScale(), transform.yScale());
auto scaledDestination = destination;
scaledDestination.scale(contextScale);

// Check if we need to clamp the temporary ImageBuffer.
auto clampingScale = FloatSize(1, 1);
ImageBuffer::sizeNeedsClamping(scaledDestination.size(), clampingScale);

// contextScale * clampingScale is the scaling factor.
auto scale = contextScale * clampingScale;
scaledDestination.scale(clampingScale);

auto rectInNativeImage = FloatRect { { }, flooredIntSize(scaledDestination.size()) };

auto nativeImage = this->nativeImage(rectInNativeImage.size(), source, colorSpace);
if (!nativeImage)
return ImageDrawResult::DidNothing;
@@ -371,7 +389,12 @@ ImageDrawResult SVGImage::drawAsNativeImage(GraphicsContext& context, const Floa
if (orientation == ImageOrientation::Orientation::FromImage)
localImagePaintingOptions = ImagePaintingOptions(options, ImageOrientation::Orientation::None);

context.drawNativeImage(*nativeImage, rectInNativeImage.size(), destination, rectInNativeImage, localImagePaintingOptions);
// Change the coordinate system to reflect the scaling factor.
context.scale(FloatSize(1 / scale.width(), 1 / scale.height()));

context.drawNativeImage(*nativeImage, rectInNativeImage.size(), scaledDestination, rectInNativeImage, localImagePaintingOptions);

context.scale(scale);

if (imageObserver())
imageObserver()->didDraw(*this);

0 comments on commit 1524fd2

Please sign in to comment.