[whatwg] Proposal: ImageData constructor or factory method with preexisting data

Kenneth Russell kbr at google.com
Tue Mar 12 16:53:48 PDT 2013


On Tue, Mar 12, 2013 at 3:49 PM, Rik Cabanier <cabanier at gmail.com> wrote:
>
>
> On Tue, Mar 12, 2013 at 3:03 PM, Kenneth Russell <kbr at google.com> wrote:
>>
>> On Tue, Mar 12, 2013 at 2:04 PM, Rik Cabanier <cabanier at gmail.com> wrote:
>> >
>> >
>> > On Tue, Mar 12, 2013 at 11:40 AM, Kenneth Russell <kbr at google.com>
>> > wrote:
>> >>
>> >> On Tue, Mar 12, 2013 at 11:15 AM, Rik Cabanier <cabanier at gmail.com>
>> >> wrote:
>> >> > sounds good!
>> >> > I think this is a convenient and useful addition.
>> >>
>> >> Great.
>> >>
>> >> > do you want to keep doubles to define the dimensions instead of
>> >> > integers? If
>> >> > so, the size should probably  4 * ceil(sw) * ceil(sh)
>> >>
>> >> I would prefer to use integers, and only used doubles to be consistent
>> >> with the other APIs like getImageData and createImageData. In this
>> >> case it would make more sense to use integers, since the width and
>> >> height are simply being used to interpret preexisting data in the
>> >> Uint8ClampedArray.
>> >
>> >
>> > The current canvas spec doesn't specifically state what happens with
>> > partial
>> > pixels. What happens today?
>> > (Also is there a definition somewhere that states when a pixel is
>> > considered
>> > filled?)
>>
>> Safari, Firefox and Chrome all round the double arguments to
>> putImageData to integers using the "truncate" rounding mode and then
>> draw the source ImageData pixel-for-pixel. For example, passing 64.5
>> or 64.99 for the dx or dy arguments is equivalent to passing 64.
>> Here's a test case.
>>
>> <canvas id="canvas" width="256" height="256"></canvas>
>> <script>
>> var canvas = document.getElementById("canvas");
>> var ctx = canvas.getContext("2d");
>> var width = canvas.width;
>> var height = canvas.height;
>> ctx.fillRect(0, 0, width, height);
>> var imageData = ctx.createImageData(width / 2, height / 2);
>> for (var ii = 0; ii < imageData.data.length; ii += 4) {
>>   imageData.data[ii + 0] = 0;
>>   imageData.data[ii + 1] = 255;
>>   imageData.data[ii + 2] = 0;
>>   imageData.data[ii + 3] = 255;
>> }
>> // Try passing 64.5, 64.99, or 65 for one or both of these arguments
>> and see the results
>> ctx.putImageData(imageData, 64, 64);
>> </script>
>>
>> In other words, the source ImageData would not be rendered into the
>> canvas at a half-pixel offset if ctx.putImageData(imageData, 64.5,
>> 64.5) were called.
>
>
> Thanks for investigating this. The fact that 'truncate' is called, should
> probably go in the spec.
> Maybe we should change the IDL to integer.

I think that would be a good idea. I believe it would be backward
compatible and leave the definition of double -> long conversion to
the Web IDL and ECMAScript specs.


>> >> I don't think it is necessary to provide a createImageDataHD in this
>> >> interface. The caller will know the devicePixelRatio and determine
>> >> whether to generate high-DPI data.
>> >
>> > That probably won't work since it results in code that executes
>> > differently
>> > on devices that are HD.
>>
>> I think it works. The application will call the new ImageData
>> constructor and pass it to either putImageData or putImageDataHD.
>> These interpret the incoming ImageData differently depending on the
>> devicePixelRatio.
>>
>> In contrast, CanvasRenderingContext2D's existing createImageDataHD and
>> getImageDataHD methods will create an ImageData that may have a
>> different width and height from those passed in. The reason for this
>> is that these methods are referring to the canvas's backing store. For
>> this new constructor which simply wraps existing pixel data, the
>> application knows exactly how many pixels are contained in the array,
>> so it makes the most sense to take the incoming width and height
>> verbatim. I don't see any advantage to having an alternate "high-DPI"
>> constructor which would multiply the width and height by the
>> devicePixelRatio behind the scenes.
>>
>
> Your proposal is:
>
> createImageData would throw an exception if the length of the
> Uint8ClampedArray was not equal to 4 * floor(sw) * floor(sh), or at
> least, if the length of the array was less than this value.

Yes.

> So, if you create an imageData that is going to be used in putImageDataHD,
> the bounds checking happens when you pass it into putImageDataHD?
> It seems the imageData object should know if it was meant for an HD call.
> There is no real reason why you could use it in both HD and non-HD APIs.

Glenn already answered this, but you can already call getImageData and
pass the result to putImageDataHD (and vice versa --
getImageDataHD/putImageData). The only difference between putImageData
and putImageDataHD is that on high-DPI displays putImageDataHD will
take the same image data and display it in a smaller region. All of
the size checking would be performed in the new ImageData constructor
we're discussing. I don't think ImageData itself should know anything
about high DPI because it's designed for pixel-by-pixel manipulation.

-Ken


>>
>> >>
>> >>
>> >> > On Tue, Mar 12, 2013 at 10:50 AM, Kenneth Russell <kbr at google.com>
>> >> > wrote:
>> >> >>
>> >> >> It should simply reference the Uint8ClampedArray, not copy it or do
>> >> >> anything else esoteric. The only way to display an ImageData in the
>> >> >> 2D
>> >> >> canvas context is via the putImageData API. I am not proposing
>> >> >> changing those semantics.
>> >> >>
>> >> >> -Ken
>> >> >>
>> >> >>
>> >> >>
>> >> >> On Mon, Mar 11, 2013 at 5:00 PM, Rik Cabanier <cabanier at gmail.com>
>> >> >> wrote:
>> >> >> > Do you expect that createImageData creates an internal copy of the
>> >> >> > Uint8ClampedArray object or is it live?
>> >> >> >
>> >> >> >
>> >> >> > On Mon, Mar 11, 2013 at 4:28 PM, Kenneth Russell <kbr at google.com>
>> >> >> > wrote:
>> >> >> >>
>> >> >> >> It would be useful to be able to create an ImageData [1] object
>> >> >> >> with
>> >> >> >> preexisting data. The main use case is to display arbitrary data
>> >> >> >> in
>> >> >> >> the 2D canvas context with no data copies.
>> >> >> >>
>> >> >> >> Proposed IDL:
>> >> >> >>
>> >> >> >> [NoInterfaceObject]
>> >> >> >> interface ImageDataFactories {
>> >> >> >>   ImageData createImageData(Uint8ClampedArray data, double sw,
>> >> >> >> double
>> >> >> >> sh);
>> >> >> >> };
>> >> >> >> Window implements ImageDataFactories;
>> >> >> >> WorkerGlobalScope implements ImageDataFactories;
>> >> >> >>
>> >> >> >> createImageData would throw an exception if the length of the
>> >> >> >> Uint8ClampedArray was not equal to 4 * floor(sw) * floor(sh), or
>> >> >> >> at
>> >> >> >> least, if the length of the array was less than this value.
>> >> >> >> (Similar
>> >> >> >> wording would be used to that of CanvasRenderingContext2D's
>> >> >> >> createImageData.)
>> >> >> >>
>> >> >> >> I don't think it is necessary to provide a createImageDataHD in
>> >> >> >> this
>> >> >> >> interface. The caller will know the devicePixelRatio and
>> >> >> >> determine
>> >> >> >> whether to generate high-DPI data.
>> >> >> >>
>> >> >> >> [1]
>> >> >> >>
>> >> >> >>
>> >> >> >>
>> >> >> >> http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#imagedata
>> >> >> >>
>> >> >> >> Comments?
>> >> >> >>
>> >> >> >> Thanks,
>> >> >> >>
>> >> >> >> -Ken
>> >> >> >
>> >> >> >
>> >> >
>> >> >
>> >
>> >
>
>



More information about the whatwg mailing list