W3C home > Mailing lists > Public > whatwg@whatwg.org > September 2013

Re: [whatwg] High-density canvases

From: Dean Jackson <dino@apple.com>
Date: Tue, 10 Sep 2013 10:18:09 +1000
Message-id: <5989691D-4430-4FA0-8E22-BCDD35CC5DC7@apple.com>
To: Ian Hickson <ian@hixie.ch>
Cc: Stephen White <senorblanco@chromium.org>, Rik Cabanier <cabanier@gmail.com>, WHAT Working Group <whatwg@whatwg.org>

On 10 Sep 2013, at 10:00 am, Ian Hickson <ian@hixie.ch> wrote:

> On Wed, 17 Jul 2013, Rik Cabanier wrote:
>> Ian wrote:
>>> 
>>> The density aspect of this might be pointless, given the failure of 
>>> getImageDataHD(); if we're dropping that one, I'll drop this one at 
>>> the same time.
>> 
>> Yes, please drop it since the HD methods are going away from the one 
>> implementation.
> 
> On Tue, 9 Jul 2013, Stephen White wrote:
>> 
>> Conversely, if it helps to bring the spec closer to the implementations, 
>> one thing we do not intend to implement in Chrome is the automatic 
>> high-DPI canvas scaling (ie., auto-doubling of backing stores, 
>> getImageDataHD(), putImageDataHD(), etc).
>> 
>> I believe Apple has also announced that they are dropping support for 
>> this in Safari 7.
> 
> So my understanding is that the reason this feature failed is that there's 
> existing content that assumes a 1:1 ratio, and having an automatic 
> high-density mode was making some pages end up with canvases with four 
> canvas pixels per CSS pixel (linearly) -- two from the browser making a 
> native canvas, times two from the page scaling the canvas for high DPI 
> displays. This is a factor of sixteen over a 1:1 canvas, a factor of four 
> more than it should be for high DPI, and a big waste of resources.

It wasn’t just that. A lot of existing code did something like this:

var pixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
for (var i = 0; i < canvas.width; i++) {
  for (var j = 0; j < canvas.height; j++) {
    pixels[j * canvas.width + i][0] = newRedValue;
    pixels[j * canvas.width + i][1] = newGreenValue;
    pixels[j * canvas.width + i][2] = newBlueValue;
  }
}
ctx.putImageData(…)

i.e. no one read the spec that says you should check the width and height
of the returned ImageData.

Then there is toDataURL(). Should that return a full-sized bitmap? Then you
need to be sure what you’re uploading it to will handle the larger size.

Basically, there was pretty unanimous support from Web developers to
“just give us the damn pixels we asked for”, as well as Chrome and Mozilla
suggesting they didn’t want to implement the auto-doubling.

We (Safari) were in a position where we were requiring developers to
write extra code just for us, as well as potentially breaking some
(existing) content.

> 
> As much as sites do this manually, though, it's a huge pain in the neck to 
> have to worry about pixel density when you're creating your canvas and 
> drawing on it, especially if you're not drawing sprites on it.
> 
> While we're talking about annoying things, there's also the annoyance that 
> canvases tend to not take zoom into account (either density-affecting zoom 
> like page zoom on desktop, or "transparent" zoom like pinch-zoom on mobile 
> for non-mobile-optimised sites, which the site isn't supposed to know 
> about): you have to remember to listen for onresize, and then manually 
> blow away your canvas and recreate it at the right density and then 
> squeeze it into place so that the coordinate space matches what your code 
> is expecting while the <canvas> is actually sized for the display.

Yes, but I think the developer is the one who best knows what size/quality
her content requires.

A great developer will do exactly as you suggest: constantly examine the
rendered size of the canvas (taking into account pixel density and viewport/zoom).

> 
> There's also the issue of full-bleed canvases where every time the 
> container changes, you have to remember to re-update the canvas coordinate 
> space and repaint because otherwise your pretty page gets all warped.
> 
> It would be nice to fix these all at once, and I think we can, by 
> introducing a configuration option on getContext(), in the style of WebGL:
> 
>   getContext('2d', { density: 'autosize' });
> 
> This would trigger the following behaviour: When the context is created, 
> and subsequently when the <canvas> changes size (e.g. due to being sized 
> with CSS relative units and the element they're relative to changing), or 
> when the display density changes size (e.g. due to page zoom), then:
> 
>   - the width and height of the canvas bitmaps get updated to match the
>     new native size of the <canvas>, at native density.
> 
>   - the coordinate space of the canvas (context.width/context.height) 
>     gets updated to match the size of the <canvas> in CSS pixel units.

Note that this would reset the context, which would throw away the contents
and context state. This might be exactly what you want though - put
this attribute on a full-bleed canvas and resize your window -> everything
disappears unless you implement the ‘resize’ event handler.

> 
>   - a 'resize' event gets fired at the <canvas>.
> 
> We would dump the *HD versions of the methods, and make the regular ones 
> go back to returning the actual raw pixels, since that would now work fine 
> and still provide HD-quality content everywhere it's available.
> 
> What do people think?

This seems ok to me. I still worry we’ll get into a situation where developers
will add density:’autosize’ (in order to get the resizing behaviour) and NOT check
the results of getImageData - everything will work fine if they never test on
a high-dpi screen.

Maybe there should be two attributes?

Dean
Received on Tuesday, 10 September 2013 00:19:45 UTC

This archive was generated by hypermail 2.4.0 : Wednesday, 22 January 2020 17:00:09 UTC