iddan / react-native-canvas

A Canvas component for React Native
MIT License
992 stars 172 forks source link

putImageData is not drawing anything in the canvas #180

Open QuevedoIB opened 4 years ago

QuevedoIB commented 4 years ago

canvasContext.putImageData(data,0,0) not drawing anything

I'm trying to pass an ImageData element to the canvas so it's displayed on it, my code looks like:

import Canvas, {ImageData} from 'react-native-canvas';

...

const handleCanvas = async canvas => {
    canvas.width = sizes.width;
    canvas.height = sizes.height;
    const ctx = canvas.getContext('2d');

    const data = await ctx.createImageData(
      encodedImage.width,
      encodedImage.height,
      encodedImage.data,
    );

    console.log(
      'data',
      data,
      data instanceof ImageData,
      'encoded',
      encodedImage,
      encodedImage instanceof ImageData,
    );

    await ctx.putImageData(data, 0, 0);
  };

...

{encodedImage && (
            <Canvas
              ref={handleCanvas}
              style={{borderWidth: 1, borderColor: 'red', zIndex: 1000000}}
            />
)}

Outputs:

Screenshot 2020-04-23 at 12 09 49

encodedImage is a black and white ImageData mask from https://www.npmjs.com/package/@tensorflow-models/body-pix#returns-4

Canvas is that red box:

Screenshot 2020-04-23 at 12 27 56

Nothing is painted inside when using putImageData with Outputs data.

If I try to pass the ImageData to another canvas context I get the following error: Error: Argument 1('imagedata') to CanvasRenderingContext2D.putImageData must be an instance of ImageData

iddan commented 4 years ago

createImageData() is not supported currently (though it should be pretty easy to implement PRs are welcome). Can you try using new ImageData(...) instead?

QuevedoIB commented 4 years ago

Reason I'm not using new ImageData(...) is that it gives me error:

Screenshot 2020-04-23 at 12 36 17 Screenshot 2020-04-23 at 12 37 47

This error is related to https://stackoverflow.com/questions/38556730/imagedata-byte-length-is-not-a-multiple-of-4-width which says length of Uint8ClampedArray needs to be the multiple of width, height and 4. In my image case 612480 / (480 319 4) = 1 so it's fine but it still gives me the error. @iddan

QuevedoIB commented 4 years ago

Anyways my expectation is that given the following input:

Screenshot 2020-04-23 at 13 00 05

(An ImageData given from another source)

putImageData should work as expected but it gives the following error:

Screenshot 2020-04-23 at 13 03 33

Because it's not an initialized ImageData from new ImageData/createImageData.

 const handleCanvas = async canvas => {
    canvas.width = sizes.width;
    canvas.height = sizes.height;
    const ctx = canvas.getContext('2d');

    console.log('encoded', encodedImage, encodedImage instanceof ImageData);

    await ctx.putImageData(encodedImage, 0, 0);
  };
QuevedoIB commented 4 years ago

My workaround is to initialize with new ImageData(...) and then add the properties to the instance.

 const handleCanvas = async canvas => {
    canvas.width = sizes.width;
    canvas.height = sizes.height;
    const ctx = canvas.getContext('2d');

    const data = new ImageData(canvas);

    data.data = encodedImage.data;
    data.width = encodedImage.width;
    data.height = encodedImage.height;

    console.log(data, data instanceof ImageData);

    await ctx.putImageData(data, 0, 0);
  };
Screenshot 2020-04-23 at 14 15 56

That way I get the desired ImageData instance with the proper data, but putImageData still does nothing to the canvas. (Nothing is drawn) Did someone get putImageData working before?

Screenshot 2020-04-23 at 14 17 18
bbhopesh commented 4 years ago

I am also facing bug with putDataImage. When I run example app without any changes, it renders differently on different platforms. Check top left rectangle in two images below, one is from an emulator and one is from real phone. However, both are rendering wrong. If code was behaving correctly, entire top left rectangle would have been black. image

image

esbenvb commented 1 year ago

I think I made something work. I have one canvas drawCanvas where I can draw with free hand, and another canvas canvas2 where I want to put the data I have drawn.

Acoording to my research, the important thing is that the ImageData you draw on a canvas has a reference to that canvas.

      const drawingData = await drawCanvas
        .getContext('2d')
        .getImageData(0, 0, drawCanvas.width, drawCanvas.height);

      const data = Object.values(drawingData.data);
      const newImageData = new ImageData(
        canvas2,
        data,
        drawCanvas.width,
        drawCanvas.height,
      );
      context2.putImageData(newImageData, 0, 0);