English 中文(简体)
Resizing an image in an HTML5 canvas
原标题:

I m trying to create a thumbnail image on the client side using javascript and a canvas element, but when I shrink the image down, it looks terrible. It looks as if it was downsized in photoshop with the resampling set to Nearest Neighbor instead of Bicubic. I know its possible to get this to look right, because this site can do it just fine using a canvas as well. I ve tried using the same code they do as shown in the "[Source]" link, but it still looks terrible. Is there something I m missing, some setting that needs to be set or something?

EDIT:

I m trying to resize a jpg. I have tried resizing the same jpg on the linked site and in photoshop, and it looks fine when downsized.

Here is the relevant code:

reader.onloadend = function(e)
{
    var img = new Image();
    var ctx = canvas.getContext("2d");
    var canvasCopy = document.createElement("canvas");
    var copyContext = canvasCopy.getContext("2d");

    img.onload = function()
    {
        var ratio = 1;

        if(img.width > maxWidth)
            ratio = maxWidth / img.width;
        else if(img.height > maxHeight)
            ratio = maxHeight / img.height;

        canvasCopy.width = img.width;
        canvasCopy.height = img.height;
        copyContext.drawImage(img, 0, 0);

        canvas.width = img.width * ratio;
        canvas.height = img.height * ratio;
        ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
    };

    img.src = reader.result;
}

EDIT2:

Seems I was mistaken, the linked website wasn t doing any better of a job of downsizing the image. I tried the other methods suggested and none of them look any better. This is what the different methods resulted in:

Photoshop:

alt text

Canvas:

alt text

Image with image-rendering: optimizeQuality set and scaled with width/height:

alt text

Image with image-rendering: optimizeQuality set and scaled with -moz-transform:

alt text

Canvas resize on pixastic:

alt text

I guess this means firefox isn t using bicubic sampling like its supposed to. I ll just have to wait until they actually add it.

EDIT3:

Original Image

最佳回答

那么,如果所有浏览器(实际上,Chrome 5 给了我相当好的一个)无法提供足够好的重采样质量,那么你该怎么办?那你就自己实现吧!哦,来吧,我们正在进入 Web 3.0 的新时代,采用 HTML5 标准的浏览器,超级优化的 JIT JavaScript 编译器,多核(†)机器,具有大量内存,你害怕什么?嘿,JavaScript 里有 Java 这个词,那应该能保证性能,对吧?看哪,缩略图生成代码:

// returns a function that calculates lanczos weight
function lanczosCreate(lobes) {
    return function(x) {
        if (x > lobes)
            return 0;
        x *= Math.PI;
        if (Math.abs(x) < 1e-16)
            return 1;
        var xx = x / lobes;
        return Math.sin(x) * Math.sin(xx) / x / xx;
    };
}

// elem: canvas element, img: image element, sx: scaled width, lobes: kernel radius
function thumbnailer(elem, img, sx, lobes) {
    this.canvas = elem;
    elem.width = img.width;
    elem.height = img.height;
    elem.style.display = "none";
    this.ctx = elem.getContext("2d");
    this.ctx.drawImage(img, 0, 0);
    this.img = img;
    this.src = this.ctx.getImageData(0, 0, img.width, img.height);
    this.dest = {
        width : sx,
        height : Math.round(img.height * sx / img.width),
    };
    this.dest.data = new Array(this.dest.width * this.dest.height * 3);
    this.lanczos = lanczosCreate(lobes);
    this.ratio = img.width / sx;
    this.rcp_ratio = 2 / this.ratio;
    this.range2 = Math.ceil(this.ratio * lobes / 2);
    this.cacheLanc = {};
    this.center = {};
    this.icenter = {};
    setTimeout(this.process1, 0, this, 0);
}

thumbnailer.prototype.process1 = function(self, u) {
    self.center.x = (u + 0.5) * self.ratio;
    self.icenter.x = Math.floor(self.center.x);
    for (var v = 0; v < self.dest.height; v++) {
        self.center.y = (v + 0.5) * self.ratio;
        self.icenter.y = Math.floor(self.center.y);
        var a, r, g, b;
        a = r = g = b = 0;
        for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) {
            if (i < 0 || i >= self.src.width)
                continue;
            var f_x = Math.floor(1000 * Math.abs(i - self.center.x));
            if (!self.cacheLanc[f_x])
                self.cacheLanc[f_x] = {};
            for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) {
                if (j < 0 || j >= self.src.height)
                    continue;
                var f_y = Math.floor(1000 * Math.abs(j - self.center.y));
                if (self.cacheLanc[f_x][f_y] == undefined)
                    self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2)
                            + Math.pow(f_y * self.rcp_ratio, 2)) / 1000);
                weight = self.cacheLanc[f_x][f_y];
                if (weight > 0) {
                    var idx = (j * self.src.width + i) * 4;
                    a += weight;
                    r += weight * self.src.data[idx];
                    g += weight * self.src.data[idx + 1];
                    b += weight * self.src.data[idx + 2];
                }
            }
        }
        var idx = (v * self.dest.width + u) * 3;
        self.dest.data[idx] = r / a;
        self.dest.data[idx + 1] = g / a;
        self.dest.data[idx + 2] = b / a;
    }

    if (++u < self.dest.width)
        setTimeout(self.process1, 0, self, u);
    else
        setTimeout(self.process2, 0, self);
};
thumbnailer.prototype.process2 = function(self) {
    self.canvas.width = self.dest.width;
    self.canvas.height = self.dest.height;
    self.ctx.drawImage(self.img, 0, 0, self.dest.width, self.dest.height);
    self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height);
    var idx, idx2;
    for (var i = 0; i < self.dest.width; i++) {
        for (var j = 0; j < self.dest.height; j++) {
            idx = (j * self.dest.width + i) * 3;
            idx2 = (j * self.dest.width + i) * 4;
            self.src.data[idx2] = self.dest.data[idx];
            self.src.data[idx2 + 1] = self.dest.data[idx + 1];
            self.src.data[idx2 + 2] = self.dest.data[idx + 2];
        }
    }
    self.ctx.putImageData(self.src, 0, 0);
    self.canvas.style.display = "block";
};

你可以通过这个获得这样的结果!

将此翻译成中文:img717.imageshack.us/img717/8910/lanczos358.png 无法翻译成中文,因为这是一张图片,没有文字。

总之,这是您示例的修正版本:

img.onload = function() {
    var canvas = document.createElement("canvas");
    new thumbnailer(canvas, img, 188, 3); //this produces lanczos3
    // but feel free to raise it up to 8. Your client will appreciate
    // that the program makes full use of his machine.
    document.body.appendChild(canvas);
};

现在是你把最好的row子带走的时候了,看到哪怕会增加客户的血压!

嗯,我的讽刺标签在哪里?

(由于代码的许多部分基于Anrieff Gallery Generator,它也受GPL2的保护吗?我不知道)

实际上,由于 JavaScript 的限制,不支持多核。

问题回答

Fast image resize/resample algorithm using Hermite filter with JavaScript. Support transparency, gives good quality. Preview:

enter image description here

更新:2.0版本已在GitHub上添加(更快,具有Web Workers +可传输对象)。终于让我搞定了!

Git: https://github.com/viliusle/Hermite-resize
Demo: http://viliusle.github.io/miniPaint/

/**
 * Hermite resize - fast image resize/resample using Hermite filter. 1 cpu version!
 * 
 * @param {HtmlElement} canvas
 * @param {int} width
 * @param {int} height
 * @param {boolean} resize_canvas if true, canvas will be resized. Optional.
 */
function resample_single(canvas, width, height, resize_canvas) {
    var width_source = canvas.width;
    var height_source = canvas.height;
    width = Math.round(width);
    height = Math.round(height);

    var ratio_w = width_source / width;
    var ratio_h = height_source / height;
    var ratio_w_half = Math.ceil(ratio_w / 2);
    var ratio_h_half = Math.ceil(ratio_h / 2);

    var ctx = canvas.getContext("2d");
    var img = ctx.getImageData(0, 0, width_source, height_source);
    var img2 = ctx.createImageData(width, height);
    var data = img.data;
    var data2 = img2.data;

    for (var j = 0; j < height; j++) {
        for (var i = 0; i < width; i++) {
            var x2 = (i + j * width) * 4;
            var weight = 0;
            var weights = 0;
            var weights_alpha = 0;
            var gx_r = 0;
            var gx_g = 0;
            var gx_b = 0;
            var gx_a = 0;
            var center_y = (j + 0.5) * ratio_h;
            var yy_start = Math.floor(j * ratio_h);
            var yy_stop = Math.ceil((j + 1) * ratio_h);
            for (var yy = yy_start; yy < yy_stop; yy++) {
                var dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half;
                var center_x = (i + 0.5) * ratio_w;
                var w0 = dy * dy; //pre-calc part of w
                var xx_start = Math.floor(i * ratio_w);
                var xx_stop = Math.ceil((i + 1) * ratio_w);
                for (var xx = xx_start; xx < xx_stop; xx++) {
                    var dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half;
                    var w = Math.sqrt(w0 + dx * dx);
                    if (w >= 1) {
                        //pixel too far
                        continue;
                    }
                    //hermite filter
                    weight = 2 * w * w * w - 3 * w * w + 1;
                    var pos_x = 4 * (xx + yy * width_source);
                    //alpha
                    gx_a += weight * data[pos_x + 3];
                    weights_alpha += weight;
                    //colors
                    if (data[pos_x + 3] < 255)
                        weight = weight * data[pos_x + 3] / 250;
                    gx_r += weight * data[pos_x];
                    gx_g += weight * data[pos_x + 1];
                    gx_b += weight * data[pos_x + 2];
                    weights += weight;
                }
            }
            data2[x2] = gx_r / weights;
            data2[x2 + 1] = gx_g / weights;
            data2[x2 + 2] = gx_b / weights;
            data2[x2 + 3] = gx_a / weights_alpha;
        }
    }
    //clear and resize canvas
    if (resize_canvas === true) {
        canvas.width = width;
        canvas.height = height;
    } else {
        ctx.clearRect(0, 0, width_source, height_source);
    }

    //draw
    ctx.putImageData(img2, 0, 0);
}

尝试使用pica-这是一个高度优化的可选择算法的调整大小工具。查看演示

例如,第1个员额的原始形象在120姆斯被重新塑造,其中Lanczos过滤器和3px窗或60ms带有方框过滤器和0.5px窗。 对于巨大的17mb图像,5,000x3,000px 树脂在 desktop和3台移动式上吸收了约1。

所有的调整原则在这个线程中都有很好的描述,pica并没有添加什么高深的科学。但它对现代JIT-s进行了优化,并且可以直接使用(通过npm或资源管理器)。此外,当可用时,它使用Web Workers来避免界面冻结。

我也打算很快添加不锐化掩蔽支持,因为在缩小后非常有用。

I know this is an old thread but it might be useful for some people such as myself that months after are hitting this issue for the first time.

Here is some code that resizes the image every time you reload the image. I am aware this is not optimal at all, but I provide it as a proof of concept.

Also, sorry for using jQuery for simple selectors but I just feel too comfortable with the syntax.

$(document).on( ready , createImage);
$(window).on( resize , createImage);

var createImage = function(){
  var canvas = document.getElementById( myCanvas );
  canvas.width = window.innerWidth || $(window).width();
  canvas.height = window.innerHeight || $(window).height();
  var ctx = canvas.getContext( 2d );
  img = new Image();
  img.addEventListener( load , function () {
    ctx.drawImage(this, 0, 0, w, h);
  });
  img.src =  http://www.ruinvalor.com/Telanor/images/original.jpg ;
};
html, body{
  height: 100%;
  width: 100%;
  margin: 0;
  padding: 0;
  background: #000;
}
canvas{
  position: absolute;
  left: 0;
  top: 0;
  z-index: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Canvas Resize</title>
  </head>
  <body>
    <canvas id="myCanvas"></canvas>
  </body>
</html>

My createImage function is called once when the document is loaded and after that it is called every time the window receives a resize event.

I tested it in Chrome 6 and Firefox 3.6, both on the Mac. This "technique" eats processor as it if was ice cream in the summer, but it does the trick.

I ve put up some algorithms to do image interpolation on html canvas pixel arrays that might be useful here:

https://web.archive.org/web/20170104190425/http://jsperf.com:80/pixel-interpolation/2

These can be copy/pasted and can be used inside of web workers to resize images (or any other operation that requires interpolation - I m using them to defish images at the moment).

I haven t added the lanczos stuff above, so feel free to add that as a comparison if you d like.

This is a javascript function adapted from @Telanor s code. When passing a image base64 as first argument to the function, it returns the base64 of the resized image. maxWidth and maxHeight are optional.

function thumbnail(base64, maxWidth, maxHeight) {

  // Max size for thumbnail
  if(typeof(maxWidth) ===  undefined ) var maxWidth = 500;
  if(typeof(maxHeight) ===  undefined ) var maxHeight = 500;

  // Create and initialize two canvas
  var canvas = document.createElement("canvas");
  var ctx = canvas.getContext("2d");
  var canvasCopy = document.createElement("canvas");
  var copyContext = canvasCopy.getContext("2d");

  // Create original image
  var img = new Image();
  img.src = base64;

  // Determine new ratio based on max size
  var ratio = 1;
  if(img.width > maxWidth)
    ratio = maxWidth / img.width;
  else if(img.height > maxHeight)
    ratio = maxHeight / img.height;

  // Draw original image in second canvas
  canvasCopy.width = img.width;
  canvasCopy.height = img.height;
  copyContext.drawImage(img, 0, 0);

  // Copy and resize second canvas to first canvas
  canvas.width = img.width * ratio;
  canvas.height = img.height * ratio;
  ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);

  return canvas.toDataURL();

}

I d highly suggest you check out this link and make sure it is set to true.

Controlling image scaling behavior

Introduced in Gecko 1.9.2 (Firefox 3.6 / Thunderbird 3.1 / Fennec 1.0)

Gecko 1.9.2 introduced the mozImageSmoothingEnabled property to the canvas element; if this Boolean value is false, images won t be smoothed when scaled. This property is true by default. view plainprint?

  1. cx.mozImageSmoothingEnabled = false;

If you re simply trying to resize an image, I d recommend setting width and height of the image with CSS. Here s a quick example:

.small-image {
    width: 100px;
    height: 100px;
}

Note that the height and width can also be set using JavaScript. Here s quick code sample:

var img = document.getElement("my-image");
img.style.width = 100 + "px";  // Make sure you add the "px" to the end,
img.style.height = 100 + "px"; // otherwise you ll confuse IE

Also, to ensure that the resized image looks good, add the following css rules to image selector:

As far as I can tell, all browsers except IE using an bicubic algorithm to resize images by default, so your resized images should look good in Firefox and Chrome.

If setting the css width and height doesn t work, you may want to play with a css transform:

If for whatever reason you need to use a canvas, please note that there are two ways an image can be resize: by resizing the canvas with css or by drawing the image at a smaller size.

See this question for more details.

i got this image by right clicking the canvas element in firefox and saving as.

alt text

var img = new Image();
img.onload = function () {
    console.debug(this.width,this.height);
    var canvas = document.createElement( canvas ), ctx;
    canvas.width = 188;
    canvas.height = 150;
    document.body.appendChild(canvas);
    ctx = canvas.getContext( 2d );
    ctx.drawImage(img,0,0,188,150);
};
img.src =  original.jpg ;

so anyway, here is a fixed version of your example:

var img = new Image();
// added cause it wasnt defined
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);

var ctx = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
// adding it to the body

document.body.appendChild(canvasCopy);

var copyContext = canvasCopy.getContext("2d");

img.onload = function()
{
        var ratio = 1;

        // defining cause it wasnt
        var maxWidth = 188,
            maxHeight = 150;

        if(img.width > maxWidth)
                ratio = maxWidth / img.width;
        else if(img.height > maxHeight)
                ratio = maxHeight / img.height;

        canvasCopy.width = img.width;
        canvasCopy.height = img.height;
        copyContext.drawImage(img, 0, 0);

        canvas.width = img.width * ratio;
        canvas.height = img.height * ratio;
        // the line to change
        // ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
        // the method signature you are using is for slicing
        ctx.drawImage(canvasCopy, 0, 0, canvas.width, canvas.height);
};

// changed for example
img.src =  original.jpg ;

For resizing to image with width less that original, i use:

    function resize2(i) {
      var cc = document.createElement("canvas");
      cc.width = i.width / 2;
      cc.height = i.height / 2;
      var ctx = cc.getContext("2d");
      ctx.drawImage(i, 0, 0, cc.width, cc.height);
      return cc;
    }
    var cc = img;
    while (cc.width > 64 * 2) {
      cc = resize2(cc);
    }
    // .. than drawImage(cc, .... )

and it works =).

I have a feeling the module I wrote will produce similar results to photoshop, as it preserves color data by averaging them, not applying an algorithm. It s kind of slow, but to me it is the best, because it preserves all the color data.

https://github.com/danschumann/limby-resize/blob/master/lib/canvas_resize.js

It doesn t take the nearest neighbor and drop other pixels, or sample a group and take a random average. It takes the exact proportion each source pixel should output into the destination pixel. The average pixel color in the source will be the average pixel color in the destination, which these other formulas, I think they will not be.

an example of how to use is at the bottom of https://github.com/danschumann/limby-resize

UPDATE OCT 2018: These days my example is more academic than anything else. Webgl is pretty much 100%, so you d be better off resizing with that to produce similar results, but faster. PICA.js does this, I believe. –

The problem with some of this solutions is that they access directly the pixel data and loop through it to perform the downsampling. Depending on the size of the image this can be very resource intensive, and it would be better to use the browser s internal algorithms.

The drawImage() function is using a linear-interpolation, nearest-neighbor resampling method. That works well when you are not resizing down more than half the original size.

If you loop to only resize max one half at a time, the results would be quite good, and much faster than accessing pixel data.

This function downsample to half at a time until reaching the desired size:

  function resize_image( src, dst, type, quality ) {
     var tmp = new Image(),
         canvas, context, cW, cH;

     type = type ||  image/jpeg ;
     quality = quality || 0.92;

     cW = src.naturalWidth;
     cH = src.naturalHeight;

     tmp.src = src.src;
     tmp.onload = function() {

        canvas = document.createElement(  canvas  );

        cW /= 2;
        cH /= 2;

        if ( cW < src.width ) cW = src.width;
        if ( cH < src.height ) cH = src.height;

        canvas.width = cW;
        canvas.height = cH;
        context = canvas.getContext(  2d  );
        context.drawImage( tmp, 0, 0, cW, cH );

        dst.src = canvas.toDataURL( type, quality );

        if ( cW <= src.width || cH <= src.height )
           return;

        tmp.src = dst.src;
     }

  }
  // The images sent as parameters can be in the DOM or be image objects
  resize_image( $(  #original  )[0], $(  #smaller  )[0] );

Credits to this post

So something interesting that I found a while ago while working with canvas that might be helpful:

To resize the canvas control on its own, you need to use the height="" and width="" attributes (or canvas.width/canvas.height elements). If you use CSS to resize the canvas, it will actually stretch (i.e.: resize) the content of the canvas to fit the full canvas (rather than simply increasing or decreasing the area of the canvas.

It d be worth a shot to try drawing the image into a canvas control with the height and width attributes set to the size of the image and then using CSS to resize the canvas to the size you re looking for. Perhaps this would use a different resizing algorithm.

It should also be noted that canvas has different effects in different browsers (and even different versions of different browsers). The algorithms and techniques used in the browsers is likely to change over time (especially with Firefox 4 and Chrome 6 coming out so soon, which will place heavy emphasis on canvas rendering performance).

In addition, you may want to give SVG a shot, too, as it likely uses a different algorithm as well.

Best of luck!

Fast and simple Javascript image resizer:

https://github.com/calvintwr/blitz-hermite-resize

const blitz = Blitz.create()

/* Promise */
blitz({
    source: DOM Image/DOM Canvas/jQuery/DataURL/File,
    width: 400,
    height: 600
}).then(output => {
    // handle output
})catch(error => {
    // handle error
})

/* Await */
let resized = await blizt({...})

/* Old school callback */
const blitz = Blitz.create( callback )
blitz({...}, function(output) {
    // run your callback.
})

History

This is really after many rounds of research, reading and trying.

The resizer algorithm uses @ViliusL s Hermite script (Hermite resizer is really the fastest and gives reasonably good output). Extended with features you need.

Forks 1 worker to do the resizing so that it doesn t freeze your browser when resizing, unlike all other JS resizers out there.

I converted @syockit s answer as well as the step-down approach into a reusable Angular service for anyone who s interested: https://gist.github.com/fisch0920/37bac5e741eaec60e983

I included both solutions because they both have their own pros / cons. The lanczos convolution approach is higher quality at the cost of being slower, whereas the step-wise downscaling approach produces reasonably antialiased results and is significantly faster.

Example usage:

angular.module( demo ).controller( ExampleCtrl , function (imageService) {
  // EXAMPLE USAGE
  // NOTE: it s bad practice to access the DOM inside a controller, 
  // but this is just to show the example usage.

  // resize by lanczos-sinc filter
  imageService.resize($( #myimg )[0], 256, 256)
    .then(function (resizedImage) {
      // do something with resized image
    })

  // resize by stepping down image size in increments of 2x
  imageService.resizeStep($( #myimg )[0], 256, 256)
    .then(function (resizedImage) {
      // do something with resized image
    })
})

Thanks @syockit for an awesome answer. however, I had to reformat a little as follows to make it work. Perhaps due to DOM scanning issues:

$(document).ready(function () {

$( img ).on("load", clickA);
function clickA() {
    var img = this;
    var canvas = document.createElement("canvas");
    new thumbnailer(canvas, img, 50, 3);
    document.body.appendChild(canvas);
}

function thumbnailer(elem, img, sx, lobes) {
    this.canvas = elem;
    elem.width = img.width;
    elem.height = img.height;
    elem.style.display = "none";
    this.ctx = elem.getContext("2d");
    this.ctx.drawImage(img, 0, 0);
    this.img = img;
    this.src = this.ctx.getImageData(0, 0, img.width, img.height);
    this.dest = {
        width: sx,
        height: Math.round(img.height * sx / img.width)
    };
    this.dest.data = new Array(this.dest.width * this.dest.height * 3);
    this.lanczos = lanczosCreate(lobes);
    this.ratio = img.width / sx;
    this.rcp_ratio = 2 / this.ratio;
    this.range2 = Math.ceil(this.ratio * lobes / 2);
    this.cacheLanc = {};
    this.center = {};
    this.icenter = {};
    setTimeout(process1, 0, this, 0);
}

//returns a function that calculates lanczos weight
function lanczosCreate(lobes) {
    return function (x) {
        if (x > lobes)
            return 0;
        x *= Math.PI;
        if (Math.abs(x) < 1e-16)
            return 1
        var xx = x / lobes;
        return Math.sin(x) * Math.sin(xx) / x / xx;
    }
}

process1 = function (self, u) {
    self.center.x = (u + 0.5) * self.ratio;
    self.icenter.x = Math.floor(self.center.x);
    for (var v = 0; v < self.dest.height; v++) {
        self.center.y = (v + 0.5) * self.ratio;
        self.icenter.y = Math.floor(self.center.y);
        var a, r, g, b;
        a = r = g = b = 0;
        for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) {
            if (i < 0 || i >= self.src.width)
                continue;
            var f_x = Math.floor(1000 * Math.abs(i - self.center.x));
            if (!self.cacheLanc[f_x])
                self.cacheLanc[f_x] = {};
            for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) {
                if (j < 0 || j >= self.src.height)
                    continue;
                var f_y = Math.floor(1000 * Math.abs(j - self.center.y));
                if (self.cacheLanc[f_x][f_y] == undefined)
                    self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2) + Math.pow(f_y * self.rcp_ratio, 2)) / 1000);
                weight = self.cacheLanc[f_x][f_y];
                if (weight > 0) {
                    var idx = (j * self.src.width + i) * 4;
                    a += weight;
                    r += weight * self.src.data[idx];
                    g += weight * self.src.data[idx + 1];
                    b += weight * self.src.data[idx + 2];
                }
            }
        }
        var idx = (v * self.dest.width + u) * 3;
        self.dest.data[idx] = r / a;
        self.dest.data[idx + 1] = g / a;
        self.dest.data[idx + 2] = b / a;
    }

    if (++u < self.dest.width)
        setTimeout(process1, 0, self, u);
    else
        setTimeout(process2, 0, self);
};

process2 = function (self) {
    self.canvas.width = self.dest.width;
    self.canvas.height = self.dest.height;
    self.ctx.drawImage(self.img, 0, 0);
    self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height);
    var idx, idx2;
    for (var i = 0; i < self.dest.width; i++) {
        for (var j = 0; j < self.dest.height; j++) {
            idx = (j * self.dest.width + i) * 3;
            idx2 = (j * self.dest.width + i) * 4;
            self.src.data[idx2] = self.dest.data[idx];
            self.src.data[idx2 + 1] = self.dest.data[idx + 1];
            self.src.data[idx2 + 2] = self.dest.data[idx + 2];
        }
    }
    self.ctx.putImageData(self.src, 0, 0);
    self.canvas.style.display = "block";
}
});

I wanted some well defined functions out of answers here so ended up with these which am hoping would be useful for others also,

function getImageFromLink(link) {
    return new Promise(function (resolve) {
        var image = new Image();
        image.onload = function () { resolve(image); };
        image.src = link;
    });
}

function resizeImageToBlob(image, width, height, mime) {
    return new Promise(function (resolve) {
        var canvas = document.createElement( canvas );
        canvas.width = width;
        canvas.height = height;
        canvas.getContext( 2d ).drawImage(image, 0, 0, width, height);
        return canvas.toBlob(resolve, mime);
    });
}

getImageFromLink(location.href).then(function (image) {
    // calculate these based on the original size
    var width = image.width / 4;
    var height = image.height / 4;
    return resizeImageToBlob(image, width, height,  image/jpeg );
}).then(function (blob) {
    // Do something with the result Blob object
    document.querySelector( img ).src = URL.createObjectURL(blob);
});

Just for the sake of testing this run it on a image opened in a tab.

I just ran a page of side by sides comparisons and unless something has changed recently, I could see no better downsizing (scaling) using canvas vs. simple css. I tested in FF6 Mac OSX 10.7. Still slightly soft vs. the original.

I did however stumble upon something that did make a huge difference and that was using image filters in browsers that support canvas. You can actually manipulate images much like you can in Photoshop with blur, sharpen, saturation, ripple, grayscale, etc.

I then found an awesome jQuery plug-in which makes application of these filters a snap: http://codecanyon.net/item/jsmanipulate-jquery-image-manipulation-plugin/428234

I simply apply the sharpen filter right after resizing the image which should give you the desired effect. I didn t even have to use a canvas element.

Looking for another great simple solution?

var img=document.createElement( img );
img.src=canvas.toDataURL();
$(img).css("background", backgroundColor);
$(img).width(settings.width);
$(img).height(settings.height);

This solution will use the resize algorith of browser! :)





相关问题
CSS working only in Firefox

I am trying to create a search text-field like on the Apple website. The HTML looks like this: <div class="frm-search"> <div> <input class="btn" type="image" src="http://www....

image changed but appears the same in browser

I m writing a php script to crop an image. The script overwrites the old image with the new one, but when I reload the page (which is supposed to pickup the new image) I still see the old one. ...

Firefox background image horizontal centering oddity

I am building some basic HTML code for a CMS. One of the page-related options in the CMS is "background image" and "stretch page width / height to background image width / height." so that with large ...

Separator line in ASP.NET

I d like to add a simple separator line in an aspx web form. Does anyone know how? It sounds easy enough, but still I can t manage to find how to do it.. 10x!

热门标签