Monday, October 5th, 2009

toDataURL, Canvas, and SVG

Category: Canvas, SVG

<p>I’m a fan of all the new ways of drawing on the web. I consider myself a Canvas evangelist, an SVG evangelist, and an evangelist for the new CSS Animation work going on. When asked “SVG or Canvas” I’ve long felt the right answer is: “Yes” :) Canvas is great at pixels, SVG is great at vectors, ’nuff said. Give me my scriptable image tag (Canvas) AND my easy scene graph (SVG).

Along these lines, I was delighted to attend a recent presentation by Samuli Kaipiainen and Matti Paksula from the University of Helsinki at SVG Open 2009. In their paper (which I recommend reading) they show all sorts of cool results concerning Canvas and SVG, but two results jumped out at me in particular.

Samulimatti

First, they showed apps built with SVG that should have used Canvas and vice versa to identify when to use one or the other. They showed a Jigsaw Puzzle program written in Canvas that should have used SVG, as the Canvas version took 2800 (!) lines of JavaScript essentially recreating the scene graph and rotations that SVG gives you for free:

jigsaw_screenshot

They then showed a Space Invaders game written with SVG that obviously should have used Canvas instead: there’s no mouse interaction, it’s keyboard driven, and it needs rapid pixel-based updates since it’s a game:

spaceinvaders_screenshot

Second, both developers point out a nifty trick and snippet of code that I’m sure will become a regular part of peoples’ work. They convert a Canvas image into a form that can be brought right into SVG by turning it into a data URL:

javascript
< view plain text >
  1. function importCanvas(sourceCanvas, targetSVG) {
  2. // get base64 encoded png from Canvas
  3. var image = sourceCanvas.toDataURL("image/png");
  4.  
  5. // Create new SVG Image element.  Must also be careful with the namespaces.
  6. var svgimg = document.createElementNS("http://www.w3.org/2000/svg", "image");
  7. svgimg.setAttributeNS("http://www.w3.org/1999/xlink", 'xlink:href', image);
  8.  
  9. // Append image to SVG
  10. targetSVG.appendChild(svgimg);
  11. }

This would be useful for drawing a fancy background with Canvas, for example, and then pulling that onto an SVG element so you get an easy scene graph and scalable graphics. Samuli and Matti report that this technique works on all browsers (but not IE unfortunately, even with ExCanvas, as that currently doesn’t support toDataURL yet).

Samuli and Matti end their paper with the following quote:

Based on these findings, there really is no vs. situation. The right technique needs to be selected for the job, and we hope this paper gives a good starting point for that. When the right choices are made, SVG and Canvas can actually benefit from each other…

Yes, there’s a limit on each standard’s capabilitiness. For pixel flare and other “demo effects”, go with Canvas. For intrinsic shapes and user interaction, go wth SVG. Graphical user interfaces on the web are an especially delicious application for SVG.

As a final note, Matti and Samuli identify a gap that HTML 5 needs to fill: it would be great if you could go the other direction and turn an SVG image into it’s rendered pixels to be passed to the Canvas tag, basically a toDataURL for SVG that can be passed into Canvas’s fromDataURL.

Related Content:

  • Get ready for HTML5 and canvas elements
    The HTML5 canvas element is a new way to display active graphics and manipulate images with JavaScript. Find out what the new element will mean for...
  • Mozilla issues Firefox mega-fix
    The digital underground could exploit as many as seven flaws to bypass security restrictions, compromise sensitive data and launch cross-site...
  • Mozilla issues Firefox mega-fix
    The digital underground could exploit as many as seven flaws to bypass security restrictions, compromise sensitive data and launch cross-site...
  • Mozilla issues Firefox mega-fix
    The digital underground could exploit as many as seven flaws to bypass security restrictions, compromise sensitive data and launch cross-site...
  • The impact of HTML5 on Web applications
    HTML, the HyperText Markup Language, is an essential part of the Internet experience. HTML5 is one part of what WHATWG sees as essential to the...

Posted by Brad Neuberg at 6:30 am
21 Comments

+++--
3.7 rating from 29 votes

21 Comments »

Comments feed TrackBack URI

I think, we need a SVGC (SVG canvas) … someting like photoshop or gimp … i’d like to put “layers” in it. Some layers will be bitmap and some vector :)

Comment by wagoon — October 5, 2009

I’ve actually been working on just that: an SVG parser that renders into . While it would be almost impossible to support things like SVG filters in canvas without a native implementation, almost everything else is implementable in javascript. My SVG parser can pass about 40% of the SVG test suite at the moment, but the better part of the test suite tests animation (and I don’t support that yet!). It works pretty well, and in some cases better than the browser’s native implementation. I will be releasing it sometime soon!

Devon

Comment by devongovett — October 5, 2009

oops. the commenting system didn’t like my canvas tag. Should have been “I’ve actually been working on just that: an SVG parser that renders into canvas.

Comment by devongovett — October 5, 2009

@wagoon: I did something similar to what you described for my visualization toolkit (http://thejit.org). When the Canvas text API is not enabled I just draw stuff on canvas and then add a svg element to create labels on top of that. When SVG is not available, well, I just use HTML labels over Canvas. You can see the result here:

http://blog.thejit.org/2009/08/30/html-svg-or-canvas-labels/

Comment by philogb — October 5, 2009

@devongovett I’ve been working on SVG-to-canvas parser as well. It converts simple shapes (http://www.w3.org/TR/SVG/shapes.html) and paths (http://www.w3.org/TR/SVG/paths.html) more or less reliably, but doesn’t yet implement things like gradients (http://www.w3.org/TR/SVG/pservers.html) or animations (http://www.w3.org/TR/SVG/animate.html). Size of SVG1.1 specification is humongous. I spent few months to complete what’s probably 5-10% of it. Implementing 100% is far from an easy task :/

Comment by kangax — October 5, 2009

When using an SVG-file as source for an img-tag you can just use a canvas and its drawImage method to get the rendered SVG into the canvas.

Comment by AndiSkater — October 5, 2009

I have found a workaround to get rendered svg pixeldata from an inline svg into a canvas (working in latest webkit nightly):

1. create an xhtml file with an inline svg (like descriibed here: http://wiki.svg.org/Inline_SVG) and save it as .xhtml file
2. encapsulate the svg-tag in a div-tag with id “svgcontainer”
3. use svgcontainer.innerHTML to get the contents of the div (which is the current svg structure!)
4. use the webkit native base64 encoder (window.btoa(svgcontainer.innerHTML)) to encode the svg as base64
5. add “data:image/svg+xml;base64,” to the start of the base64 string
6. create an img-element and assign the base64 svg dataurl as src
7. use canvas drawImage to draw the img into the canvas
8. now you have the rendered svg as editable pixel data in a canvas.

This will only work in webkit because FF does not support svg files as sources for img-tags.

If this is of general interest I can post are full example in my blog (www.bytestrom.eu)

Comment by AndiSkater — October 5, 2009

i managed to get some canvas on the bbc, down the left hand side beside the tracklist’s, if you click play you’ll see the canvas element, seems to work beautifully

Comment by mcgoooo — October 5, 2009

To implement svg.toDataURL(), what about traversing the SVG DOM and then executing the corresponding Canvas command for each SVG element, and then getting canvas.toDataURL()? There wouldn’t be a 100% overlap, but most could be translated I’d imagine.

Comment by westonruter — October 5, 2009

@westonruter What do you mean by “executing corresponding Canvas command”? There’s really no one-to-one mapping here; one SVG element with few attributes can result in *hundreds* of corresponding canvas commands.

Comment by kangax — October 5, 2009

@kangax cool! SVG paths have been the most difficult to implement so far. But after a lot of work, I have a parser that can accurately render 99% of paths thrown at it. I have implemented almost all but filters and animation so it’s coming along!

Comment by devongovett — October 5, 2009

You can embed a canvas directly into SVG without going through dataURL, using .

Comment by rocallahan — October 5, 2009

Er, using <foreignObject>.

Comment by rocallahan — October 5, 2009

Great comments from folks with workarounds and other work having SVG and Canvas play well together! I’m excited to see your projects come to fruition; email me when you are ready so we can blog about it here.

Best,
Brad

Comment by Brad Neuberg — October 5, 2009

Hi all!

It seems like “everybody” wants to solve this by writing a SVG parser for Canvas. SVG is however pretty complicated and for most(?) of the usecases just a bitmap export (like .toDataURL) is sufficient. SVG is already rendered in the browser, so it should be trivial to implement for browsers.

To make that SVG parser do more than toDataURL for SVG, you also need to implement a scene graph for Canvas (and that’s what SVG does natively). Even the most perfect (100% complete) SVG parser without SVGs SMIL animations, scene graph, etc. would be only as good as SVG.toDataURL()

And, SVG is for different things than what Canvas is good for. These technologies do not overlap, we really need both (and especially SVG!)

– Matti, co-author of that paper.

Comment by matti — October 5, 2009

foreignobject embed is a solution for web, but if that SVG needs to be exported / saved, then everything what has been included with foreignObject can not be rendered anymore, because SVG doesn’t have any idea what’s inside there. SVGImageElement with canvas.toDataURL base64 encoded PNG as source solves this. SVG can be exported and Canvas (latest state before export) is included.

Comment by matti — October 5, 2009

@matti I was actually writing this so that I can use SVG in canvas supporting browsers. All of the browsers that support SVG, do not support the same things, and the things that they do support, they support differently. The canvas implementations in these browsers are pretty much the same all around, partially because canvas is trivially easy for browser makers to implement. I wanted to take advantage of SVG, but have my images look the same across browser without using plugins like flash. Thus I implemented it in canvas!

Comment by devongovett — October 5, 2009

Here’s an example where I’m trying to choose between SVG and Canvas.

I have a project coming up where I’ll be drawing anti-aliased lines over an image. Which would be better for the task, Canvas or SVG? Can I just put a div up as an overlay and draw lines? Does Canvas support a transparent background for this kind of work?

Comment by Nosredna — October 5, 2009

@devongovett: The core of SVG 1.1 is well implemented and pretty consistent across the browsers unless you are using rarely used features like Filters. What are you trying to do that isn’t working cross browser?

@Nosredna: Is there any user interaction with what you are drawing? If there is, I would probably use SVG with lines and an image inside. If there isn’t, then it would make sense to use the Canvas tag and drawImage.

Comment by Brad Neuberg — October 5, 2009

@brad What I want to be able to do, is export an image, any image, from programs like Adobe Illustrator and put it on a web page. If you’ve tried this with complex Illustrator files, you run into all sorts of problems mostly involving typography. Also, when you run these browsers through the SVG test suite, there are these little things that are rendered incorrectly – and it’s the little things that make the difference in the long run.

I’m also looking for speed. Speed in initial rendering is one thing, speed in updating is another. As you said in the post, fast updating in games like that space invaders example should be done in canvas. Once the initial parse and render of the SVG file is complete, you can cache the output and do very fast incremental updates. For example, an SVG file that takes say 200ms to load, parse, and render for the first time might take 25ms to update from the cache. This hugely benefits performance. Updating the DOM is not known to be fast, and is a pain in the backside anyway. This example gets the best of both worlds. You can design your application in a design program like Illustrator, which is much easier than hand coding it, and also reap the benefit of canvas’s fast updating speeds while not having to deal with the DOM, in a single solution.

Comment by devongovett — October 5, 2009

Leave a comment

You must be logged in to post a comment.