Кварц 2D реализует html5 canvas globalCompositeOperation - PullRequest
1 голос
/ 21 марта 2020

Я пытаюсь реализовать html5 canvas globalCompositeOperation, используя CGContextSetBlendMode и переводя html5 операторов холста (source-in, source-atop и др. c.) В его аналоги CGBlendMode (kCGBlendModeSourceIn, kCGBlendModeSourceAtop, * *.).

Вот ожидаемые результаты в соответствии со спецификациями:

globalCompositeOperation

С CGContextSetBlendMode я получаю это вместо:

enter image description here

Некоторые результаты неверны. Например, source-out (kCGBlendModeSourceOut в Quartz 2D) не обрезает синий прямоугольник.

Какая реализация верна, я не уверен. Но мой вопрос, есть ли обходной путь? После некоторой переделки я пришел к этому решению, которое предварительно обрабатывает пункт назначения перед применением операции:

  1. Обрезать все, кроме источника (то есть красный круг)
  2. Удалить пункт назначения (оставив только изображение в кружке)

Вот шаг предварительной обработки, который делает это (при условии, что исходный путь уже установлен):

auto save = CGContextCopyPath(ctx);
CGContextSaveGState(ctx);
CGContextAddRect(ctx, CGRectInfinite);
CGContextEOClip(ctx);
CGContextSetBlendMode(ctx, kCGBlendModeClear);
CGContextAddRect(ctx, CGRectInfinite);
CGContextFillPath(ctx);
CGContextRestoreGState(ctx);
CGContextAddPath(ctx, save);
CGPathRelease(save);

С обходным путем я получаю почти то, что Я хочу:

enter image description here

за исключением этого артефакта в исходном состоянии с некоторыми (сглаживающими?) Полосами в контуре круга.

Есть ли лучший способ? Я делаю это неправильно? Я что-то упустил?

Заранее большое спасибо, и, пожалуйста, простите за этот длинный sh вопрос.

Ответы [ 2 ]

0 голосов
/ 31 марта 2020

Хорошо, получается, что и Skia, и Quartz-2D ведут себя одинаково при компоновке векторных фигур (контуров). Таким образом, кажется, что html canvas здесь нечетный.

https://skia.org/user/api/SkBlendMode_Overview#Blend_Mode отмечает, что:

Рисовать геометрию с прозрачностью, используя композитирование Porter_Duff, не объедините прозрачные исходные пиксели, оставив место назначения вне геометрии нетронутым. Но: Рисование растрового изображения с прозрачностью с использованием композитинга Porter_Duff позволяет очистить место назначения.

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

При таком способе вы получите ожидаемые результаты:

enter image description here

0 голосов
/ 23 марта 2020

Режимы смешивания реализуются функцией, которая в основном принимает два цвета (каждый из одного источника) в качестве входных данных и дает вам полученный цвет в качестве выходных.

f(a,b) = c

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

c[1][1] = f(a[1][1], b[1][1])
c[1][2] = f(a[1][2], b[1][2])
...
c[n][m] = f(a[n][m], b[n][m])

где n и m - ширина и высота изображения в пикселях соответственно.

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

Относительно source-out - это один из операторов композитинга Портера / Даффа, который несколько похож на режимы наложения. Помимо двух цветов также нужны значения альфа-канала для этих цветов. Альфа-значение представляет, сколько каждого источника должно быть представлено в конечном результате, и это значение «участия» либо используется, либо отбрасывается в зависимости от оператора, который вы хотите использовать.

function sourceOut(aColor, bColor, aAlpha, bAlpha) {
  return bAlpha * (1 - aAlpha) * bColor;
}

См. JSBin с полным примером

Ссылка:

...