Подтвердить что ты не робот

Недопустимые результаты смешивания во всех браузерах с холстом HTML5.

Резюме

При многократном рисовании чего-либо (видимо, с низким значением альфа) на холсте, независимо от того, имеет ли он функцию drawImage() или fill, полученные цвета значительно неточны во всех проверенных мной браузерах. Здесь образец результатов, которые я получаю с конкретной операцией смешивания:

Invalid blending results

Демонстрация проблем

Для примера и некоторого кода для игры, проверьте этот jsFiddle, который я обработал:

http://jsfiddle.net/jMjFh/2/

Верхний набор данных является результатом теста, который вы можете настроить, отредактировав iters и rgba в верхней части кода JS. Он принимает любой цвет, указанный вами, RGBA all in range [0, 255], и печатает его на полностью чистом, прозрачном холсте iters количество раз. В то же время, он ведет текущий расчет того, что стандартная функция Porter-Duff source-over-dest будет производить для той же процедуры (то есть, что браузеры должны запускать и придумывать).

Нижний набор данных представляет собой граничный случай, который я нашел, когда смешение [127, 0, 0, 2] поверх [127, 0, 0, 63] приводит к неточному результату. Интересно, что смешение [127, 0, 0, 2] поверх [127, 0, 0, 62] и всех цветов [127, 0, 0, x], где x <= 62 дает ожидаемый результат. Обратите внимание, что этот граничный случай действителен только в Firefox и IE в Windows. В каждом другом браузере на каждой другой операционной системе, которую я тестировал, результаты намного хуже.

Кроме того, если вы запустите тест в Firefox в Windows и Chrome на Windows, вы заметите разницу значительная в результатах смешивания. К сожалению, мы не говорим об одном или двух значениях - это намного хуже.

Фоновая информация

Мне известно о том, что спецификация canvas HTML5 указывает, что выполнение drawImage() или putImageData(), а затем вызов getImageData() может отображать слегка различные результаты из-за ошибок округления. В этом случае и на основе всего, что я использовал до сих пор, мы говорим о чем-то миниатюрном, как красный канал, являющийся 122 вместо 121.

Как немного справочного материала, Я задал этот вопрос некоторое время назад, когда @NathanOstgard провел отличные исследования и сделал вывод о том, что виновата альфа-премультипликация. Хотя это, безусловно, может быть здесь, я думаю, что реальная основная проблема - это нечто большее.

Вопрос

Есть ли у кого-нибудь какие-либо подсказки, как я могу закончить с (дико неточными) значениями цвета, которые я вижу? Более того, и что еще более важно, есть ли у кого-нибудь идеи, как обойти проблему и обеспечить согласованные результаты во всех браузерах? Вручную смешивание пикселей не является вариантом из-за причин производительности.

Спасибо!


Изменить: Я просто добавил трассировку стека, которая выводит каждое промежуточное значение цвета и ожидаемое значение для этого этапа процесса.


Изменить: После немногочисленного изучения, я думаю, что я немного продвинулся.

Рассуждение для Chrome14 на Win7

В Chrome14 на Win7 полученный цвет после операции смешивания серый, что много значений от ожидаемого и желаемого красноватого результата. Это заставило меня поверить, что Chrome не имеет проблемы с округлением и точностью, потому что разница настолько велика. Вместо этого, похоже, Chrome хранит все значения пикселей с предварительно умноженной альфа.

Эта идея может быть продемонстрирована путем рисования одного цвета на холсте. В Chrome, если вы примените цвет [127, 0, 0, 2] один раз к холсту, вы получите [127, 0, 0, 2], когда вы его прочитаете. Однако, если вы дважды применяете [127, 0, 0, 2] к холсту, Chrome дает вам [85, 0, 0, 3], если вы проверите полученный цвет. Это действительно имеет смысл, если учесть, что предварительный эквивалент [127, 0, 0, 2] равен [1, 0, 0, 2].

По-видимому, когда Chrome14 на Win7 выполняет операцию смешивания, он ссылается на предварительно умноженное значение цвета [1, 0, 0, 2] для компонента dest и значение без предварительного умножения [127, 0, 0, 2] для компонента source. Когда эти два цвета смешиваются вместе, мы получаем [85, 0, 0, 3], используя подход Porter-Duff-источник-dest-dest, как и ожидалось.

Итак, похоже, что Chrome14 на Win7 непоследовательно ссылается на значения пикселей при работе с ними. Информация хранится в предварительно умноженном состоянии внутри, представлена ​​вам в непремущественно умноженной форме и управляется с использованием значений обеих форм.

Я думаю, что можно обойти это, сделав некоторые дополнительные вызовы getImageData() и/или putImageData(), но последствия производительности кажутся такими большими. Кроме того, это, похоже, не та же проблема, что и Firefox7 на Win7, поэтому для этого потребуется сделать еще несколько исследований.


Изменить: Ниже приведен один возможный подход, который, вероятно, будет работать.

Мысли о решении

Я еще не вернулся к работе над этим, но один подход WebGL, с которым я недавно пришел, состоял в том, чтобы запускать все операции рисования с помощью простого пиксельного шейдера, который выполняет комбинацию Porter-Duff-source-over-dest, Кажется, проблема здесь возникает только при интерполяции значений для целей смешивания. Итак, если шейдер считывает два значения пикселей, вычисляет результат и записывает (не смешивает) значение в пункт назначения, он должен смягчать проблему. По крайней мере, это не должно сочетаться. Тем не менее, все равно будут незначительные погрешности округления.

Я знаю, что в своем первоначальном вопросе я упомянул, что ручное смешивание пикселей не будет жизнеспособным. Для реализации 2D-холста приложения, которое требует обратной связи в реальном времени, я не вижу способа исправить это. Все смешения будут выполняться на процессоре и будут препятствовать выполнению другого кода JS. Однако с помощью WebGL ваш пиксельный шейдер работает на графическом процессоре, поэтому я уверен, что не должно быть никакого повышения производительности.

Сложная часть состоит в том, что можно кормить холст dest в виде текстуры в шейдере, потому что вам также нужно отображать его на холсте для просмотра. В конечном счете, вы хотите избежать необходимости генерировать объект текстуры WebGL из вашего видимого холста каждый раз, когда его необходимо обновить. Поддержание двух копий данных, с одной в виде текстуры в памяти и одной в виде видимого холста (который обновляется путем штамповки текстуры по мере необходимости), должен решить эту проблему.

4b9b3361

Ответ 1

О, боже. Я думаю, мы просто вошли в зону сумерек здесь. Боюсь, у меня нет ответа, но я могу предоставить хотя бы немного информации. Ваша догадка верна, что, по крайней мере, здесь что-то больше.

Я бы предпочел, чтобы пример был более простым (и, следовательно, менее подвержен какой-либо ошибке), я боюсь, что у вас может быть проблема с 0-255 где-то вместо 0-1, но он не выглядел полностью), чем у вас, поэтому Я взбила что-то крошечное. То, что я нашел, не понравилось:

<canvas id="canvas1" width="128" height="128"></canvas>
<canvas id="canvas2" width="127" height="127"></canvas>

+

var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
var can2 = document.getElementById('canvas2');
var ctx2 = can2.getContext('2d');

var alpha = 2/255.0;

ctx.fillStyle = 'rgba(127, 0, 0, ' + alpha + ')';
ctx2.fillStyle = 'rgba(127, 0, 0, ' + alpha + ')'; // this is grey
//ctx2.fillStyle = 'rgba(171, 0, 0, ' + alpha + ')'; // this works

for (var i = 0; i < 32; i++) {
      ctx.fillRect(0,0,500,500);
      ctx2.fillRect(0,0,500,500);
}

Уступает:

enter image description here

Смотрите сами:

http://jsfiddle.net/jjAMX/

Похоже, что если ваша площадь поверхности слишком мала (не область fillRect, а сама область холста), то операция рисования будет стирать.

Кажется, он отлично работает в IE9 и FF7. Я отправил его в Chromium как ошибка. (Мы посмотрим, что они скажут, но их типичный M.O. пока что игнорирует большинство отчетов об ошибках, если они не являются проблемами безопасности).

Я бы попросил вас проверить ваш пример - я думаю, что у вас может быть проблема с 0-255 до 0-1. Код, который у меня здесь, кажется, работает просто dandy, за исключением, возможно, ожидаемого красноватого цвета, но - согласованный итоговый цвет во всех браузерах, за исключением этой проблемы Chrome.

В конце концов, Chrome, вероятно, делает что-то для оптимизации холстов с площадью поверхности, меньшей 128х128, которая загрязняет вещи.

Ответ 2

Настройка globalCompositeOperation на "lighter" (операция, в которой перекрывающиеся значения пикселей добавляются вместе, это не операция по умолчанию для многих браузеров) дает один и тот же цвет и дает результат в ie9, chrome 14 и последнем авроре firefox:

Expected: [126.99999999999999, 0, 0, 63.51372549019608]
Result: [127, 0, 0, 64]

Expected: [126.99999999999999, 0, 0, 63.51372549019608]
Result: [127, 0, 0, 64]

http://jsfiddle.net/jMjFh/11/

edit: вы можете узнать, что здесь означают различные составные операции:

https://developer.mozilla.org/en/Canvas_tutorial/Compositing#globalCompositeOperation

например, "copy" был полезен для меня, когда мне нужно было сделать анимацию затухания в определенных частях холста