【问题标题】:How to scale an imageData in HTML canvas?如何在 HTML 画布中缩放 imageData?
【发布时间】:2011-03-27 18:36:18
【问题描述】:

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

【问题讨论】:

    标签: html canvas getimagedata


    【解决方案1】:

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

    【讨论】:

    • 对于canvas.widthcanvas.height,变量canvas在哪里定义?
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      您可以使用 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 上的文档

      【讨论】:

      • 我不明白...它将画布的内容绘制到同一画布上?
      【解决方案4】:

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

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

      【讨论】:

      • 还可以使用更高质量的插值方法,例如双线性或样条。
      • @KvanTTT 完全正确,虽然有时你想要那些厚实的像素。
      • c 变量指的是什么?
      • @TomášZato 这是画布上下文
      • 现在有一个 imageSmoothingEnabled 属性可以设置为 false,以便在缩放的上下文中实现最近邻插值。
      【解决方案5】:

      您可以将 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/

      【讨论】:

      • 我想补充一点,您可以在不创建另一个画布的情况下执行此操作 ctx.drawImage(ctx.canvas,0,0);将画布绘制到自身上,您可以在绘制时缩放。
      • 但是这个比例也会弄乱坐标 10 x 是 1.5*10。你如何弥补这一点
      • 我对这种情况有同样的问题:newcanvas 在某些像素中具有 alpha = 0。在将 newcanvas 绘制到原始画布上时。阿尔法变成黑色。如何解决这个问题
      猜你喜欢
      • 1970-01-01
      • 2019-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多