Redimensionando uma imagem em uma canvas HTML5

Eu estou tentando criar uma imagem em miniatura no lado do cliente usando javascript e um elemento de canvas, mas quando eu reduzir a imagem, parece terrível. Parece que foi reduzido no photoshop com o reamostramento definido como ‘Nearest Neighbor’ em vez de Bicubic. Eu sei que é possível fazer com que isso pareça certo, porque este site pode fazer isso muito bem usando uma canvas também. Eu tentei usar o mesmo código que eles fazem como mostrado no link “[Fonte]”, mas ainda parece terrível. Há algo que estou perdendo, alguma configuração que precisa ser definida ou algo assim?

EDITAR:

Eu estou tentando resize um jpg. Eu tentei resize o mesmo jpg no site vinculado e no photoshop, e parece bem quando reduzido.

Aqui está o código relevante:

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:

Parece que eu estava enganado, o site vinculado não estava fazendo nenhum trabalho melhor de reduzir a imagem. Eu tentei os outros methods sugeridos e nenhum deles parece melhor. Isto é o que os diferentes methods resultaram em:

Photoshop:

texto alternativo

Tela de pintura:

texto alternativo

Imagem com renderização de imagem: optimizeQuality definido e dimensionado com largura / altura:

texto alternativo

Imagem com renderização de imagem: optimizeQuality set e escalonado com -moz-transform:

texto alternativo

Lona resize em pixastic:

texto alternativo

Eu acho que isso significa que o Firefox não está usando amostragem bicúbica como é suposto. Vou ter que esperar até que eles realmente o adicionem.

EDIT3:

Imagem original

Então, o que você faz se todos os navegadores (na verdade, o Chrome 5 me deu uma boa) não lhe dará qualidade suficiente para reamostragem? Você os implementa então! Ah, vamos lá, estamos entrando na nova era da Web 3.0, navegadores compatíveis com HTML5, compiladores de javascript JIT super otimizados, máquinas de vários núcleos (†), com toneladas de memory, do que você tem medo? Ei, tem a palavra java em javascript, então isso deve garantir o desempenho, certo? Eis o código gerador de miniaturas:

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

... com o qual você pode produzir resultados como esses!

img717.imageshack.us/img717/8910/lanczos358.png

De qualquer forma, aqui está uma versão 'fixa' do seu exemplo:

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

Agora é hora de testar seus melhores navegadores e ver qual deles provavelmente aumentará a pressão sanguínea do seu cliente!

Umm, onde está o meu sarcasmo?

(já que muitas partes do código são baseadas no Anrieff Gallery Generator, ele também é coberto pela GPL2? Eu não sei)

na verdade devido à limitação do javascript, o multi-core não é suportado.

Algoritmo rápido de redimensionamento / resample de imagem usando o filtro Hermite com JavaScript. Suporte a transparência, dá boa qualidade. Visualizar:

insira a descrição da imagem aqui

Atualização : versão 2.0 adicionada no GitHub (mais rápido, web workers + objects transferíveis). Finalmente eu consegui trabalhar!

Git: https://github.com/viliusle/Hermite-resize
Demonstração: 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); } 

Tente pica – é um resizer altamente otimizado com algoritmos selecionáveis. Veja demonstração .

Por exemplo, a imagem original do primeiro post é redimensionada em 120ms com filtro Lanczos e janela 3px ou 60ms com filtro Box e janela 0.5px. Para uma enorme resolução de 17mb, o tamanho de 5000x3000px redimensiona-se em 1s no desktop e 3s no celular.

Todos os princípios de redimensionamento foram descritos muito bem neste segmento, e a pica não adiciona ciência de foguetes. Mas é otimizado muito bem para os modernos JIT-s e está pronto para uso fora da checkbox (via npm ou bower). Além disso, ele usa webworkers quando disponíveis para evitar congelamentos de interface.

Eu também pretendo adicionar suporte a máscara de nitidez em breve, porque é muito útil após o downscale.

Eu sei que este é um tópico antigo, mas pode ser útil para algumas pessoas, como eu, que meses depois, estão atingindo esse problema pela primeira vez.

Aqui está um código que redimensiona a imagem toda vez que você recarrega a imagem. Eu sei que isso não é o ideal, mas eu o forneço como prova de conceito.

Além disso, desculpe por usar o jQuery para seletores simples, mas eu me sinto muito confortável com a 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; } 
     Canvas Resize      

Eu coloquei alguns algoritmos para fazer interpolação de imagem em matrizes de pixel de canvas html que podem ser úteis aqui:

http://jsperf.com/pixel-interpolation/2

Estes podem ser copiados / colados e podem ser usados ​​dentro dos web workers para resize imagens (ou qualquer outra operação que exija interpolação – estou usando-os para desfigurar imagens no momento).

Eu não adicionei as coisas lanczos acima, então sinta-se à vontade para adicionar isso como uma comparação, se quiser.

Se você está simplesmente tentando resize uma imagem, recomendo definir a width e a height da imagem com CSS. Aqui está um exemplo rápido:

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

Observe que a height e a width também podem ser definidas usando JavaScript. Aqui está um exemplo de código rápido:

 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 

Além disso, para garantir que a imagem redimensionada pareça boa, adicione as seguintes regras css ao seletor de imagens:

  • -ms-interpolation-mode: bicubic : introduza no IE7
  • image-rendering: optimizeQuality : introduzido no FireFox 3.6

Tanto quanto eu posso dizer, todos os navegadores, exceto o IE, usam um algoritmo bicubico para resize imagens por padrão, para que suas imagens redimensionadas fiquem bem no Firefox e no Chrome.

Se a configuração da width e height do css não funcionar, você pode querer brincar com uma transform css:

  • -moz-transform: scale(sx[, sy])
  • -webkit-transform:scale(sx[, sy])

Se, por alguma razão, você precisar usar uma canvas, observe que há duas maneiras de resize uma imagem: redimensionando a canvas com css ou desenhando a imagem em um tamanho menor.

Veja esta questão para mais detalhes.

Espero que isto ajude!

Eu sugiro que você verifique este link e verifique se ele está definido como verdadeiro.

Controlando o comportamento de dimensionamento de imagem

Introduzido no Gecko 1.9.2 (Firefox 3.6 / Thunderbird 3.1 / Fennec 1.0)

O Gecko 1.9.2 introduziu a propriedade mozImageSmoothingEnabled no elemento canvas; se esse valor booleano for falso, as imagens não serão suavizadas quando forem dimensionadas. Essa propriedade é verdadeira por padrão. ver plainprint?

  1. cx.mozImageSmoothingEnabled = false;

Esta é uma function javascript adaptada do código do @Telanor. Ao passar uma imagem base64 como primeiro argumento para a function, ela retorna a base64 da imagem redimensionada. maxWidth e maxHeight são opcionais.

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

Para resize a imagem com largura menor que o original, eu uso:

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

e funciona =).

Eu tenho esta imagem clicando com o botão direito do mouse no elemento da canvas no firefox e salvando como.

texto alternativo

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

De qualquer forma, aqui está uma versão ‘fixa’ do seu exemplo:

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

Então, algo interessante que achei há algum tempo, enquanto trabalhava com canvas, pode ser útil:

Para resize o controle de canvas sozinho, é necessário usar os atributos height="" e width="" (ou canvas.width / canvas.height elements). Se você usar CSS para resize a canvas, ela aumentará (isto é, resizeá) o conteúdo da canvas para caber na canvas inteira (em vez de simplesmente aumentar ou diminuir a área da canvas.

Valeria a pena tentar criar a imagem em um controle de canvas com os atributos height e width definidos para o tamanho da imagem e, em seguida, usando o CSS para resize a canvas para o tamanho que você está procurando. Talvez isso usasse um algoritmo de redimensionamento diferente.

Também deve ser notado que a canvas tem efeitos diferentes em navegadores diferentes (e até versões diferentes de diferentes navegadores). É provável que os algoritmos e técnicas usados ​​nos navegadores mudem com o tempo (especialmente com o lançamento do Firefox 4 e do Chrome 6 tão cedo, o que colocará forte ênfase no desempenho da renderização de canvass).

Além disso, você pode querer dar uma chance ao SVG, já que provavelmente também usa um algoritmo diferente.

Boa sorte!

O problema com algumas dessas soluções é que elas acessam diretamente os dados de pixel e passam por elas para realizar a redução da amostragem. Dependendo do tamanho da imagem, isso pode consumir muitos resources, e seria melhor usar os algoritmos internos do navegador.

A function drawImage () está usando um método de reamostragem de interpolação linear, mais próxima do vizinho. Isso funciona bem quando você não está redimensionando mais da metade do tamanho original .

Se você fizer um loop para resize apenas o máximo de uma metade de cada vez, os resultados serão muito bons e muito mais rápidos do que acessar os dados de pixel.

Esta function diminui para metade de cada vez até atingir o tamanho desejado:

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

Créditos para esta postagem

Tenho a sensação de que o módulo que escrevi produzirá resultados semelhantes ao photoshop, pois preserva os dados de cor calculando a média deles, não aplicando um algoritmo. É meio lento, mas para mim é o melhor, porque preserva todos os dados de colors.

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

Ele não pega o vizinho mais próximo e solta outros pixels, ou amostra um grupo e pega uma média aleatória. Ele leva a proporção exata que cada pixel de origem deve gerar no pixel de destino. A cor média do pixel na fonte será a cor média do pixel no destino, o que essas outras fórmulas, eu acho que elas não serão.

um exemplo de como usar está na parte inferior de https://github.com/danschumann/limby-resize

Eu converti a resposta da @ syockit, bem como a abordagem em um serviço Angular reutilizável para quem estiver interessado: https://gist.github.com/fisch0920/37bac5e741eaec60e983

Eu incluí as duas soluções porque ambas têm suas próprias vantagens / desvantagens. A abordagem de convolução lanczos é de maior qualidade ao custo de ser mais lenta, enquanto a abordagem de downscaling passo a passo produz resultados razoavelmente antialiased e é significativamente mais rápida.

Exemplo de uso:

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

Resizer rápido e simples da imagem de Javascript:

https://github.com/calvintwr/Hermite-resize

Usar:

 h.resize({ source: document.getElementById('image'), // any canvas or image elements, jQuery or native width: 400, height: 600, output: 'image', // [optional] `image` or `canvas`. If not entered output is same as input element. quality: 0.7, // [optional] applicable for `image` output only }, function(output) { //your callback }); 

História

Isto é realmente depois de muitas rodadas de pesquisa, lendo e tentando.

O algoritmo resizer usa o script Hermite do @ ViliusL (o Hermite resizer é realmente o mais rápido e dá uma saída razoavelmente boa). Estendido com resources que você precisa.

Forks 1 worker para fazer o redimensionamento para que ele não congele seu navegador ao resize, ao contrário de todos os outros JS resizers lá fora.

Acabei de fazer uma página de comparações lado a lado e, a menos que algo tenha mudado recentemente, não consegui enxergar melhor o downsizing (dimensionamento) usando canvas vs. simples css. Eu testei no FF6 Mac OSX 10.7. Ainda ligeiramente suave em relação ao original.

No entanto, eu deparei com algo que fez uma grande diferença e que estava usando filtros de imagem em navegadores que suportam a canvas. Você pode manipular imagens de maneira muito parecida com o Photoshop, com borrão, nitidez, saturação, ondulação, tons de cinza etc.

Eu então encontrei um plug-in jQuery incrível que torna a aplicação desses filtros muito fácil: http://codecanyon.net/item/jsmanipulate-jquery-image-manipulation-plugin/428234

Eu simplesmente aplico o filtro de nitidez logo após resize a imagem, o que deve lhe dar o efeito desejado. Eu nem precisei usar um elemento de canvas.

Procurando outra ótima solução simples?

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

Esta solução usará o algoritmo de redimensionamento do navegador! 🙂

Obrigado @syockit por uma resposta incrível. no entanto, tive que reformatar um pouco da seguinte maneira para que funcionasse. Talvez devido a problemas de digitalização do 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"; } });