Как соединяются части конвейера рисования на холсте Android (2D)?
Я хотел бы лучше понять, как компоненты конвейера рисования Canvas в Android (2D) сочетаются друг с другом.
Например, как взаимодействуют XferMode, Shader, MaskFilter и ColorFilter? Справочные документы для этих классов довольно редки, и документы для Canvas и Paint на самом деле не добавляют никакого полезного объяснения.
Мне также не совсем понятно, как операции рисования имеют собственные цвета (например: drawBitmap
в отличие от "векторных" примитивов, таких как drawRect
) вписываются во все это - всегда ли они игнорируют Paint
цвет и использовать вместо этого использовать свой собственный цвет?
Я также был удивлен тем, что можно сделать что-то вроде этого:
Paint eraser = new Paint();
eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawOval(rectF, eraser);
Это стирает овал. До того, как я это заметил, моей мысленной моделью было то, что рисование на холсте (концептуально) рисует в отдельный "слой", а затем этот слой состоит из растрового изображения холста с использованием режима переноса Paint. Если бы это было так просто, то вышеприведенный код стер бы весь растровый рисунок (в пределах области отсечения), так как CLEAR всегда устанавливает цвет (и альфа) в 0 независимо от альфа-канала источника. Таким образом, это подразумевает, что существует дополнительная разновидность маскирования, чтобы ограничить стирание овалом.
Я нашел демоверсии API, но каждая демоверсия работает "в вакууме" и не показывает, как то, на чем она фокусируется (например, XferModes), взаимодействует с другими вещами (например, ColorFilters).
С достаточным количеством времени и усилий я мог бы эмпирически выяснить, как эти части связаны или расшифровывают источник, но я надеюсь, что кто-то еще уже разработал это, или еще лучше, что есть некоторая фактическая документация модели конвейера / чертежа, которая Я пропустил.
Этот вопрос был вдохновлен, увидев код в этом ответе на другой вопрос SO.
Обновить
Когда я искал какую-то документацию, мне пришло в голову, что, поскольку многие вещи, которые меня интересуют, кажутся довольно тонким шпоном поверх лыж, возможно, есть некоторая документация по лыжам, которая будет полезна. Лучшее, что я мог найти, это документация дляSkPaint
который говорит:
Существует 6 типов эффектов, которые можно назначить краске:
- SkPathEffect - изменение геометрии (пути) до того, как она сгенерирует альфа-маску (например, штрих)
- SkRasterizer - создание пользовательских слоев маски (например, теней)
- SkMaskFilter - изменение альфа-маски до ее раскрашивания и рисования (например, размытие, тиснение)
- SkShader - например, градиенты (линейный, радиальный, развертка), растровые шаблоны (зажим, повтор, зеркальное отображение)
- SkColorFilter - изменить исходный цвет (и) перед применением xfermode (например, цветовой матрицы)
- SkXfermode - например, режимы переноса портера, режимы наложения
Это не указано явно, но я предполагаю, что порядок эффектов здесь - это порядок, в котором они появляются в конвейере.
3 ответа
Как сказал Ромен Гай: "На Stackru трудно ответить на этот вопрос". На самом деле не было никакой полной документации, и полная документация была бы довольно большой, чтобы включить ее здесь.
В итоге я прочитал источник и провел кучу экспериментов. По пути я делал заметки и в итоге превратил их в документ, который вы можете увидеть здесь:
а также эта схема:
http://xenomachina.com/android-canvas-pipeline.png
Это "неофициально", очевидно, поэтому применяются обычные оговорки.
Исходя из вышеизложенного, здесь приведены ответы на некоторые из "подвопросов":
Мне также не совсем понятно, каким образом операции рисования имеют собственные цвета (например:
drawBitmap
в отличие от "векторных" примитивов, таких какdrawRect
) вписываются во все это - всегда ли они игнорируютPaint
цвет и использовать вместо этого использовать свой собственный цвет?
"Исходные цвета" происходят из Shader
, В drawBitmap
Shader
временно заменен BitmapShader
если неALPHA_8
Bitmap
используется. В других случаях, если нет Shader
указан Shader
который просто генерирует сплошной цвет, Paint
цвет, используется.
Я также был удивлен тем, что можно сделать что-то вроде этого:
Paint eraser = new Paint(); eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); canvas.drawOval(rectF, eraser);
Это стирает овал. До того, как я это заметил, моей мысленной моделью было то, что рисование на холсте (концептуально) рисует в отдельный "слой", а затем этот слой состоит из растрового изображения холста с использованием режима переноса Paint. Если бы это было так просто, то вышеприведенный код стер бы весь растровый рисунок (в пределах области отсечения), так как CLEAR всегда устанавливает цвет (и альфа) в 0 независимо от альфа-канала источника. Таким образом, это подразумевает, что существует дополнительная разновидность маскирования, чтобы ограничить стирание овалом.
XferMode
относится к "исходным цветам" (из Shader
) и "цвета назначения" (из Canvas
"s Bitmap
). Затем результат смешивается с пунктом назначения, используя маску, вычисленную в растеризации. Посмотрите фазу Передачи в вышеупомянутом документе для получения дополнительной информации.
На этот вопрос сложно ответить на Stackru. Однако прежде чем начать, обратите внимание, что фигуры (например, drawRect()) НЕ имеют внутреннего цвета. Информация о цвете всегда приходит от объекта Paint.
Это стирает овал. До того, как я это заметил, моей мысленной моделью было то, что рисование на холсте (концептуально) рисует в отдельный "слой", а затем этот слой состоит из растрового изображения холста с использованием режима переноса Paint. Если бы это было так просто, то вышеприведенный код стер бы весь растровый рисунок (в пределах области отсечения), так как CLEAR всегда устанавливает цвет (и альфа) в 0 независимо от альфа-канала источника. Таким образом, это подразумевает, что существует дополнительная разновидность маскирования, чтобы ограничить стирание овалом.
Ваша модель немного не в порядке. Овал не рисуется в отдельном слое (если вы не вызываете Canvas.saveLayer()), он рисуется непосредственно на фоновый рисунок Canvas. Режим переноса Paint применяется к каждому пикселю, нарисованному примитивом. В этом случае только результат растеризации овала влияет на растровое изображение. Особой маскировки не происходит, сам овал - маска.
Во всяком случае, вот упрощенный вид конвейера:
- Примитив (прямоугольник, овал, путь и т. Д.)
- PathEffect
- растеризации
- MaskFilter
- Цвет / Shader / ColorFilter
- Xfermode
(Я только что увидел ваше обновление и да, то, что вы нашли, описывает этапы конвейера по порядку.)
Конвейер становится немного более сложным при использовании слоев (Canvas.saveLayer()), так как конвейер удваивается. Сначала вы проходите через конвейер, чтобы визуализировать ваши примитивы в закадровом растровом изображении (слое), а затем закадровое растровое изображение применяется к Canvas, проходя через конвейер.
SkPathEffect - изменение геометрии (траектории) перед созданием альфа-маски (например, штриховки). SkRasterizer - создание пользовательских слоев маски (например, теней). SkMaskFilter - изменение альфа-маски до ее раскрашивания и рисования (например, размытия). SkShader - например, градиенты. (линейный, радиальный, развертка), растровые шаблоны (зажим, повтор, зеркальное отображение) SkColorFilter - изменить исходный цвет (цвета) перед применением xfermode (например, цветовой матрицы) SkXfermode - например, режимы переноса портье, режимы наложения