Почему HTML Canvas getImageData() не возвращает те же самые значения, которые были только что установлены?
При записи пикселей в контекст HTML Canvas с помощью putImageData
я обнаруживаю, что значения пикселей не совсем совпадают, когда я их снова извлекаю. Я поставил тестовую страницу , показывая эту проблему. Свернувшись, проблема в том, что:
var id = someContext.getImageData(0,0,1,1);
id.data[0]=id.data[3]=64; // 25% red, 25% alpha
id.data[1]=id.data[2]=0; // No blue or green
someContext.putImageData(id,0,0);
var newData = someContext.getImageData(0,0,1,1);
console.log( newData.data[0] );
В Chrome v8 красное значение возвращается как 63
; на Firefox v3.6, Safari v5 и IE9 красное значение возвращается как 67
(все в Windows). В OS X Chrome v7, Safari v5 и Firefox v3.6 также возвращаются как 67
. Ни один из них не возвращается в качестве значения 64
, изначально заданного!
Использование setTimeout
для задержки между настройкой и повторной выборкой не имеет значения. Изменение фона страницы не имеет значения. Использование save()
и restore()
в контексте (за эта маловероятная статья) не имеет значения.
Ответы
Ответ 1
ImageData определяется как HTML5 как неускоренный, но в большинстве реализаций canvas используется предварительно умноженный буфер поддержки для ускорения компоновки и т.д. Это означает, что когда данные записываются, а затем считываются из резервного буфера, он может меняться.
Я бы предположил, что Chrome v8 поднял ошибку на предыдущем выпуске [un] премьютипирующего кода с webkit.org(он был сломан раньше, хотя я не помню никаких недавних событий, и это не объясняет окна только дисперсия)
[править]. Возможно, стоит проверить webkit в ночное время на окнах? поскольку реализация imagedata не имеет какой-либо конкретной платформы, которую она разделяла между всеми браузерами webkit и могла просто быть разбита на сборках на основе MSVC]
Ответ 2
Похож на вопрос округления для меня...
64/255 = 0.2509... (rounded down to give 0.25)
0.25 * 255 = 63.75 (rounded down to give 63)
== OR ==
64/255 = 0.2509... (rounded up to give 0.26)
0.26 * 255 = 66.3 (rounded up to give 67)
Помните, что 255 - максимальное значение, а не 256;)
EDIT: Конечно, это не объяснит, почему ведет себя альфа-канал...
Ответ 3
Спецификация HTML5 поощряет поставщиков браузеров к использованию того, что называется Premultiplied Alpha
. По существу это означает, что пиксели хранятся в 32-битных целых числах, где каждый канал содержит 8-битное значение цвета. По соображениям производительности премультиплексированная альфа-версия используется браузерами. Это означает, что он предварительно умножает значения цвета на основе альфа-значения.
Вот пример. У вас есть такой цвет, чтобы значения для RGB были 128
, 64
, 67
. Теперь, ради большей производительности, значения цвета будут предварительно умножены на значение альфа. Итак, если значение альфа 16
, все значения цвета будут умножаться на 16/256
(= 0.0625
). В этом случае результирующие значения для RGB становятся 8
, 4
, 4.1875
(округлены до 4
, потому что значения цвета пикселя не являются плавающими).
Проблема возникает, когда вы делаете именно то, что вы здесь делаете; задавая цветовые данные с определенным альфа-значением, а затем отбрасывая фактические значения цвета. Предыдущий синий цвет 4.1875
, который был округлен до 4
, станет 64
вместо 67
, когда вы вызываете getImageData()
.
Вот почему вы видите все это, и оно никогда не изменится, если базовая реализация в движке браузера не изменится, чтобы использовать цветовую систему, которая не страдает от этого.