如何在 HTML 画布中缩放 imageData?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3448347/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-29 04:04:07  来源:igfitidea点击:

How to scale an imageData in HTML canvas?

htmlcanvasgetimagedata

提问by Masiar

I have a canvas in my webpage; I create a new Image data in this canvas then I modify some pixel through myImgData.data[] array. Now I would like to scale this image and make it bigger. I tried by scaling the context but the image remains small. Is it possible to do this? Thanks

我的网页中有一个画布;我在这个画布中创建了一个新的图像数据,然后我通过 myImgData.data[] 数组修改了一些像素。现在我想缩放这个图像并使它更大。我尝试缩放上下文,但图像仍然很小。是否有可能做到这一点?谢谢

回答by Castrohenge

You could draw the imageData to a new canvas, scale the original canvas and then draw the new canvas to the original canvas.

您可以将 imageData 绘制到新画布上,缩放原始画布,然后将新画布绘制到原始画布上。

Something like this should work:

这样的事情应该工作:

var imageData = context.getImageData(0, 0, 100, 100);
var newCanvas = $("<canvas>")
    .attr("width", imageData.width)
    .attr("height", imageData.height)[0];

newCanvas.getContext("2d").putImageData(imageData, 0, 0);

context.scale(1.5, 1.5);
context.drawImage(newCanvas, 0, 0);

Here's a functioning demo http://jsfiddle.net/Hm2xq/2/.

这是一个功能演示http://jsfiddle.net/Hm2xq/2/

回答by Casey Rodarmor

I needed to do it without the interpolation that putImageData() causes, so I did it by scaling the image data into a new, resized ImageData object. I can't think of any other time I've thought that using 5 nested for loops was a good idea:

我需要在没有 putImageData() 导致的插值的情况下完成它,所以我通过将图像数据缩放到一个新的、调整大小的 ImageData 对象来做到这一点。我想不出任何其他时间我认为使用 5 个嵌套的 for 循环是个好主意:

function scaleImageData(imageData, scale) {
  var scaled = c.createImageData(imageData.width * scale, imageData.height * scale);

  for(var row = 0; row < imageData.height; row++) {
    for(var col = 0; col < imageData.width; col++) {
      var sourcePixel = [
        imageData.data[(row * imageData.width + col) * 4 + 0],
        imageData.data[(row * imageData.width + col) * 4 + 1],
        imageData.data[(row * imageData.width + col) * 4 + 2],
        imageData.data[(row * imageData.width + col) * 4 + 3]
      ];
      for(var y = 0; y < scale; y++) {
        var destRow = row * scale + y;
        for(var x = 0; x < scale; x++) {
          var destCol = col * scale + x;
          for(var i = 0; i < 4; i++) {
            scaled.data[(destRow * scaled.width + destCol) * 4 + i] =
              sourcePixel[i];
          }
        }
      }
    }
  }

  return scaled;
}

I hope that at least one other programmer can copy and paste this into their editor while muttering, "There but for the grace of god go I."

我希望至少有其他程序员可以将其复制并粘贴到他们的编辑器中,同时喃喃自语:“看在上帝的恩典之下,我去吧。”

回答by user3079576

I know it's an old subject, but since people like may find it useful, I add my optimization to the code of rodarmor :

我知道这是一个古老的主题,但由于喜欢的人可能会发现它很有用,我将我的优化添加到了 rodarmor 的代码中:

function scaleImageData(imageData, scale) {
    var scaled = ctx.createImageData(imageData.width * scale, imageData.height * scale);
    var subLine = ctx.createImageData(scale, 1).data
    for (var row = 0; row < imageData.height; row++) {
        for (var col = 0; col < imageData.width; col++) {
            var sourcePixel = imageData.data.subarray(
                (row * imageData.width + col) * 4,
                (row * imageData.width + col) * 4 + 4
            );
            for (var x = 0; x < scale; x++) subLine.set(sourcePixel, x*4)
            for (var y = 0; y < scale; y++) {
                var destRow = row * scale + y;
                var destCol = col * scale;
                scaled.data.set(subLine, (destRow * scaled.width + destCol) * 4)
            }
        }
    }

    return scaled;
}

This code uses less loops and runs roughly 30 times faster. For instance, on a 100x zoom of a 100*100 area this codes takes 250 ms while the other takes more than 8 seconds.

这段代码使用的循环更少,运行速度大约快 30 倍。例如,在 100*100 区域的 100 倍变焦上,这个编码需要 250 毫秒,而另一个需要超过 8 秒。

回答by Sixtease

You can scale the canvas using the drawImage method.

您可以使用 drawImage 方法缩放画布。

context = canvas.getContext('2d');
context.drawImage( canvas, 0, 0, 2*canvas.width, 2*canvas.height );

This would scale the image to double the size and render the north-west part of it to the canvas. Scaling is achieved with the third and fourth parameters to the drawImage method, which specify the resulting width and height of the image.

这会将图像缩放到两倍大小并将其西北部分渲染到画布上。缩放是通过 drawImage 方法的第三个和第四个参数实现的,它们指定图像的结果宽度和高度。

See docs at MDN https://developer.mozilla.org/en-US/docs/DOM/CanvasRenderingContext2D#drawImage%28%29

请参阅 MDN 上的文档https://developer.mozilla.org/en-US/docs/DOM/CanvasRenderingContext2D#drawImage%28%29

回答by Chris Holmes

@Castrohenge's answer works, but as Muhammad Umer points out, it messes up the mouse coordinates on the original canvas after that. If you want to maintain the ability to perform additional scales (for cropping, etc.) then you need to utilize a second canvas (for scaling) and then fetch the image data from the second canvas and put that into the original canvas. Like so:

@Castrohenge 的答案有效,但正如 Muhammad Umer 指出的那样,此后它会弄乱原始画布上的鼠标坐标。如果您想保持执行额外缩放(用于裁剪等)的能力,那么您需要使用第二个画布(用于缩放),然后从第二个画布中获取图像数据并将其放入原始画布中。像这样:

function scaleImageData(imageData, scale){
    var newCanvas = $("<canvas>")
      .attr("width", imageData.width)
      .attr("height", imageData.height)[0];

    newCanvas.getContext("2d").putImageData(imageData, 0, 0);

    // Second canvas, for scaling
    var scaleCanvas = $("<canvas>")
      .attr("width", canvas.width)
      .attr("height", canvas.height)[0];

    var scaleCtx = scaleCanvas.getContext("2d");

    scaleCtx.scale(scale, scale);
    scaleCtx.drawImage(newCanvas, 0, 0);

    var scaledImageData =  scaleCtx.getImageData(0, 0, scaleCanvas.width, scaleCanvas.height);

    return scaledImageData;
}