0

So i'm adding some image manipulation functions to one of our company projects. Part of the feature is an image cropper with the desire to 'auto-detect' the cropped image to some degree. If our guess is bad they can just drag & drop the cropper points, but most images people should be able to be auto-cropped.

My issue is when i'm putting the data back into the canvas indexes that work don't seem make any sense to me based on the documentation. I'm trying to take the rect I find and convert he canvas to a single image size that will now contain my whole rect.

    let width = right - left + 1, height = bottom - top + 1;

    canvas.width = width;
    canvas.height = height;

    ctx.putImageData(imageBuffer, -left, -top, left, top, width,height);

This gives me the correct image. I would have expected based on the documentation that the below code would be correct. I verified in mspaint that my indexes for the rect are correct so I know it isn't my algorithm coming up with weird numbers.

    let width = right - left + 1, height = bottom - top + 1;

    canvas.width = width;
    canvas.height = height;

    ctx.putImageData(imageBuffer, 0, 0, left, top, width,height);

Why do you have to put a negative indexing for the 2nd & 3rd argument? I've verified it behaves like this in both Chrome & Firefox.

Chase R Lewis
  • 2,119
  • 1
  • 22
  • 47

1 Answers1

1

Yes, it might be a bit confusing, but when you putImageData, the destinationWidth and destinationHeight you would have in e.g drawImage, are always equal to the ImageData's width and height.

The 4 last params of putImageData(), dirtyX, dirtyY, dirtyWidth and dirtyHeight values are relative to the ImageData's boundaries.

So with the first two params, you just set the position of the ImageData's boundaries, with the 4 others, you set the position of your pixels in this ImageData's boundary.

var ctx = canvas.getContext('2d');

var imgBound = {
  x: 10,
  y: 10,
  width: 100,
  height: 100
},
innerImg = {
  x: 20,
  y: 20,
  width: 200,
  height: 200
};
// a new ImageData, the size of our canvas
var img = ctx.createImageData(imgBound.width, imgBound.height);
// fill it with noise
var d = new Uint32Array(img.data.buffer);
for(var i=0;i<d.length; i++)
  d[i] = Math.random() * 0xFFFFFFFF;

function draw() {

ctx.putImageData(img,
  imgBound.x,
  imgBound.y,
  innerImg.x,
  innerImg.y,
  innerImg.width,
  innerImg.height
);
// the ImageData's boundaries
ctx.strokeStyle = 'blue';
ctx.strokeRect(imgBound.x, imgBound.y, imgBound.width, imgBound.height);

// our pixels boundaries relative to the ImageData's bbox
ctx.strokeStyle = 'green';
ctx.strokeRect(
 // for stroke() we need to add the ImageData's translation
  innerImg.x + imgBound.x,
  innerImg.y + imgBound.y,
  innerImg.width,
  innerImg.height
  );
}
var inner_direction = -1,
  imgBound_direction = -1;
function anim() {
  innerImg.width += inner_direction;
  innerImg.height += inner_direction;
  if(innerImg.width <= -50 || innerImg.width > 200) inner_direction *= -1;
  imgBound.x += imgBound_direction;
  if(imgBound.x <= 0 || imgBound.x > 200)
    imgBound_direction *= -1;
  
  ctx.clearRect(0,0,canvas.width,canvas.height);
  draw();
  requestAnimationFrame(anim);
}
anim();
canvas{border: 1px solid;}
<canvas id="canvas" width="300" height="300"></canvas>
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • I don't know what the rect is without scanning the pixels so need the getImageData, technically I could probably draw the image again rather than putting the image back since it is still on the canvas. Ah it's relative to the image data coordinates even though it draws to the canvas? Bit odd but that's the only way the numbers line up. You'd think the first 2 points would be in the canvases coordinate system since its supposed to be the destination. – Chase R Lewis Mar 27 '18 at 04:54
  • @user2927848 ah I missed the *auto-detect* feature in your question, then putImageData is ok, since you already got it. And yes, the first two points are in the destination canvas coords, but in your code your are translating the pixels inside their ImageData's boundaries, so you need to translate the whole ImageData to catch up this translation. – Kaiido Mar 27 '18 at 05:47