1

I'm trying to draw an image with d3 from a json file. This image data is then put into a canvas context to actually draw the image. I would like to rotate the resulting image but I cant find how. The code I actually use

$(document).ready(function() {
    d3.json("resources/image.json").then(function(image) {
        var canvas = d3.select("colorImage")
                       .append("canvas")
                       .attr("width", 200)
                       .attr("height", 200);


        var context = canvas.node().getContext("2d");
        var image_data = context.createImageData(200, 200);

        image_data.data.set(image);
        context.rotate(60); 
        context.putImageData(image_data, 0, 0);  // <---- image not rotated
        context.fillRect( 100, 100, 20, 20 );  // <--- rectangle rotated
    }).catch(function(error){
        if (error) throw error;
    });
})
GuillaumeA
  • 3,493
  • 4
  • 35
  • 69

1 Answers1

4

putImageData() and getImageData() are not affected by the context's transformation matrix.

To rotate it, the easiest is to create an ImageBitmap from it and to draw that ImageBitmap using drawImage():

(async () => {
  const canvas = document.getElementById( 'canvas' );
  const context = canvas.getContext( '2d' );
  // make some noise
  const image = new Uint8Array( Uint32Array.from(
      { length: 200 * 200 },
      _ => (Math.random() * 0xFFFFFFFF)
    ).buffer );

  const image_data = context.createImageData( 200, 200 );
  image_data.data.set( image );
  
  const center_w = canvas.width / 2;
  const center_h = canvas.height / 2;
  const bitmap = await createImageBitmap( image_data );
  context.translate( center_w, center_h ); 
  context.rotate( 60 );
  context.translate( -center_w, -center_h ); 
  context.drawImage( bitmap, center_w-100, center_h-100 );  // <---- image rotated
  context.lineWidth = 4;
  context.strokeStyle = 'green';
  context.strokeRect( center_w-100, center_h-100, 200, 200 );  // <--- rectangle rotated
})().catch( console.error );
<canvas id="canvas" height="300" width="300"></canvas>

And for browsers that do not support createImageBitmap() method, you can monkey-patch this exact use quite easily by using an HTMLCanvasElement:

/* if( !window.createImageBitmap ) { */ // for demo we force monkey-patch
 monkeyPatchCreateImageBitmap();
/*} */

(async () => {
  const canvas = document.getElementById( 'canvas' );
  const context = canvas.getContext( '2d' );
  // make some noise
  const image = new Uint8Array( Uint32Array.from(
      { length: 200 * 200 },
      _ => (Math.random() * 0xFFFFFFFF)
    ).buffer );

  const image_data = context.createImageData( 200, 200 );
  image_data.data.set( image );
  
  const center_w = canvas.width / 2;
  const center_h = canvas.height / 2;
  const bitmap = await createImageBitmap( image_data );
  context.translate( center_w, center_h ); 
  context.rotate( 60 );
  context.translate( -center_w, -center_h ); 
  context.drawImage( bitmap, center_w-100, center_h-100 );  // <---- image rotated
  context.lineWidth = 4;
  context.strokeStyle = 'green';
  context.strokeRect( center_w-100, center_h-100, 200, 200 );  // <--- rectangle rotated
})().catch( console.error );


// minimalistic version that only accepts ImageData, and no clipping
function monkeyPatchCreateImageBitmap() {
  window.createImageBitmap = function( source ) {
    if( source instanceof ImageData ) {
      const dest = document.createElement( 'canvas' );
      dest.width = source.width;
      dest.height = source.height;
      dest.getContext( '2d' ).putImageData( source, 0, 0 );
      return Promise.resolve( dest );
    }
  };
}
<canvas id="canvas" height="300" width="300"></canvas>
Kaiido
  • 123,334
  • 13
  • 219
  • 285