簡體   English   中英

在 HTML5 畫布中調整圖像大小

[英]Resizing an image in an HTML5 canvas

我正在嘗試使用 javascript 和 canvas 元素在客戶端創建一個縮略圖圖像,但是當我縮小圖像時,它看起來很糟糕。 它看起來好像在 Photoshop 中縮小了尺寸,重采樣設置為“最近的鄰居”而不是雙三次。 我知道有可能讓它看起來正確,因為這個網站也可以使用畫布來做。 我嘗試使用與“[Source]”鏈接中所示相同的代碼,但它看起來仍然很糟糕。 有什么我遺漏的,需要設置的一些設置還是什么?

編輯:

我正在嘗試調整 jpg 的大小。 我嘗試在鏈接的網站和 photoshop 中調整相同的 jpg 大小,縮小后看起來不錯。

這是相關的代碼:

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

編輯2:

似乎我錯了,鏈接的網站在縮小圖像尺寸方面並沒有做得更好。 我嘗試了建議的其他方法,但沒有一個看起來更好。 這就是不同的方法導致的結果:

Photoshop:

替代文字

帆布:

替代文字

帶有圖像渲染的圖像:優化質量設置並按寬度/高度縮放:

替代文字

帶有圖像渲染的圖像:優化質量設置並使用 -moz-transform 縮放:

替代文字

畫布在 pixastic 上調整大小:

替代文字

我想這意味着 firefox 沒有像它應該的那樣使用雙三次采樣。 我只需要等到他們真正添加它。

編輯3:

原圖

那么如果所有瀏覽器(實際上,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);
};

現在是時候使用您最好的瀏覽器了,看看哪個瀏覽器最不可能增加您客戶的血壓!

嗯,我的諷刺標簽在哪里?

(因為很多部分的代碼是基於Anrieff Gallery Generator 的,是不是也包含在 GPL2 下?我不知道)

實際上由於 javascript 的限制,不支持多核。

使用帶 JavaScript 的Hermite過濾器快速調整圖像大小/重新采樣算法。 支持透明度,提供良好的質量。 預覽:

在此處輸入圖片說明

更新:在 GitHub 上添加了 2.0 版(更快,網絡工作者 + 可轉移對象)。 最后我讓它工作了!

Git: https : //github.com/viliusle/Hermite-resize
演示: 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 - 這是一個高度優化的調整器,具有可選擇的算法。 演示

例如,第一篇文章的原始圖像使用 Lanczos 過濾器和 3px 窗口在 120 毫秒內調整大小,或者使用 Box 過濾器和 0.5 像素窗口在 60 毫秒內調整大小。 對於 17mb 的巨大圖像,5000x3000px 調整大小在桌面上需要大約 1 秒,在移動設備上需要 3 秒。

此線程中很好地描述了所有調整大小的原則,並且 pica 沒有添加火箭科學。 但它針對現代 JIT-s 進行了很好的優化,並且可以開箱即用(通過 npm 或 bower)。 此外,它在可用時使用網絡工作者以避免界面凍結。

我還計划盡快添加反銳化蒙版支持,因為它在縮小后非常有用。

我知道這是一個舊線程,但它可能對某些人(例如我自己)在幾個月后第一次遇到此問題時有用。

這是一些每次重新加載圖像時調整圖像大小的代碼。 我知道這根本不是最佳選擇,但我提供它作為概念證明。

另外,很抱歉將 jQuery 用於簡單的選擇器,但我對語法感到太舒服了。

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

我的 createImage 函數在加載文檔時被調用一次,之后每次窗口收到調整大小事件時都會調用它。

我在 Mac 上的 Chrome 6 和 Firefox 3.6 中對其進行了測試。 這種“技術”就像夏天吃冰淇淋一樣吃處理器,但它確實有效。

我已經提出了一些算法來對 html canvas 像素數組進行圖像插值,這些算法在這里可能有用:

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

這些可以被復制/粘貼,並且可以在網絡工作者內部使用來調整圖像大小(或任何其他需要插值的操作 - 我目前正在使用它們來去除圖像)。

我沒有在上面添加 lanczos 的東西,所以如果你願意,可以隨意添加它作為比較。

這是一個改編自 @Telanor 代碼的 javascript 函數。 當將圖像 base64 作為第一個參數傳遞給函數時,它返回調整大小的圖像的 base64。 maxWidth 和 maxHeight 是可選的。

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

}

我強烈建議您查看此鏈接並確保將其設置為 true。

控制圖像縮放行為

在 Gecko 1.9.2 (Firefox 3.6 / Thunderbird 3.1 / Fennec 1.0) 中引入

Gecko 1.9.2 向 canvas 元素引入了 mozImageSmoothingEnabled 屬性; 如果此布爾值為 false,則縮放時不會平滑圖像。 默認情況下,此屬性為 true。 查看普通版?

  1. cx.mozImageSmoothingEnabled = false;

如果您只是想調整圖像大小,我建議您使用 CSS 設置圖像的widthheight 這是一個快速示例:

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

請注意,也可以使用 JavaScript 設置heightwidth 這是快速代碼示例:

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

此外,為了確保調整后的圖像看起來不錯,請將以下 css 規則添加到圖像選擇器:

據我所知,除 IE 之外的所有瀏覽器默認使用雙三次算法來調整圖像大小,因此您調整后的圖像在 Firefox 和 Chrome 中應該看起來不錯。

如果設置 css widthheight不起作用,您可能需要使用 css transform

如果出於某種原因您需要使用畫布,請注意有兩種方法可以調整圖像大小:使用 css 調整畫布大小或以較小的尺寸繪制圖像。

有關更多詳細信息,請參閱此問題

我通過右鍵單擊 Firefox 中的畫布元素並另存為來獲得此圖像。

替代文字

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';

所以無論如何,這是您示例的“固定”版本:

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';

為了調整寬度小於原始圖像的大小,我使用:

    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, .... )

它有效=)。

我有一種感覺,我編寫的模塊會產生與 photoshop 類似的結果,因為它通過對顏色數據進行平均而不是應用算法來保留顏色數據。 它有點慢,但對我來說它是最好的,因為它保留了所有的顏色數據。

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

它不會取最近的鄰居並丟棄其他像素,也不會采樣一組並取隨機平均值。 它采用每個源像素應輸出到目標像素的確切比例。 源中的平均像素顏色將是目標中的平均像素顏色,而這些其他公式,我認為它們不會。

如何使用的示例位於https://github.com/danschumann/limby-resize的底部

2018 年 10 月更新:這些天我的例子比其他任何東西都更具學術性。 Webgl 幾乎是 100%,所以你最好調整它的大小以產生類似的結果,但速度更快。 我相信 PICA.js 做到了這一點。 ——

其中一些解決方案的問題在於它們直接訪問像素數據並循環遍歷它以執行下采樣。 根據圖像的大小,這可能會占用大量資源,最好使用瀏覽器的內部算法。

drawImage()函數使用線性插值、最近鄰重采樣方法。 當您調整大小不超過原始大小的一半時,這很有效

如果循環一次最多只調整一半,結果會非常好,並且比訪問像素數據快得多。

此函數一次下采樣一半,直到達到所需的大小:

  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] );

歸功於此帖子

所以我不久前在使用畫布時發現的一些有趣的東西可能會有所幫助:

要自行調整畫布控件的大小,您需要使用height=""width=""屬性(或canvas.width / canvas.height元素)。 如果您使用 CSS 來調整畫布的大小,它實際上會拉伸(即:調整大小)畫布的內容以適合整個畫布(而不是簡單地增加或減少畫布的區域)。

嘗試將圖像繪制到畫布控件中,將高度和寬度屬性設置為圖像的大小,然后使用 CSS 將畫布調整為您正在尋找的大小,這是值得一試的。 也許這會使用不同的調整大小算法。

還需要注意的是,canvas在不同瀏覽器(甚至不同瀏覽器的不同版本)中的效果是不同的。 瀏覽器中使用的算法和技術可能會隨着時間的推移而改變(尤其是 Firefox 4 和 Chrome 6 即將推出,這將非常重視畫布渲染性能)。

此外,您可能還想嘗試一下 SVG,因為它也可能使用不同的算法。

祝你好運!

我將@syockit 的答案以及逐步降低的方法轉換為可重用的 Angular 服務,供任何感興趣的人使用: https : //gist.github.com/fisch0920/37bac5e741eaec60e983

我包含了這兩種解決方案,因為它們都有自己的優點/缺點。 lanczos 卷積方法以速度較慢為代價獲得更高質量,而逐步降尺度方法產生合理的抗鋸齒結果並且速度明顯更快。

用法示例:

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
    })
})

快速簡單的 Javascript 圖像調整器:

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.
})

歷史

這真的是經過多輪研究、閱讀和嘗試。

resizer 算法使用@ViliusL 的 Hermite 腳本(Hermite resizer 確實是最快的並且提供了相當不錯的輸出)。 擴展了您需要的功能。

Forks 1 worker 進行調整大小,以便在調整大小時不會凍結您的瀏覽器,這與那里的所有其他 JS 調整器不同。

感謝@syockit 提供了一個很棒的答案。 但是,我必須按如下方式重新格式化才能使其正常工作。 可能是由於 DOM 掃描問題:

$(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";
}
});

我想要一些定義明確的函數,所以最終得到了這些,希望對其他人也有用,

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

只是為了測試這個,在選項卡中打開的圖像上運行它。

尋找另一個偉大的簡單解決方案?

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

此解決方案將使用瀏覽器的調整大小算法! :)

我只是運行了一頁並排比較,除非最近發生了什么變化,否則我看不到使用畫布與簡單 css 更好的縮小(縮放)。 我在 FF6 Mac OSX 10.7 中進行了測試。 與原版相比仍然略軟。

然而,我確實偶然發現了一些確實產生巨大差異的東西,那就是在支持畫布的瀏覽器中使用圖像過濾器。 實際上,您可以像在 Photoshop 中一樣使用模糊、銳化、飽和度、波紋、灰度等操作圖像。

然后我發現了一個很棒的 jQuery 插件,它使這些過濾器的應用變得輕而易舉: http : //codecanyon.net/item/jsmanipulate-jquery-image-manipulation-plugin/428234

我只是在調整圖像大小后立即應用銳化濾鏡,這應該會給你想要的效果。 我什至不必使用畫布元素。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM