How can I implement a zoom function in a 2D canvas?

164 views Asked by At

I'm trying to create a library for procedural textures in JavaScript and for some reason I can't really wrap my head around why my zooming function wont work. I tried lots of things but I always get funky results instead of what I originally wanted to achieve. Basically, if I zoom into a noise texture I want it to appear all blocky.

My current implementation:

this.Zoom = function(factor) {
  var imageData, curData, newData,
    zoomFactor = Math.pow(2, factor);

  curData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);    
  newData = ctx.createImageData(curData);

  for (var y = 0; y < ctx.canvas.height; y++) {
    for (var x = 0; x < ctx.canvas.width; x++) {
      var curIndex = y * ctx.canvas.width + x;
      var targetIndex = Math.floor(y / zoomFactor + x / zoomFactor) * 4;

      newData.data[curIndex]     = curData.data[targetIndex];
      newData.data[curIndex + 1] = curData.data[targetIndex + 1];
      newData.data[curIndex + 2] = curData.data[targetIndex + 2];
      newData.data[curIndex + 3] = curData.data[targetIndex + 3];
    }
  }

  ctx.putImageData(newData, 0, 0);

  return this;
}

And here's my JSFiddle: http://jsfiddle.net/j18eqgrq/

For some reason I get this weird effect and I can't wrap my head around it. I think I messed up something with the targetIndex.

2

There are 2 answers

0
Danny Ruijters On BEST ANSWER

In your inner for-loop the code should be:

var curIndex = y * ctx.canvas.width * 4 + x * 4;
var targetIndex = Math.floor(y / zoomFactor) * ctx.canvas.width * 4 + Math.floor(x / zoomFactor) * 4;

You should take the RGBA into account in the factor 4, and also the zoomed image index should be calculated through y * image_width + x

0
Philipp On

You can implement zooming by taking the original canvas and drawing it to a new canvas with the 9-argument form of context.drawImage. This form requires both a source- and a target-rectangle. When the sizes differ, the source is stretched accordingly.

Another method you can use when you haven't drawn anything yet and you want to draw it zommed from the start is to use context.scale to set a zoom factor and context.translate to set the zoom center.