Я пытаюсь создать изображение миниатюр на стороне клиента с помощью JavaScript и элемента холста, но когда я уменьшаю изображение вниз, это выглядит ужасным. Выглядит, как будто это было уменьшено в фотошопе с набором передискретизации 'Ближайшему Соседу' вместо Bicubic. Я знаю, что его возможное заставляет это выглядеть правильным, потому что этот сайт может сделать это очень хорошо использование холста также. Я попытался использовать тот же код, который они делают как показано в" [Источник]" ссылка, но это все еще выглядит ужасным. Есть ли что-то, что я пропускаю, некоторые устанавливающие, который должен быть установлен или что-то?
Править:
Я пытаюсь изменить размер jpg. Я попытался изменить размер того же 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;
}
EDIT2:
Кажется, что я ошибался, связанный веб-сайт не делал немного лучше задания уменьшения изображения. Я попробовал другие предложенные методы, и ни один из них не смотрит немного лучше. Это - то, к чему привели различные методы:
Photoshop:
Холст:
Изображение с рендерингом изображения: набор optimizeQuality и масштабируемый с шириной/высотой:
Изображение с рендерингом изображения: набор optimizeQuality и масштабируемый с - moz-преобразовывает:
Холст изменяет размер на pixastic:
Я предполагаю, что это означает, что Firefox не использует bicubic, выбирающий как его воображаемое к. Я должен буду просто ожидать, пока они на самом деле не добавляют его.
EDIT3:
Итак, что вы будете делать, если все браузеры (на самом деле, 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);
};
Теперь пора протестировать ваши лучшие браузеры и посмотреть, какой из них с наименьшей вероятностью повысит кровяное давление вашего клиента!
Ммм, где мой тег сарказма?
(поскольку многие части кода основаны на Anrieff Gallery Generator , распространяется ли он также под GPL2? Я не знаю)
† на самом деле из-за ограничений javascript многоядерность не поддерживается.
Если вы просто пытаетесь изменить размер изображения, я бы рекомендовал задать 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
не работает, вы можете поиграть с 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';
Итак, кое-что интересное, что я обнаружил некоторое время назад при работе с canvas, что может оказаться полезным:
Чтобы изменить размер элемента управления canvas самостоятельно, вам нужно использовать атрибуты height=""
и width=""
(или элементы canvas.width
/canvas.height
). Если вы используете CSS для изменения размера холста, он будет фактически растягивать (т.е. изменять размер) содержимое холста, чтобы оно поместилось на весь холст (а не просто увеличивать или уменьшать площадь холста.
Стоит попробовать нарисовать изображение в элементе управления canvas с атрибутами height и width, установленными на размер изображения, а затем использовать CSS для изменения размера холста до нужного вам размера. Возможно, в этом случае используется другой алгоритм изменения размера.
Следует также отметить, что в разных браузерах (и даже в разных версиях разных браузеров) холст работает по-разному. Алгоритмы и методы, используемые в браузерах, вероятно, будут меняться со временем (особенно с учетом скорого выхода Firefox 4 и Chrome 6, в которых большое внимание будет уделяться производительности рендеринга canvas).
Кроме того, вы можете попробовать SVG, так как в нем, вероятно, также используется другой алгоритм.
Желаю удачи!
Я бы настоятельно рекомендовал вам проверить эту ссылку и убедиться, что она установлена на true.
Управление поведением масштабирования изображений
Введено в Gecko 1.9.2 (Firefox 3.6. / Thunderbird 3.1 / Fennec 1.0)
В Gecko 1.9.2 появилась функция свойство mozImageSmoothingEnabled для элементу canvas; если это булево значение равно false, изображения не будут сглаживаться при масштабировании. По умолчанию это свойство true по умолчанию. view plainprint?
- cx.mozImageSmoothingEnabled = false;