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

Демонстрация проблем
Для примера и некоторого кода для игры, проверьте этот jsFiddle, который я обработал:
Верхний набор данных является результатом теста, который вы можете настроить, отредактировав 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 из вашего видимого холста каждый раз, когда его необходимо обновить. Поддержание двух копий данных, с одной в виде текстуры в памяти и одной в виде видимого холста (который обновляется путем штамповки текстуры по мере необходимости), должен решить эту проблему.
