Pixel perfect scaling of images while zooming the canvas with fabric.js

夙愿已清 提交于 2020-01-17 12:15:09

问题


My software is a kind of pixel art paint program with brushes, fabric.js was heavily modified in order to have pixel rounded translation (the position is always an integer) for the fabric.js objects, however resizing images and making their pixels fit the canvas in a 1:1 ratio is still an issue.

Right now, the fabric.js zoomToPoint function is used to be able to zoom on the canvas with the mouse wheel, when images aren't scaled, their pixels fit with the canvas but when i scale any objects down (or up), pixels does not fit anymore with the canvas so the pixels of the object are actually smaller or bigger than the canvas pixel.

Screenshots of the problem with a zoom level of 8 :

Original image dimensions (no problems, each pixels fit the canvas pixels)

Resized image, pixels of the image does not fit the canvas pixels anymore

How can the pixels of the images fit the canvas pixels in a 1:1 ratio when scaling them?


回答1:


By its nature, resizing involves resampling the original image and interpolating/convoluting the existing pixels into the resized space. The resulting image is meant to be visually appealing at its new size. But for an image resized by X, the resulting pixels certainly do not have an X:1 relationship anymore.

To zoom-in you need a "projection" where each single 1x1 pixel is expanded into a 2x2 pixel group (or a 3x3 pixel group, etc).

To zoom-out you need a "projection" where each 2x2 pixel group is condensed into a single 1x1 pixel set.

To zoom-out, the source (before scaling) must be made up of pixel groups. This means that you cannot zoom-out an image below its original size without using resampling. There is no projection available to zoom-out below 1X. A workaround is to have the user draw at 2X (or 3X or 4X) projection so you can zoom-out.

FabricJS does not do projections natively...You'll have to use a temporary canvas element to do projections.

Here's example code to zoom using projections:

var zoom=2;
var img=new Image();
img.crossOrigin='anonymous';
img.onload=start;
//img.src="sun.png";
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/sun.png";
function start(){

  var iw=img.width;
  var ih=img.height;   

  img.width=iw;
  img.height=ih;
  // set the "current zoom factor" on the original image to 1x
  img.zoom=1;

  // test: resize by 4X
  var zoom4x=resize(img,4,'canvas4x');
  document.body.appendChild(zoom4x);

  // test: resize the 4X back down to 2X
  var zoom2x=resize(zoom4x,0.50,'canvas2x');
  document.body.appendChild(zoom2x);

  // test: resize the 2X back down to 1X
  var zoom1x=resize(zoom2x,0.50,'canvas1x');
  document.body.appendChild(zoom1x);

  // test: resize the 1X down to half-size
  var zoomHx=resize(zoom1x,0.50,'canvas1x');
  if(zoomHx){document.body.appendChild(zoomHx)};

  // display the original image
  document.body.appendChild(img);


}



var resize = function(img,scale,id){

  var zoom=parseInt(img.zoom*scale);
  if(zoom<1){
    console.log('Cannot recale image to less than original size');
    return;
  }

  // get the pixels from the original img using a canvas
  var c1=document.createElement('canvas');
  var cw1=c1.width=img.width;
  var ch1=c1.height=img.height;
  var ctx1=c1.getContext('2d');
  ctx1.drawImage(img,0,0);
  var imgData1=ctx1.getImageData(0,0,cw1,ch1);
  var data1=imgData1.data;

  // create a canvas to hold the resized pixels
  var c2=document.createElement('canvas');
  c2.id=id;
  c2.zoom=zoom;
  var cw2=c2.width=cw1*scale;
  var ch2=c2.height=ch1*scale;
  var ctx2=c2.getContext('2d');
  var imgData2=ctx2.getImageData(0,0,cw2,ch2);
  var data2=imgData2.data;

  // copy each source pixel from c1's data1 into the c2's data2
  for(var y=0; y<ch2; y++) {
    for(var x=0; x<cw2; x++) {
      var i1=(Math.floor(y/scale)*cw1+Math.floor(x/scale))*4;
      var i2 =(y*cw2+x)*4;            
      data2[i2]   = data1[i1];
      data2[i2+1] = data1[i1+1];
      data2[i2+2] = data1[i1+2];
      data2[i2+3] = data1[i1+3];
    }
  }

  // put the modified pixels back onto c2
  ctx2.putImageData(imgData2,0,0);

  // return the canvas with the zoomed pixels
  return(c2);
}
body{ background-color: ivory; }
canvas{border:1px solid red;}
<h4>Left: 4X, 2X, 1X projections, Right:Original Image</h4>


来源:https://stackoverflow.com/questions/29633200/pixel-perfect-scaling-of-images-while-zooming-the-canvas-with-fabric-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!