Как остановить альфа-премультипликацию с помощью canvas imageData?

Есть ли способ остановить премультипликацию альфа-канала для данных холста или обходной путь?

Я хочу создать образ (в данном случае некоторые случайные значения rgba) и сохранить холст в качестве изображения.

На втором этапе я хочу сравнить исходное изображение с сгенерированным изображением с помощью imageData, однако это не будет работать из-за предварительного умножения альфа-канала моих пикселей rgba в сгенерированном изображении.

Пример

function drawImage(ctx) {
    var img = ctx.createImageData(canvas.width,canvas.height);

        for (var i=img.data.length;i-=4;) {     
                img.data[i] = Math.floor(Math.random() * 255);
                img.data[i+1] = Math.floor(Math.random() * 255);
                img.data[i+2] = Math.floor(Math.random() * 255);
                img.data[i+3] = Math.floor(Math.random() * 255);
        }

        ctx.putImageData(img, 0, 0);
            // our image data we just set
        console.log(img.data);
            // the image data we just placed onto the canvas
        console.log(ctx.getImageData(0,0,canvas.width, canvas.height).data);
}   

В консоли вы найдете два выхода console.log. Первый перед премультипликацией, второй - после умножения. Эти два выхода отличаются друг от друга, некоторые значения отключены на 3 или более. Это происходит только при наличии частичной прозрачности (альфа устанавливается на что угодно, кроме 255).

Есть ли способ получить тот же результат? Любые идеи об этой проблеме? Любые идеи, как создать что-то вроде обходного пути для этой проблемы?

Заранее благодарю вас!

Ответ 1

Bleh, это признанная проблема в отношении спецификации холста. Он отмечает:

Из-за потери качества конвертирования в и из предварительно умноженных значений цвета альфа, пиксели, которые только что были установлены с использованием putImageData(), могут быть возвращены эквивалентному getImageData() как разные значения.

Итак, это:

var can = document.createElement('canvas');
var ctx = can.getContext('2d');
can.width = 1;
can.height = 1;
var img = ctx.createImageData(1, 1);
img.data[0] = 40;
img.data[1] = 90;
img.data[2] = 200;
var ALPHAVALUE = 5;
img.data[3] = ALPHAVALUE;
console.log(img.data); 
ctx.putImageData(img, 0, 0);
console.log(ctx.getImageData(0, 0, 1, 1).data); 

выходы:

[40, 90, 200, 5]
[51, 102, 204, 5]

Во всех браузерах.

Итак, это операция с потерями, нет обходного пути, если они не изменят спецификацию, чтобы дать возможность не использовать премультипликацию. Это обсуждалось еще в 2008 году в списке рассылки WHATWG, и они решили, что "поездка туда и обратно" /идентификация данных "поставить/получить изображение" не является обещанием, которое спекуляция желательна.

Если вам нужно "сохранить" данные изображения, вы не сможете сохранить его и сохранить ту же точность, используя putImageData. Обходные пути путем рисования полных альфа-данных во временный холст и перерисовка на основной холст с меньшим globalAlpha тоже не будут работать.

Итак, вам не повезло. К сожалению.


По сей день (12 мая 2014 года) это все еще обсуждается в списке WHATWG: http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2014-May/296792.html

Ответ 2

Хорошо, это все еще облом...

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