How to scale an imageData in HTML canvas?

前端 未结 5 1542
礼貌的吻别
礼貌的吻别 2020-12-01 05:37

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 mak

相关标签:
5条回答
  • 2020-12-01 05:47

    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

    0 讨论(0)
  • 2020-12-01 05:50

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

    0 讨论(0)
  • 2020-12-01 05:56

    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.

    0 讨论(0)
  • 2020-12-01 05:57

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

    0 讨论(0)
  • 2020-12-01 06:08

    @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;
    }
    
    0 讨论(0)
提交回复
热议问题