javascriptcanvaspurescriptputimagedata

How to set a property of a typed object while retaining it's type?


I have a specific case, but I'm also curious in general.

I have a canvas, I get it's ImgData using createImageData, and I set it's data property to be something else, in order to then use PutImageData and pass it this modified ImgData.

My PureScript version fails, although writing the logic in Javascript in the console works flawlessly. The error is:

foreign.js:423 Uncaught TypeError: Failed to execute 'putImageData' on 'CanvasRenderingContext2D': 
parameter 1 is not of type 'ImageData'.

In PureScript:

getCleanBuffer :: forall e. Context2D -> Eff (canvas :: Canvas | e) ImageData
getCleanBuffer ctx = do
  imgData <- getImageData ctx 0.0 0.0 160.0 144.0
  return imgData { data = buffer }
 where buffer = asUint8ClampedArray $ A.replicate (160*144*4) 0

Writing this javascript in console works (arr is 160*144*4 zeroes)

var arr2 = new Uint8ClampedArray(arr);
var imgData = ctx.getImageData(0,0,160,144);
imgData.data = arr2;
ctx.putImageData(imgData,0,0);

I've debugged getCleanBuffer and here's what I found

var getCleanBuffer = function (ctx) {
var buffer = Data_TypedArray.asUint8ClampedArray(Data_Array.replicate((160 * 144 | 0) * 4 | 0)(0));
return function __do() {
    var v = Graphics_Canvas.getImageData(ctx)(0.0)(0.0)(160.0)(144.0)();
    var $9 = {};
    for (var $10 in v) {
        if (v.hasOwnProperty($10)) {
            $9[$10] = v[$10];
        };
    };
    $9.data = buffer;
    return $9;

This $9 (ImgData copy with modified data) variable, when inspected, is revealed not to be of type ImgData, like v (the original ImgData) was, but it's just an 'Object'. It's missing the width and height which v had, but even when I fix it during debug I still get the same error.

I think the problem is that the new object is a typeless record. The fact that the width and height were not copied is another problem.

I can write a javascript function to set the data attribute manually. But this case in general, seems like a common one, surely I'm missing something, am I not?

Thanks!


Solution

  • In this case the type of ImageData is incorrect, it's a type synonym for a record, but it is not safe to treat JavaScript class typed values as records for exactly the reason you're discovering here.

    I've opened a bug report as Phil suggested.

    For the general case, this is where lenses come in handy. You'd define a lens like _data :: LensP ImageData Uint8ClampedArray, and then could do imgData # _data %~ buffer to achieve something similar to the update syntax.