Смешивание двух изображений с помощью html5 canvas
Мне нужно нарисовать 2 варианта одного и того же изображения друг над другом, чьи альфа-значения составляют в целом 1.
Например:
- imageA
имеет альфа 0.4
, в то время как imageB
имеет альфа 0.6
,
- Желаемый результат - полностью непрозрачное изображение (альфа из 1.0
для каждого пикселя), который выглядит как смесь 60% imageB
и 40% смесь imageA
,
Однако я считаю, что холст html5 по умолчанию использует режим наложения, отличный от добавления альфа-смешанных изображений, поэтому две вещи, нарисованные с альфа-значениями ниже 1, не будут равны 1.
Я пробовал все разные режимы композитинга, а также режимы наложения Adobe, но ни один из них не дал желаемого результата.
Простой тестовый пример - нарисовать пурпурный прямоугольник дважды, оба с альфа 0.5
, Мое желание, чтобы результирующие пиксели были rgb(255, 0, 255)
, Тем не менее, результат немного прозрачен.
Кто-нибудь знает какой-нибудь способ достижения этого результата?
1 ответ
• Если вы посмотрите на спецификацию canvas'context2D о режиме смешивания по умолчанию ('source-over'):
http://dev.w3.org/fxtf/compositing-1/
Вы увидите формулу (я переименовал переменные для ясности):
colorOut = prevColor x prevAlpha + newColor x newAlpha x (1 - prevAlpha)
И для получающейся альфы:
αlphaOut = prevAlpha + newAlpha x (1 - prevAlpha)
Заметить, что,
а) неожиданно формула не является линейной (из-за: новый альфа х (1 - пред альфа) фактор).
б) точка имеет как меньшие значения r,g,b, так и уменьшенную альфа. Таким образом, при повторном использовании он будет выделять квадрат (0,6)==0,36 до конечного изображения. (... совершенно нелогично...)
• Так что же происходит с вашими 60% / 40% дро?
1) изображение А нарисовано с альфа = 60%. Формула выше дает:
colorOut = color_A * 0.6 ;
alphaOut = 0.6 ;
2) изображение B нарисовано с альфа = 40%
colorOut = color_A * 0.6 * 0.6 + color_B x 0.4 x (0.4);
alphaOut = 0.6 + 0.4 * 0.4;
Итоговая формула ==>
colorOut = 0.36 * color_A + 0.16 * color_B ;
alphaOut = 0.76 ;
Вы видите, что это совсем не та смесь 60/40, которую вы ожидали.
• Как исправить?
1) Вы можете сделать это вручную, используя getImageData / putImageData. Помните о проблеме перекрестного происхождения: если ваши изображения не приходят с вашего домена, их imageData будет пустым. Для выступлений сделайте ставку на коэффициент замедления 50+ по сравнению с холстом (= GPU), выполняющим работу. "Реальное время" (=60 кадров в секунду) может быть невозможно в зависимости от размера изображения / вычислительной мощности / браузера. Но у вас есть полный контроль.
2) Но если вы сейчас посмотрите на комбинированный режим "светлее", кажется, у нас есть наш парень:
http://dev.w3.org/fxtf/compositing-1/
Формула сейчас:
colorOut = prevColor x prevAlpha + newColor x newAlpha
И для получающейся альфы:
αlphaOut = prevAlpha + newAlpha
Вы видите, что это простой оператор "плюс", идеально подходящий для ваших требований.
Итак, решение:
сохранить ctx.
- установить более легкий композитный режим.
- нарисовать первое изображение на 60% альфа.
- нарисуйте второе изображение на 40% альфа.
восстановить ctx.
Последние слова: если вы хотите проверить правильность последней альфа (==255), используйте такую функцию:
function logAlpha(ctx,x,y) {
var imDt = ctx.getImageData(x,y,1,1).data;
if (!(imDt[0] || imDt[1] || imDt[2])) console.log('null point. CORS issue ?');
console.log(' point at (' +x+ ',' +y+ ') has an alpha of ' + imDt[3] ) ;
}