WJA
WJA

Reputation: 7004

copy only a part of a canvas using context.putImageData()

I am trying to fill a canvas iteratively piece by piece. Moreover, I want an the canvas to be filled in iteratively with squares of the original image.

For performance reasons, I first render the original image to an offscreen canvas and then get the imagedata using getImageData().

In the onscreen canvas, I then try to iteratively plot the pieces using the following code:

        var x = 0;
        var y = 0;
        for (var i = 0; i <= (nbPiecesHorizontal*nbPiecesVertical-1); i++) {

            onscreenContext.putImageData(
                offScreenData.imageData, 
                x*pieceWidth, 
                y*pieceHeight, 
                x*pieceWidth, 
                y*pieceHeight, 
                pieceWidth, 
                pieceHeight);


            // iter
            x = x + 1;
            if (x == (nbPiecesHorizontal)) {
                x = 0;
                y = y +1;
            }
        };

However, I only get that a couple of pieces are drawn, more specific only the pieces in the corners. I want off course, all pieces to be drawn. How can I solve this?

Upvotes: 0

Views: 1462

Answers (1)

markE
markE

Reputation: 105015

Using .getImageData and .putImageData are slow and expensive methods to draw to the canvas because they manipulate the canvas at the pixel level.

A better way of incrementally draw an image to canvas is to use the clipping version of context.drawImage. The clipping version of drawImage will clip a specified portion of your original image and draw it onto the canvas.

Using drawImage is much faster because pixels are just copied from the source image to the destination canvas instead of going through the additional "filter" pixel manipulation.

Here's how the clipping version of drawImage works:

context.drawImage(

    sourceImage,  // the source image to clip from

    sX,           // the left X position to start clipping 
    sY,           // the top Y position to start clipping
    sW,           // clip this width of pixels from the source
    wH,           // clip this height of pixels from the source

    dX,           // the left X canvas position to start drawing the clipped sub-image
    dY,           // the top Y canvas position to start drawing the clipped sub-image
    dW,           // scale sW to dW and draw a dW wide sub-image on the canvas
    dH            // scale sH to dH and draw a dH high sub-image on the canvas

}

Example code and a Demo:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

var nextTime=0;
var duration=500;
var nextX=0;
var nextY=0;
var cols=5;
var rows=3;
var iw,ih,colWidth,rowHeight;

var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/annotateMe.jpg";
function start(){
  iw=canvas.width=img.width;
  ih=canvas.height=img.height;
  colWidth=iw/cols;
  rowHeight=ih/rows;
  requestAnimationFrame(animate);
}


function animate(time){

  // check if the elapsed time has reached nextTime
  // if not, just request another animation loop
  if(time<nextTime){requestAnimationFrame(animate); return;}

  // reset nextTime 
  nextTime=time+duration;

  // calc the x,y of the subimage to clip from the whole img
  var x=parseInt(nextX*colWidth);
  var y=parseInt(nextY*rowHeight);

  // clip the subimage from the source image
  // and draw the subimage onto the canvas
  ctx.drawImage(
    // use img as the source for clipping
    img,
    // clip the next cell from the img at x,y 
    // and clip out a subimage with size colWidth x rowHeight
    x,y,colWidth,rowHeight,
    // draw the clipped subimage to the canvas
    x,y,colWidth,rowHeight
  );

  // calc the next subimage x,y for the next loop
  var imageIsComplete=false;
  nextX++;
  if(nextX>cols-1){
    nextX=0;
    nextY++;
    if(nextY>rows-1){ imageIsComplete=true; }
  }

  // if the image is not complete, request another loop
  if(!imageIsComplete){ requestAnimationFrame(animate); }

  if(imageIsComplete){alert('Image is complete!');}

}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>

Upvotes: 4

Related Questions