Search code examples
paperjssvg.js

Rendering imageData to new canvas


I'm following a tutorial by George Francis in the tutorial after some initial examples he shows how to use image data to create random layouts.

I'm trying to work out how to get the image data from a canvas created using paper.js, as I need to get the rgb values from each individual pixel on the canvas

Link to codepen

Unknowns:

  • Do I need to use the rasterize() method on the shape I've created?

Currently I am attempting the following:

// create a white rectangle the size of the view (not sure I need this but doing it so that there are both white and black pixels)
const bg = new paper.Path.Rectangle({
  position: [0,0],
  size: view.viewSize.multiply(2),
  fillColor: 'white'
})

// create a black rectangle smaller than the view size
const shape = new paper.Path.RegularPolygon({
  radius: view.viewSize.width * 0.4,
  fillColor: 'black',
  strokeColor: 'black',
  sides: 4,
  position: view.center
})

// So far so good shapes render as expected. Next put the shapes in a group
const group = new paper.Group([bg,shape])
// rasterise the group (thinking it needs to be rasterized to get the pixel data, but again , not sure?)
group.rasterize()
// iterate over each pixel on the canvas and get the image data
for(let x = 0; x < width; x++){
  for(let y = 0; y < height; y++){
    const { data } = view.context.getImageData(x,y,1,1)
    console.log(data)
  }
}

Expecting: To get an array of buffers where if the pixel is white it would give me

Uint8ClampedArray(4) [0, 0, 0, 0, buffer: ArrayBuffer(4),
byteLength: 4, byteOffset: 0, length: 4]
0: 255
1: 255
2: 255
//(not sure if the fourth index represents (rgb'a')?
3: 255
buffer: 
ArrayBuffer(4)
byteLength: 4
byteOffset: 0
length: 4
Symbol(Symbol.toStringTag): (...)
[[Prototype]]: TypedArray

and if the pixel is black I should get

Uint8ClampedArray(4) [0, 0, 0, 0, buffer: ArrayBuffer(4),
    byteLength: 4, byteOffset: 0, length: 4]
    0: 0
    1: 0
    2: 0
    3: 0
    buffer: 
    ArrayBuffer(4)
    byteLength: 4
    byteOffset: 0
    length: 4
    Symbol(Symbol.toStringTag): (...)
    [[Prototype]]: TypedArray

i.e either 255,255,255 (white) or 0,0,0(black)

Instead, all the values are 0,0,0?


Solution

  • I think that your issue was that at the time where you are getting the image data, your scene is not yet drawn to the canvas.
    In order to make sure it's drawn, you just need to call view.update().

    Here's a simple sketch demonstrating how it could be used.
    Note that you don't need to rasterize your scene if you are using the Canvas API directly to manipulate the image data. But you could also rasterize it and take advantage of Paper.js helper methods like raster.getPixel().

    // Draw a white background (you effectively need it otherwise your default
    // pixels will be black).
    new Path.Rectangle({
        rectangle: view.bounds,
        fillColor: 'white'
    });
    
    // Draw a black rectangle covering most of the canvas.
    new Path.Rectangle({
        rectangle: view.bounds.scale(0.9),
        fillColor: 'black'
    });
    
    // Make sure that the scene is drawn into the canvas.
    view.update();
    
    // Get the canvas image data.
    const { width, height } = view.element;
    const imageData = view.context.getImageData(0, 0, width, height);
    
    // Loop over each pixel and store all the different colors to check that this works.
    const colors = new Set();
    const length = imageData.data.length;
    for (let i = 0; i < length; i += 4) {
        const [r, g, b, a] = imageData.data.slice(i, i + 4);
        const color = JSON.stringify({ r, g, b, a });
        colors.add(color);
    }
    console.log('colors', [...colors]);