Canvas drawImage does not correctly scale images using an SVG source
Categories
(Core :: Graphics: Canvas2D, defect, P3)
Tracking
()
People
(Reporter: simon.sarris, Unassigned)
References
(Regression)
Details
(Keywords: parity-chrome, parity-edge, regression, Whiteboard: [gfx-noted])
Attachments
(6 files)
User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36
Steps to reproduce:
Simple repro: https://codepen.io/simonsarris/pen/mOJdQe
This correctly works in other major browsers.
Code also here and attached, with example of FF vs Chrome vs IE Edge.
<body>
<canvas id="canvas" style="border: solid 3px red; width:400px; height:400px"></canvas>
<p>The above should look "squished", as it is 391x391 drawn into the area 100x391. For reference the original SVG:</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/1/1a/SVG_example_markup_grid.svg">
</img></p>
</body>
<script>
var can = document.getElementById('canvas');
can.width = 400;
can.height = 400;
var ctx = can.getContext('2d');
var img = new Image();
// like its 1999:
img.onload = function() {
// The SVG's real dimensions are 391x391, we are going to draw it at
// 100x391, this will produce a squished image (this is good)
// If it produces a scaled image (100x100), this is bad
ctx.drawImage(img, 0, 0, 100, 391);
}
img.src = "https://upload.wikimedia.org/wikipedia/commons/1/1a/SVG_example_markup_grid.svg";
</script>
Actual results:
The SVG image is scaled down uniformly, instead of scaled differently on X (should be scaled) and Y (should not be scaled)
Expected results:
A destination width and height of 100x391 should draw a source
Reporter | ||
Comment 1•5 years ago
|
||
Updated•5 years ago
|
Comment 2•5 years ago
|
||
Regression Window: https://hg.mozilla.org/integration/mozilla-inbound/pushloghtml?fromchange=3488976ecf0f&tochange=3d51132a0099
Regressed by: 3d51132a0099aed9bd4dfc16584eff231253a35e Seth Fowler — Bug 1043560 - Refactor the imgIContainer::Draw API. r=tn,dholbert,jwatt,mwu,mattwoodrow,roc sr=jrmuizel
Updated•5 years ago
|
Comment 3•5 years ago
•
|
||
If you don't want to preserve the aspect ratio then indicate you don't want to:
img.src = "https://upload.wikimedia.org/wikipedia/commons/1/1a/SVG_example_markup_grid.svg#svgView(preserveAspectRatio(none))";
Or alternatively edit the SVG_example_markup_grid.svg so that it has preserveAspectRatio="none" on its root element if you control that file.
Is ignoring the preserveAspectRatio value actually useful behaviour by Chrome/Edge? What if you do want to preserve the aspect ratio?
Reporter | ||
Comment 4•5 years ago
|
||
But this isn't a matter of SVG rendering, it is one of Canvas context rendering. As soon as something is to be painted on the HTML Canvas context, shouldn't it follow the context's rules for drawing?
It is a feature of the Canvas Context that calls to context.drawImage(img, x, y, width, height, [ignoring src args])
will scale (axis independent) any image input according to the provided width
and height
. Preserving the aspect ratio may be a feature of how SVG draws itself, but when taking an SVG source and drawing it to a Canvas context with drawImage
, I think the expectation that other browsers are following is to use the canvas context's rules for how to draw.
Comment 5•5 years ago
|
||
The priority flag is not set for this bug.
:lsalzman, could you have a look please?
For more information, please visit auto_nag documentation.
Updated•5 years ago
|
Comment 6•4 years ago
|
||
I spent a while googling while trying to figure out what was going on here. I was about to write a ticket very similar to this one.
As far as I can tell, this ticket is the only/most-accessible place on the internet that describes the step of modifying preserveAspectRatio
being necessary to allow CanvasRenderingContext2D#drawImage
with 9 arguments to work for svgs.
The 9 argument variant of drawImage
is designed to allow the programmer to map one rectangle in the source image to another inside the canvas. I can understand the thinking behind preserving aspect ratio, but if the expected behavior of the 9 argument variant is going to be violated it would be nice to have documentation showing working code.
If this behavior is going to stay the way it is, I think it would be helpful to future devs to have working examples for these cases (if not more):
- svg downloaded by setting image.src
- svg as base64 dataUrl
this issue, combined with #700533, makes using drawImage
with svgs very tricky on Firefox.
Comment 7•4 years ago
|
||
It should be noted that the 3 and 5 argument variants also violate expectations when preserveAspectRatio
has not been changed.
Comment 8•4 years ago
|
||
Setting status back to UNCONFIRMED since w3c at the moment seem to be suggesting that Firefox has the intended rendering.
Comment hidden (obsolete) |
Updated•4 years ago
|
Updated•2 years ago
|
Comment 10•9 months ago
|
||
I've identified this ticket as it's affecting me in doing an off-screen rendering to perform some calculations on a user-provided image.
preserveAspectRadio(none)
provided an explicit workaround for me, and like the earlier poster I only found it from this ticket.
But my intuition is this is not correct to require this because as a JavaScript author I would expect the default rendering of equivalent SVG and PNG images to be equivalent. The PNG does not preserve its aspect ratio on any browser when provided drawImage(), and therefore I would not expect the SVG to do this.
Comment 11•9 months ago
|
||
Comment 12•9 months ago
|
||
Comment 13•9 months ago
|
||
Comment 14•9 months ago
|
||
Comment 15•9 months ago
|
||
We have found that the use of preserveAspectRadio(none)
has a negative side-effects on Safari browsers (tested version 16.1 here), giving a different bounding box. Therefore it can't be applied unconditionally, and something like the following is necessary:
if (firefox)
img.src = `${url}#svgView(preserveAspectRatio(none))`;
else
img.src = url;
Description
•