Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify drawImage(<svg preserveAspectRatio>) #6546

Open
Kaiido opened this issue Apr 1, 2021 · 3 comments
Open

Clarify drawImage(<svg preserveAspectRatio>) #6546

Kaiido opened this issue Apr 1, 2021 · 3 comments
Labels
interop Implementations are not interoperable with each other topic: canvas

Comments

@Kaiido
Copy link
Member

Kaiido commented Apr 1, 2021

There is currently an interop issue when drawing an SVG image to a canvas regarding the scaling operation.

Let source_image be a detached HTMLImageElement with no width nor height attributes pointing to an SVG document whose root <svg> element has <svg width="100" height="100" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"...

Then let's draw this image in a way it should get scaled non-uniformly on a target canvas

ctx.drawImage( source_image,
  0, 0, 100, 100, // source_rect
  0, 0, 350, 100  // dest_rect
);

https://jsfiddle.net/z5a89mqh/

Note that we use an SVG image with intrinsic width and height, and that we do define the source rectangle of drawImage to avoid more interop issues.

  • Firefox will render it as if it was rendered in an <img width="350" height="100"> element, i.e they will respect the preserveAspectRatio rule and draw our 100x100 graphic unscaled in the middle of the dest_rect.
  • Chrome on the other hand will treat it as a two pass rendering, as if they were first drawing source_image as a bitmap using the SVG's intrinsic size1 and then stretching that bitmap to dest_rect, except that they don't really do that in order to keep the drawings sharp. So in this browser, we end up with our graphic stretched to the dest_rect, completely ignoring the svg's preserveAspectRatio.
  • Safari apparently takes the source <img> element as image, which would be against what CanvasImageSource says. So in this exact case, they'll stretch it like Chrome but with all the antialias artifacts, and if the <img> has a width and height on its own (even if set only through CSS) they'll pass what's rendered in that <img> to drawImage(). [fiddle].

If we try to find what the specs have to say, we have in drawImage

Paint the region of the image argument specified by the source rectangle on the region of the rendering context's output bitmap specified by the destination rectangle, after applying the current transformation matrix to the destination rectangle.

Both interpretations sound possible here. We need to check what image really means and in what state it is: if it doesn't have a size yet, SVG's viewBox and preserveAspectRatio probably can't be used and the SVG doesn't have a viewport, which means it's in an undefined state.
Trying to go back to what is this "image argument" leads to CanvasImageSource which states

When a CanvasImageSource object represents an HTMLOrSVGImageElement, the element's image must be used as the source image.

And then going to HTMLImageElement, there doesn't seem to be anything specifying in which state is an "image" representing a vector graphic, so it's hard to tell who's right.

FWIW, as a web-author I was assuming Firefox's behavior was the correct one because it made sense to me to have the dest_rect act as the final host for the SVG image, but that was before I read the drawImage specs... Now I just don't know anymore.


  1. Actually Chrome doesn't seem to really use the intrinsic size, as can be seen when drawing an image without intrinsic width and height, but in our easy-on-purpose example it comes to the same result.
@domenic
Copy link
Member

domenic commented Apr 1, 2021

/cc @whatwg/canvas

@annevk annevk added interop Implementations are not interoperable with each other topic: canvas labels Apr 12, 2021
@Kaiido
Copy link
Member Author

Kaiido commented Sep 15, 2021

Some previous discussions: w3c/svgwg#762

@schenney-chromium
Copy link
Contributor

I've come across this while looking through canvas issues. The SVG issue highlights the underlying conflict here: Should the canvas drawImage behavior respect the SVG image author's request or the canvas author's request. I find these two arguments in favor of the canvas author to be rather compelling:

  • If the canvas is drawing 3rd party images, an end user would not expect scaling behavior to change between a PNG and an SVG given as the image to draw.
  • It is possible to achieve any desired result with canvas src and dest rect settings, but not possible to do so if the browser forces the SVG aspect ratio. In particular, I think a canvas author setting up a CanvasImageSource from an element, can determine if it's SVG and if it has a preserveAspectRatio (not so sure about this bit) and so on. Then choose to respect it in the scr/dest rectangles.

In other words, I believe we could update the spec to say that the preserveAspectRatio in SVG-as-image used as a canvas source respects the aspect ratio in determining the source intrinsic dimensions but not when mapping to the dest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
interop Implementations are not interoperable with each other topic: canvas
Development

No branches or pull requests

4 participants