如何缩放 HTML 画布中的图像数据?

发布于 2024-09-13 20:27:25 字数 121 浏览 7 评论 0原文

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

初熏 2024-09-20 20:27:25

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

像这样的东西应该可以工作:

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);

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

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

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/.

晌融 2024-09-20 20:27:25

我需要在没有 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 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:

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."

作死小能手 2024-09-20 20:27:25

我知道这是一个古老的主题,但由于喜欢的人可能会发现它有用,所以我将我的优化添加到 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;
}

此代码使用更少的循环,并且运行速度大约快 30 倍。例如,在 100*100 区域的 100 倍缩放上,此代码需要 250 毫秒,而其他代码则需要 8 秒以上。

I know it's an old subject, but since people like may find it useful, I add my optimization to the code of 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.

黎夕旧梦 2024-09-20 20:27:25

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

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

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

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

You can scale the canvas using the drawImage method.

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.

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

没有伤那来痛 2024-09-20 20:27:25

如今,渲染缩放的 ImageData 对象的最佳方法通常是创建一个 ImageBitmap 来自它。
所有现代浏览器最终都支持它

这将使用更快的路径将 ImageData 的内容渲染为可供 drawImage() 绘制的位图,理论上比第二个最佳选项要快得多将 ImageData 放在辅助 上并重新绘制该

主要的问题是 createImageBitmap() 是异步*,因此它可能不太适合动画帧。

(async () => {
  // here I'll just make some noise in my ImageData
  const imagedata = new ImageData(100, 100);
  const arr = new Uint32Array(imagedata.data.buffer);
  for( let i = 0; i < arr.length; i++ ) {
    arr[i] = Math.random() * 0xFFFFFF + 0xFF000000;
  }
  // now to render it bigger
  const bitmap = await createImageBitmap(imagedata);
  const canvas = document.querySelector("canvas");
  const ctx = canvas.getContext("2d");
  ctx.imageSmoothingEnabled = false; // keep pixel perfect
  ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
})();
<canvas width="1000" height="1000"></canvas>

* 从技术上讲,只有 Firefox 会异步实现 createImageBitmap(),Chrome 和 Safari 会同步解析返回的 Promise,因此在动画帧中使用它是安全的:< a href="https://jsfiddle.net/vraz3xcg/" rel="nofollow noreferrer">https://jsfiddle.net/vraz3xcg/

Nowadays, the best way to render a scaled ImageData object is generally to create an ImageBitmap from it.
All modern browsers finally do support it.

This will use a faster path to render the ImageData's content to a bitmap readily available to be painted by drawImage(), theoretically a lot faster than the second best option of putting the ImageData on a secondary <canvas> and redraw that <canvas>.

The main catch-up is that createImageBitmap() is asynchronous*, and thus it may not fit in an animation frame very well.

(async () => {
  // here I'll just make some noise in my ImageData
  const imagedata = new ImageData(100, 100);
  const arr = new Uint32Array(imagedata.data.buffer);
  for( let i = 0; i < arr.length; i++ ) {
    arr[i] = Math.random() * 0xFFFFFF + 0xFF000000;
  }
  // now to render it bigger
  const bitmap = await createImageBitmap(imagedata);
  const canvas = document.querySelector("canvas");
  const ctx = canvas.getContext("2d");
  ctx.imageSmoothingEnabled = false; // keep pixel perfect
  ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
})();
<canvas width="1000" height="1000"></canvas>

* Technically, only Firefox does implement createImageBitmap(<ImageData>) asynchronously, Chrome and Safari will resolve the returned Promise synchronously, and there, it's safe to use it in an animation frame: https://jsfiddle.net/vraz3xcg/

水波映月 2024-09-20 20:27:25

@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;
}

@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:

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文