Изменение размеров изображения в холсте HTML5

Я пытаюсь создать изображение миниатюр на стороне клиента с помощью 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:

alt text

Холст:

alt text

Изображение с рендерингом изображения: набор optimizeQuality и масштабируемый с шириной/высотой:

alt text

Изображение с рендерингом изображения: набор optimizeQuality и масштабируемый с - moz-преобразовывает:

alt text

Холст изменяет размер на pixastic:

alt text

Я предполагаю, что это означает, что Firefox не использует bicubic, выбирающий как его воображаемое к. Я должен буду просто ожидать, пока они на самом деле не добавляют его.

EDIT3:

Исходное изображение

309
задан kukuch 27 November 2018 в 06:36
поделиться

5 ответов

Итак, что вы будете делать, если все браузеры (на самом деле, 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 многоядерность не поддерживается.

390
ответ дан 23 November 2019 в 01:14
поделиться

Если вы просто пытаетесь изменить размер изображения, я бы рекомендовал задать 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 к селектору изображения:

Насколько я могу судить, все браузеры, кроме IE, используют бикубический алгоритм для изменения размера изображений по умолчанию, поэтому ваши измененные изображения должны выглядеть хорошо в Firefox и Chrome.

Если установка css width и height не работает, вы можете поиграть с css transform:

Если по какой-то причине вам необходимо использовать холст, обратите внимание, что есть два способа изменить размер изображения: изменить размер холста с помощью css или нарисовать изображение меньшего размера.

См. этот вопрос для более подробной информации.

6
ответ дан 23 November 2019 в 01:14
поделиться

Я получил это изображение, щелкнув правой кнопкой мыши элемент холста в Firefox и сохранив его как.

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

так или иначе, вот «фиксированная» версия вашего примера:

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';
4
ответ дан 23 November 2019 в 01:14
поделиться

Итак, кое-что интересное, что я обнаружил некоторое время назад при работе с canvas, что может оказаться полезным:

Чтобы изменить размер элемента управления canvas самостоятельно, вам нужно использовать атрибуты height="" и width="" (или элементы canvas.width/canvas.height). Если вы используете CSS для изменения размера холста, он будет фактически растягивать (т.е. изменять размер) содержимое холста, чтобы оно поместилось на весь холст (а не просто увеличивать или уменьшать площадь холста.

Стоит попробовать нарисовать изображение в элементе управления canvas с атрибутами height и width, установленными на размер изображения, а затем использовать CSS для изменения размера холста до нужного вам размера. Возможно, в этом случае используется другой алгоритм изменения размера.

Следует также отметить, что в разных браузерах (и даже в разных версиях разных браузеров) холст работает по-разному. Алгоритмы и методы, используемые в браузерах, вероятно, будут меняться со временем (особенно с учетом скорого выхода Firefox 4 и Chrome 6, в которых большое внимание будет уделяться производительности рендеринга canvas).

Кроме того, вы можете попробовать SVG, так как в нем, вероятно, также используется другой алгоритм.

Желаю удачи!

2
ответ дан 23 November 2019 в 01:14
поделиться

Я бы настоятельно рекомендовал вам проверить эту ссылку и убедиться, что она установлена на true.

Управление поведением масштабирования изображений

Введено в Gecko 1.9.2 (Firefox 3.6. / Thunderbird 3.1 / Fennec 1.0)

В Gecko 1.9.2 появилась функция свойство mozImageSmoothingEnabled для элементу canvas; если это булево значение равно false, изображения не будут сглаживаться при масштабировании. По умолчанию это свойство true по умолчанию. view plainprint?

  1. cx.mozImageSmoothingEnabled = false;
6
ответ дан 23 November 2019 в 01:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: