Dariel Pratama
Dariel Pratama

Reputation: 1675

faster method of copying image data

i have the following script

// copy image data to secondary canvas
var pixelData = contextSource.getImageData(x - (lineWidth/2), y - (lineWidth/2), lineWidth, lineWidth);
var tmpCanvas = document.createElement('canvas');
tmpCanvas.width = tmpCanvas.height = lineWidth;
var tmpContext = tmpCanvas.getContext('2d');

tmpContext.putImageData(pixelData, 0, 0);

contextDest.save();
contextDest.arc(x, y, (lineWidth/2), 0, 2*Math.PI);
contextDest.clip();
contextDest.drawImage(tmpCanvas, x - (lineWidth/2), y - (lineWidth/2));
contextDest.restore();

the script is sampling image data from canvas source when mouse is moving over the source, then copy it to destination. the script is working good, but a bit slow. here is the result when i move the mouse pointer a bit faster.

enter image description here

is there any faster method than mine? please help

Upvotes: 3

Views: 354

Answers (1)

markE
markE

Reputation: 105015

enter image description here

@Banana has a good point.

Instead of drawing just the mouse positions, try connecting the positions into a single continuous line..

If you want the rounded effect in your illustration, you can set:

context.lineCap='round';
context.lineJoin='round';

As to masking faster...

A much faster way of masking your image is to:

  • draw your single line on the second canvas.
  • Use compositing to make all new draws be visible only where existing pixels are opaque. This compositing is 'source-in' and is set using: context.globalCompositeOperation='source-in'
  • Draw your image onto the second canvas. The image will show only where the line was.

Using compositing is much faster because compositing is hardware accelerated and is optimized by the browser.

That compositing code might look like this:

function draw(){
    // clear the second canvas
    ctx1.clearRect(0,0,cw,ch);

    // draw your continuous line 
    ctx1.beginPath();
    ctx1.moveTo(points[0].x,points[0].y);
    for(var i=1;i<points.length;i++){
        p=points[i];
        ctx1.lineTo(p.x,p.y);
    }
    ctx1.stroke();

    // set compositing so new draws only appear in 
    // existing opaque pixels
    ctx1.globalCompositeOperation='source-in';

    // draw the image
    // the image will only be visible in the exising line
    ctx1.drawImage(img,0,0);

    // be tidy! return compositing to its default mode
    ctx1.globalCompositeOperation='source-over';
}

Here's an example and Demo:

var canvas1=document.getElementById("canvas1");
var ctx1=canvas1.getContext("2d");

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var scrollX=$canvas.scrollLeft();
var scrollY=$canvas.scrollTop();

var isDown=false;
var startX;
var startY;

var points=[];
var cw,ch;

var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/tempHouse%201.jpg";
function start(){
  cw=canvas.width=canvas1.width=img.width;
  ch=canvas.height=canvas1.height=img.height;

  ctx1.lineCap = "round";
  ctx1.lineJoin = "round";
  ctx1.lineWidth=20;

  ctx.drawImage(img,0,0);

  $("#canvas").mousedown(function(e){handleMouseDown(e);});
  $("#canvas").mousemove(function(e){handleMouseMove(e);});
  $("#canvas").mouseup(function(e){handleMouseUp(e);});
  $("#canvas").mouseout(function(e){handleMouseOut(e);});
}


function draw(){
  // clear the second canvas
  ctx1.clearRect(0,0,cw,ch);

  // draw your continuous line 
  ctx1.beginPath();
  ctx1.moveTo(points[0].x,points[0].y);
  for(var i=1;i<points.length;i++){
    p=points[i];
    ctx1.lineTo(p.x,p.y);
  }
  ctx1.stroke();

  // set compositing so new draws only appear in 
  // existing opaque pixels
  ctx1.globalCompositeOperation='source-in';

  // draw the image
  // the image will only be visible in the exising line
  ctx1.drawImage(img,0,0);

  // be tidy! return compositing to its default mode
  ctx1.globalCompositeOperation='source-over';
}


function handleMouseDown(e){
  e.preventDefault();
  e.stopPropagation();
  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);
  points.length=0;
  points.push({x:mouseX,y:mouseY});
  isDown=true;
}

function handleMouseUp(e){
  e.preventDefault();
  e.stopPropagation();
  isDown=false;
  draw();
}

function handleMouseOut(e){
  e.preventDefault();
  e.stopPropagation();
  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);
  points.push({x:mouseX,y:mouseY});
  isDown=false;
  draw();
}

function handleMouseMove(e){
  if(!isDown){return;}
  e.preventDefault();
  e.stopPropagation();
  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);
  points.push({x:mouseX,y:mouseY});
  draw();
}
body{ background-color: ivory; }
canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<canvas id="canvas" width=300 height=300></canvas><br>
<canvas id="canvas1" width=300 height=300></canvas>

Upvotes: 3

Related Questions