HTML5 canvas: is there a way to resize image with “nearest neighbour” resampling?

后端 未结 5 1690
天涯浪人
天涯浪人 2021-02-01 09:41

I have some JS that makes some manipulations with images. I want to have pixelart-like graphics, so I had to enlarge original images in graphics editor. But I think it\'d be goo

相关标签:
5条回答
  • 2021-02-01 10:18

    I wrote a NN resizing script a while ago using ImageData (around line 1794)

    https://github.com/arahaya/ImageFilters.js/blob/master/imagefilters.js

    You can see a demo here

    http://www.arahaya.com/imagefilters/

    unfortunately the builtin resizing should be slightly faster.

    0 讨论(0)
  • 2021-02-01 10:20
    image-rendering: -webkit-optimize-contrast; /* webkit */
    image-rendering: -moz-crisp-edges /* Firefox */
    

    http://phrogz.net/tmp/canvas_image_zoom.html can provide a fallback case using canvas and getImageData. In short:

    // Create an offscreen canvas, draw an image to it, and fetch the pixels
    var offtx = document.createElement('canvas').getContext('2d');
    offtx.drawImage(img1,0,0);
    var imgData = offtx.getImageData(0,0,img1.width,img1.height).data;
    
    // Draw the zoomed-up pixels to a different canvas context
    for (var x=0;x<img1.width;++x){
      for (var y=0;y<img1.height;++y){
        // Find the starting index in the one-dimensional image data
        var i = (y*img1.width + x)*4;
        var r = imgData[i  ];
        var g = imgData[i+1];
        var b = imgData[i+2];
        var a = imgData[i+3];
        ctx2.fillStyle = "rgba("+r+","+g+","+b+","+(a/255)+")";
        ctx2.fillRect(x*zoom,y*zoom,zoom,zoom);
      }
    }
    

    More: MDN docs on image-rendering

    0 讨论(0)
  • 2021-02-01 10:27

    Based on Paul Irish's comment:

    function resizeBase64(base64, zoom) {
        return new Promise(function(resolve, reject) {
            var img = document.createElement("img");
    
            // once image loaded, resize it
            img.onload = function() {
                // get image size
                var imageWidth = img.width;
                var imageHeight = img.height;
    
                // create and draw image to our first offscreen canvas
                var canvas1 = document.createElement("canvas");
                canvas1.width = imageWidth;
                canvas1.height = imageHeight;
                var ctx1 = canvas1.getContext("2d");
                ctx1.drawImage(this, 0, 0, imageWidth, imageHeight);
    
                // get pixel data from first canvas
                var imgData = ctx1.getImageData(0, 0, imageWidth, imageHeight).data;
    
                // create second offscreen canvas at the zoomed size
                var canvas2 = document.createElement("canvas");
                canvas2.width = imageWidth * zoom;
                canvas2.height = imageHeight * zoom;
                var ctx2 = canvas2.getContext("2d");
    
                // draw the zoomed-up pixels to a the second canvas
                for (var x = 0; x < imageWidth; ++x) {
                    for (var y = 0; y < imageHeight; ++y) {
                        // find the starting index in the one-dimensional image data
                        var i = (y * imageWidth + x) * 4;
                        var r = imgData[i];
                        var g = imgData[i + 1];
                        var b = imgData[i + 2];
                        var a = imgData[i + 3];
                        ctx2.fillStyle = "rgba(" + r + "," + g + "," + b + "," + a / 255 + ")";
                        ctx2.fillRect(x * zoom, y * zoom, zoom, zoom);
                    }
                }
    
                // resolve promise with the zoomed base64 image data
                var dataURI = canvas2.toDataURL();
                resolve(dataURI);
            };
            img.onerror = function(error) {
                reject(error);
            };
            // set the img soruce
            img.src = base64;
        });
    }
    
    resizeBase64(src, 4).then(function(zoomedSrc) {
        console.log(zoomedSrc);
    });
    

    https://jsfiddle.net/djhyquon/69/

    0 讨论(0)
  • 2021-02-01 10:32

    There is no built-in way. You have to do it yourself with getImageData.

    0 讨论(0)
  • 2021-02-01 10:32

    I'll echo what others have said and tell you it's not a built-in function. After running into the same issue, I've made one below.

    It uses fillRect() instead of looping through each pixel and painting it. Everything is commented to help you better understand how it works.

    //img is the original image, scale is a multiplier. It returns the resized image.
    function Resize_Nearest_Neighbour( img, scale ){
        //make shortcuts for image width and height
        var w = img.width;
        var h = img.height;
    
        //---------------------------------------------------------------
        //draw the original image to a new canvas
        //---------------------------------------------------------------
    
        //set up the canvas
        var c = document.createElement("CANVAS");
        var ctx = c.getContext("2d");
        //disable antialiasing on the canvas
        ctx.imageSmoothingEnabled = false;
        //size the canvas to match the input image
        c.width = w;
        c.height = h;
        //draw the input image
        ctx.drawImage( img, 0, 0 );
        //get the input image as image data
        var inputImg = ctx.getImageData(0,0,w,h);
        //get the data array from the canvas image data
        var data = inputImg.data;
    
        //---------------------------------------------------------------
        //resize the canvas to our bigger output image
        //---------------------------------------------------------------
        c.width = w * scale;
        c.height = h * scale;
        //---------------------------------------------------------------
        //loop through all the data, painting each pixel larger
        //---------------------------------------------------------------
        for ( var i = 0; i < data.length; i+=4 ){
    
            //find the colour of this particular pixel
            var colour = "#";
    
            //---------------------------------------------------------------
            //convert the RGB numbers into a hex string. i.e. [255, 10, 100]
            //into "FF0A64"
            //---------------------------------------------------------------
            function _Dex_To_Hex( number ){
                var out = number.toString(16);
                if ( out.length < 2 ){
                    out = "0" + out;
                }
                return out;
            }
            for ( var colourIndex = 0; colourIndex < 3; colourIndex++ ){
                colour += _Dex_To_Hex( data[ i+colourIndex ] );
            }
            //set the fill colour
            ctx.fillStyle = colour;
    
            //---------------------------------------------------------------
            //convert the index in the data array to x and y coordinates
            //---------------------------------------------------------------
            var index = i/4;
            var x = index % w;
            //~~ is a faster way to do 'Math.floor'
            var y = ~~( index / w );
            //---------------------------------------------------------------
            //draw an enlarged rectangle on the enlarged canvas
            //---------------------------------------------------------------
            ctx.fillRect( x*scale, y*scale, scale, scale );
        }
    
        //get the output image from the canvas
        var output = c.toDataURL("image/png");
        //returns image data that can be plugged into an img tag's src
        return output;
    }
    

    Below is an example of it in use.

    Your image would appear in the HTML like this:

    <img id="pixel-image" src="" data-src="pixel-image.png"/>
    

    The data-src tag contains the URL for the image you want to enlarge. This is a custom data tag. The code below will take the image URL from the data tag and put it through the resizing function, returning a larger image (30x the original size) which then gets injected into the src attribute of the img tag.

    Remember to put the function Resize_Nearest_Neighbour (above) into the <script> tag before you include the following.

    function Load_Image( element ){
        var source = element.getAttribute("data-src");
        var img = new Image();
    
        img.addEventListener("load", function(){
    
            var bigImage = Resize_Nearest_Neighbour( this, 30 );
            element.src = bigImage;
    
        });
    
        img.src = source;
    }
    
    Load_Image( document.getElementById("pixel-image") );
    
    0 讨论(0)
提交回复
热议问题