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

Резюме

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

Invalid blending results

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

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

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

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

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

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

Общие сведения

Мне известно о том, что спецификация холста 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] для компонента источника . Когда эти два цвета смешиваются вместе, мы, как и ожидалось, получаем [85, 0, 0, 3] с использованием подхода Портера-Даффа «источник-над-назначением».

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

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


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

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

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

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

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

19
задан Community 23 May 2017 в 10:32
поделиться