I'm trying to update the ImageData of a Canvas's image context, and when I try to set elements in the data array, I get an error saying the array has type Js.Typed_array.Uint8ClampedArray.t
when something expected array('a)
.
Why can't I update a JS TypedArray implementation?
Here's my component code (simplified somewhat for clarity):
let make = _children => {
let map = FeatureMap.make(100, 100);
let (width, height) = map.dimensions;
{...component,
initialState: () => {
map: map,
canvasRef: ref(None)
},
didMount: self => switch (self.state.canvasRef^) {
| None => ()
| Some(canvas) => {
let ctx = getContext2d(canvas);
let imageData = createImageDataCoords(ctx, ~width=float_of_int(width), ~height=float_of_int(height));
let data = Webapi.Dom.Image.data(imageData);
Array.iteri((x, row) => {
Array.iteri((y, weight) => {
let index = (x * width + y) * 4;
let (r, g, b) = weight;
data[index + 0] = r;
data[index + 1] = g;
data[index + 2] = b;
data[index + 3] = 0;
}, row);
}, map.weights);
ctx |> putImageData(imageData, 0., 0., 0., 0., float_of_int(width), float_of_int(height));
}
},
render: _self => <canvas id="weight-map"
width={string_of_int(width)}
height={string_of_int(width)}
ref={_self.handle(setCanvasRef)}></canvas>
};
};
To the compiler, an array('a)
is not the same type as a Js.Typed_array.Uint8ClampedArray.t
, hence their operations (including indexing) are not interchangeable. This is the same principle by which you can't add an int and a float.
To set a typed array element, you need to find (or write) a binding that allows you to do that explicitly, rather than using the indexing operator. To do that, you can look in the Js.Typed_array
module–there is a module type S
which we can take to mean 'all typed array modules must conform to this module signature'. And that includes the Js.Typed_array.Uint8ClampedArray
module. So you can use the S
module type's unsafe_set
function to set the typed array elements, because Js.Typed_array.Uint8ClampedArray
implements it:
let module UI8s = Js.Typed_array.Uint8ClampedArray;
UI8s.unsafe_set(data, index, r);
UI8s.unsafe_set(data, index + 1, g);
UI8s.unsafe_set(data, index + 2, b);
UI8s.unsafe_set(data, index + 3, 0);