Изменение размера изображения на холсте HTML5
Я пытаюсь создать эскиз изображения на стороне клиента с помощью javascript и элемента canvas, но когда я сжимаю изображение вниз, это выглядит ужасно. Похоже, что он был уменьшен в photoshop с пересчетом, установленным на "ближайший сосед" вместо Бикубического. Я знаю, что его можно заставить это выглядеть правильно, потому что этот сайт может сделать это просто отлично, используя холст, а также. Я попытался использовать тот же код, который они делают, как показано в ссылке "[Source]", но он все еще выглядит ужасный. Есть ли что-то, что я упускаю, некоторые настройки, которые должны быть установлены или что-то еще?
EDIT:
Я пытаюсь изменить размер jpg. Я попытался изменить размер того же jpg на связанном сайте и в photoshop, и он отлично выглядит при уменьшении.
вот соответствующий код:
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:
Кажется, я ошибся, связанный веб-сайт не делал никакой лучшей работы по сокращению изображения. Я попробовал другие предложенные методы и никто из них не пойдет. Вот к чему привели различные методы:
Photoshop:
холст:
Изображение с визуализацией изображения: набор оптимальных параметров и масштабирование по ширине / высоте:
Изображение с визуализацией изображения: набор оптимальных значений и масштабирование с помощью-moz-transform:
размер холста на pixastic:
Я думаю, это означает firefox не использует бикубическую выборку, как предполагалось. Мне просто придется подождать, пока они на самом деле добавить.
EDIT3:
18 ответов:
Так что же делать, если все браузеры (на самом деле, 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"; };
...с помощью которого вы можете получить такие результаты!
так или иначе, вот "исправленная" версия вашего примера:
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); };
теперь пришло время пит ваши лучшие браузеры там и посмотреть, какой из них будет наименее вероятно увеличить кровяное давление вашего клиента!
МММ, где мой тег сарказма?
(так как многие части кода основаны на Генератор Галереи Анрифф это также охватывается GPL2? Я не знаю)
† на самом деле из-за ограничения javascript, многоядерный не поддерживается.
быстрый алгоритм изменения размера/пересчета изображений с помощью Эрмита фильтр с JavaScript. Поддержка прозрачности, дает хорошее качество. Предварительный просмотр:
обновление: версия 2.0 добавлена на GitHub (быстрее, веб-работники + переносимые объекты). Наконец - то у меня получилось!
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 - это сильно оптимизированный resizer с дискретными алгоритмами. Смотрите демо.
например, исходное изображение из первого сообщения изменяется в 120 мс с фильтром Lanczos и окном 3px или 60 мс с коробчатым фильтром и окном 0.5 px. Для огромного 17mb изображения 5000x3000px изменение размера занимает ~1s на рабочем столе и 3s на мобильном телефоне.
все принципы изменения размера были очень хорошо описаны в этой теме, и pica не добавляет ракетостроение. Но он оптимизирован очень хорошо подходит для современных JIT-s, и готов к использованию из коробки (через npm или bower). Кроме того, он использует webworkers, когда это возможно, чтобы избежать зависания интерфейса.
Я также планирую добавить поддержку нерезкой маски в ближайшее время, потому что это очень полезно после снижения масштаба.
Я знаю, что это старая нить, но это может быть полезно для некоторых людей, таких как я, что через несколько месяцев после того, как вы впервые столкнулись с этой проблемой.
вот некоторый код, который изменяет размер изображения каждый раз при загрузке изображения. Я знаю, что это совсем не оптимально, но я предоставляю это как доказательство концепции.
кроме того, извините за использование 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 вызывается один раз при загрузке документа и после этого он вызывается каждый раз, когда окно получает событие resize.
Я тестировал его в Chrome 6 и Firefox 3.6, как на Mac. Эта "техника" ест процессор так, как если бы летом было мороженое, но она делает свое дело.
Я поставил некоторые алгоритмы для интерполяции изображений на массивах пикселей HTML canvas, которые могут быть полезны здесь:
http://jsperf.com/pixel-interpolation/2
Они могут быть скопированы / вставлены и могут использоваться внутри веб - работников для изменения размера изображений (или любой другой операции, требующей интерполяции-я использую их для определения изображений в данный момент).
Я не добавил материал lanczos выше, поэтому не стесняйтесь добавлять это в качестве сравнения, если бы вы как.
если вы просто пытаетесь изменить размер изображения, я советую
width
иheight
изображения с CSS. Вот краткий пример:.small-image { width: 100px; height: 100px; }
отметим, что
height
иwidth
также можно установить с помощью JavaScript. Вот быстрый пример кода: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 в селектор изображений:
-ms-interpolation-mode: bicubic
: ввести в IE7image-rendering: optimizeQuality
: введено в FireFox 3.6насколько я могу судить, все браузеры, кроме IE, используют бикубический алгоритм для изменения размера изображений по умолчанию, поэтому ваши измененные изображения должны хорошо выглядеть в Firefox и Chrome.
если установить css
width
иheight
не работает, вы можете играть с csstransform
:если по какой-то причине вы нужно чтобы использовать холст, обратите внимание, что есть два способа изменить размер изображения: путем изменения размера холста с помощью css или путем рисования изображения в меньшем размере.
посмотреть этот вопрос для более подробной информации.
надеюсь, что это помогает!
Я настоятельно рекомендую вам проверить этой ссылке и убедитесь, что он установлен в true.
управление масштабированием изображений
введен в Gecko 1.9.2 (в Firefox 3.6 / Thunderbird 3.1 / Fennec 1.0)
движок Gecko 1.9.2 введен свойство mozImageSmoothingEnabled к элемент canvas; если это логическое значение false, изображения не будет сглаживается при масштабировании. Это свойство True по умолчанию. вид простой отпечаток?
- cx.mozImageSmoothingEnabled = false;
это функция javascript, адаптированная из кода @Telanor. При передаче изображения 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(); }
для изменения размера изображения с шириной меньше, чем оригинал, я использую:
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, .... )
и оно работает =).
Я получил это изображение, щелкнув правой кнопкой мыши элемент canvas в 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';
Итак, что-то интересное, что я нашел некоторое время назад во время работы с холстом, что может быть полезно:
чтобы изменить размер элемента управления canvas самостоятельно, необходимо использовать
height=""
иwidth=""
атрибуты (илиcanvas.width
/canvas.height
элементов). Если вы используете CSS для изменения размера холста, он будет фактически растягивать (т. е.: изменять размер) содержимое холста, чтобы соответствовать полному холсту (а не просто увеличивать или уменьшать площадь холста.это стоило бы попробовать рисование изображения в элементе управления canvas с атрибутами height и width, установленными на размер изображения, а затем с помощью CSS для изменения размера холста до нужного размера. Возможно, это будет использовать другой алгоритм изменения размера.
следует также отметить, что canvas имеет разные эффекты в разных браузерах (и даже в разных версиях разных браузеров). Алгоритмы и методы, используемые в браузерах, вероятно, изменятся с течением времени (особенно с Firefox 4 и Chrome 6 выходит так скоро, что будет уделять большое внимание производительности рендеринга холста).
кроме того, вы можете дать SVG выстрел, тоже, как это, вероятно, использует другой алгоритм, а также.
удачи!
проблема с некоторыми из этих решений заключается в том, что они обращаются непосредственно к данным пикселей и перебирают их для выполнения понижающей дискретизации. В зависимости от размера изображения, это может быть очень ресурсоемким, и было бы лучше использовать внутренние алгоритмы браузера.
на 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] );
кредиты этот пост
У меня есть ощущение, что модуль, который я написал, даст аналогичные результаты photoshop, поскольку он сохраняет данные цвета, усредняя их, не применяя алгоритм. Это немного медленно, но для меня это лучший, потому что он сохраняет все данные о цвете.
https://github.com/danschumann/limby-resize/blob/master/lib/canvas_resize.js
Он не берет ближайшего соседа и не отбрасывает другие пиксели или не выбирает группу и не принимает случайное среднее значение. Оно принимает точная пропорция каждый исходный пиксель должен выводиться в целевой пиксель. Средний цвет пикселя в источнике будет средним цветом пикселя в пункте назначения, которого в этих других формулах, я думаю, не будет.
пример использования находится в нижней части https://github.com/danschumann/limby-resize
я преобразовал ответ @syockit, а также пошаговый подход в многоразовый угловой сервис для всех, кто заинтересован:https://gist.github.com/fisch0920/37bac5e741eaec60e983
Я включил оба решения, поскольку они оба имеют свои плюсы / минусы. Метод свертки ланцоса является более качественным за счет снижения скорости, в то время как поэтапный подход к масштабированию дает разумно сглаженные результаты и значительно быстрее.
пример использования:
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 Image resizer:
https://github.com/calvintwr/Hermite-resize
использование:
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 });
история
это действительно после многих раундов исследований, чтения и попыток.
алгоритм изменения размера использует сценарий Hermite @ViliusL (Hermite resizer действительно самый быстрый и дает достаточно хороший результат). Расширенный с функциями, которые вам нужны.
вилки 1 работник для того чтобы сделать изменение размера, чтобы он не замораживал Ваш браузер при изменении размера, в отличие от всех других JS-размеров.
Я просто запустил страницу параллельных сравнений, и если что-то не изменилось в последнее время, я не мог видеть лучшего сокращения (масштабирования) с помощью canvas против простого css. Я тестировал в FF6 Mac OSX 10.7. Все еще немного мягкий по сравнению с оригиналом.
однако я наткнулся на то, что действительно имело огромное значение, и это было использование фильтров изображений в браузерах, поддерживающих canvas. Вы можете фактически манипулировать изображениями так же, как в Photoshop с размытием, резкостью, насыщенностью, пульсацией, оттенки серого и т. д.
затем я нашел удивительный плагин jQuery, который делает применение этих фильтров оснастки: http://codecanyon.net/item/jsmanipulate-jquery-image-manipulation-plugin/428234
Я просто применяю фильтр резкости сразу после изменения размера изображения, который должен дать вам желаемый эффект. Мне даже не пришлось использовать элемент canvas.
ищете еще одно отличное простое решение?
var img=document.createElement('img'); img.src=canvas.toDataURL(); $(img).css("background", backgroundColor); $(img).width(settings.width); $(img).height(settings.height);
Это решение будет использовать алгоритм изменения размера браузера! :)
спасибо @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"; } });