如何将小型 ImageData 缩放到大型 HTML canvas

How to scale small ImageData to large HTML canvas

我正在尝试将 100x100 的图像数据放入 canvas 1000x1000,但是做不到,

let width=1000;      //canvas width
let height=1000;    //canvas height
let img_w=100;      //image width
let img_h=100;      //image height
let img=new Image();
img.width=img_w
img.height=img_h
img.src="./flower.jpg"
var canvas = document.getElementById('mycanvas');
var context = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
let pixels,scannedimg;
img.onload=()=>{
context.drawImage(img, 0, 0,width,height );
scannedimg = context.getImageData(0, 0, img.width, img.height);
pixels=scannedimg.data
console.log(pixels)
redraw();
}

let row=4*img_w;
let col=img_h;

function redraw(){
    for(let i=0;i<row;i+=4){
        for(let j=0;j<col;j++){
            pixels[i+j*row]=0;
            pixels[i+j*row+1]=0;
            pixels[i+j*row+2]=0;
            //pixels[i+j*400+3]=0;
        }
    }
   scannedimg.data=pixels;
    console.log(scannedimg);
    context.putImageData(scannedimg,0,0,0,0,width,height);
}

我已经把原来的数组转换成黑色图像数组(零数组),但是在 canvas 上,它仍然是 100x100 如何将其缩放到 1000x1000? 我不想遍历 1000x1000 并将其设置为零, 我需要一个计算效率高的答案

除非您将像素计算外包给 WebAssembly 模块,否则 JavaScript-only 方法对于大图像确实会相当慢。

老实说,我不确定您在代码中实际做了什么。

首先,您将 unknown-sized .jpg 绘制为 1000x1000 canvas,这 - 除非 .jpg 也是 1000x1000 - 将缩放并最终扭曲源图像。

let width=1000;
let height=1000; 
context.drawImage(img, 0, 0, width, height);

其次,您正在从 1000x1000 canvas 的 top-left 中获取 100x100 区域的像素数据。

let img_w=100;
let img_h=100;
img.width=img_w;
img.height=img_h;
scannedimg = context.getImageData(0, 0, img.width, img.height);

最后,在您的 redraw() 函数中,您相当随机地将一些像素设置为黑色,并将其绘制回 1000x1000 的 canvas(这种方式行不通,但我会稍后进入)。

让我们来点不同的吧。假设我们有一张 300x200 的图片。首先,我们需要将其绘制为 100x100 canvas,同时保持其纵横比以获得 100x100 图像数据。 这可以使用动态创建的 off-screen <canvas> 元素来完成,因为我们不需要看到它。

现在棘手的部分是 CanvasRenderingContext2D putImageData() 方法。我假设您在考虑宽度和高度的最后一对参数将拉伸现有像素数据以填充由(x、y、宽度、高度)指定的区域。好吧,事实并非如此。相反,我们需要 - 再次 - 将 100x100 像素数据绘制到 same-sized off-screen canvas(或者为了简单性 re-use 现有的)并将其绘制到最终的 canvas 使用 drawImage() 方法。

这是所有内容的汇总:

let pixelsWidth = 100;
let pixelsHeight = 100;
let finalWidth = 500;
let finalHeight = 500;
let tempCanvas = document.createElement('canvas');
let tempContext = tempCanvas.getContext('2d');
tempCanvas.width = pixelsWidth;
tempCanvas.height = pixelsHeight;
let pixelData;
let img = new Image();
img.crossOrigin = 'anonymous';
img.onload = (e) => {
  let scale = e.target.naturalWidth >= e.target.naturalHeight ? pixelsWidth / e.target.naturalWidth : pixelsHeight / e.target.naturalHeight;
  let tempWidth = e.target.naturalWidth * scale;
  let tempHeight = e.target.naturalHeight * scale;
  tempContext.drawImage(e.target, pixelsWidth / 2 - tempWidth / 2, pixelsHeight / 2 - tempHeight / 2, tempWidth, tempHeight);
  pixelData = tempContext.getImageData(0, 0, pixelsWidth, pixelsHeight);
  redraw();
}
img.src = 'https://picsum.photos/id/237/300/200';

function redraw() {
  let canvas = document.getElementById('canvas');
  let context = canvas.getContext('2d');
  canvas.width = finalWidth;
  canvas.height = finalHeight;
  tempContext.putImageData(pixelData, 0, 0);
  context.drawImage(tempCanvas, 0, 0, finalWidth, finalHeight);
}
canvas {
  background: #cccccc;
}
<canvas id="canvas"></canvas>

渲染 ImageData 放大的最佳方法是从中创建 ImageBitmap
所有现代浏览器最终都支持它(如果您需要对旧浏览器的支持,I got you covered)。

这将使用更快的路径将 ImageData 的内容呈现为易于由 drawImage 绘制的位图,理论上比将 ImageData 放在 [=26= 上的第二最佳选择快得多] 并重新绘制 canvas.

(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>