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

Allow SVGSVGElement as image parameter in CanvasRenderingContext2D.drawImage() #10289

Open
hfmanson opened this issue Apr 20, 2024 · 3 comments
Labels
addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest topic: canvas

Comments

@hfmanson
Copy link

hfmanson commented Apr 20, 2024

What is the issue with the DOM Standard?

Currently a SVGSVGElement can only be rendered in a HTML canvas by

  • serializing the containing document to a string
  • creating a blob out of it
  • convert the blob to an url
  • create an image element and assign the src property
  • adding an EventListener which draws the loaded image asynchronously.
const svgSource =
`<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1" version="1.1">
  <circle cx="0.5" cy="0.5" r="0.4" fill="red" stroke="blue" stroke-width="10"/>
</svg>
`;
  
addEventListener("load", () => {
  function drawSVGSVGElement(svgsvgElement) {
// serializing the containing document to a string
    const s = new XMLSerializer();
    const str = s.serializeToString(svgsvgElement.ownerDocument);
// creating a blob out of it    
    let blob = new Blob([str], {type: 'image/svg+xml'});
// convert the blob to an url
    let url = URL.createObjectURL(blob);
// create an image element and assign the src property    
    let image = document.createElement('img');
    image.src = url;
// adding an EventListener which draws the loaded image.*/    
    image.addEventListener('load', function () {
      const canvas = document.getElementById('canvas');
      const context = canvas.getContext("2d");
      context.drawImage(image, 0, 0);
      URL.revokeObjectURL(url);
    });
  }
  function getSVGSVGElement() {
    const parser = new DOMParser();
    const doc = parser.parseFromString(svgSource, "image/svg+xml");
    return doc.documentElement;
  }
  const svgsvgElement = getSVGSVGElement();
  drawSVGSVGElement(svgsvgElement);
}, false);

Demo in jsfiddle

If CanvasRenderingContext2D.drawImage() also allows to have an image parameter of type SVGSVGElement the code would be

function drawSVGSVGElement(svgsvgElement) {
  const canvas = document.getElementById('canvas');
  const context = canvas.getContext("2d");
  context.drawImage(svgsvgElement, 0, 0);
}
@annevk annevk transferred this issue from whatwg/dom Apr 20, 2024
@annevk annevk added addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest topic: canvas labels Apr 20, 2024
@Kaiido
Copy link
Member

Kaiido commented Apr 20, 2024

One of the advantages of having to go through an <img> element is that this allows for easier sandboxing of the SVG image. For instance here I modified the original fiddle to show both the in DOM <svg> and the one drawn on the canvas. We wouldn't want the :visited selector to be leaked in the canvas where it can be read back.

So this means that the browser can't go directly from the image rendered into the doc to the canvas, it will have to recompute it there, or to taint the canvas which puts quite a limit on the feature.

I'd personally propose that if we do this for <svg>, why not do it for any element? IIRC there was already a proposal for such an "any element as source" idea, though I can't find it right now.

@hfmanson
Copy link
Author

hfmanson commented Apr 21, 2024

Indeed, CanvasRenderingContext2D.drawImage() does not render the image, but its bitmap which is a bit confusing (better name drawBitmap?). So passing a SVGSVGElement is not appropriate for drawImage().
bitmap only is demonstrating the above. When drawing the image on the canvas opacity is not applied, just the image bitmap is drawn.

I could change the proposal to "add CanvasRenderingContext2D.drawDocument() method", which has the same signature a drawImage apart for its first parameter which would be a document instead of an image-like parameter with a bitmap. The semantics can be the same as when a SVG image src is supplied, which also converts a SVG to a bitmap.

I think having a drawImage with any element as parameter is not possible, e.g. it's not possible to draw a SVG circle without it's svg document. Is that correct?

@annevk
Copy link
Member

annevk commented Apr 22, 2024

@Kaiido makes a good point that it needs the same restrictions as <img> SVG, so perhaps this isn't worth doing. Supporting arbitrary (styled) node trees without revealing anything about the end user is rather involved and not something anyone has been willing to take on thus far.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest topic: canvas
Development

No branches or pull requests

3 participants