- Класс ColorMatrix и фильтр ColorMatrixColorFilter
- Фильтр ColorMatrixColorFilter
- Инвертирование цветов
- Метод setScale()
- Метод setSaturation()
- Метод setRotate()
- Метод setConcat()
- Manipulating images and Drawables with Android’s ColorFilter
- Tinting, custom effects and reusability for visual Android app resources
- Where can this be used? 🤔
- Canvas
- Drawable
- ImageView
- Introducing our sample image 🖼️
- PorterDuffColorFilter 1️⃣
- LightingColorFilter 2️⃣
- ColorMatrixColorFilter 3️⃣
- Limitations and other options 🛠️
- Полный список
- ColorMatrixColorFilter
- LightingColorFilter
Класс ColorMatrix и фильтр ColorMatrixColorFilter
Фильтр ColorMatrixColorFilter
Фильтр для изменения цвета, использующий значения 4х5 матрицы.
Матрица может иметь следующий вид: 4 строки, в каждой строке по 5 значений:
Допустим, у нас есть некий цвет ARGB. Применим к нему фильтр. Фильтр возьмёт текущее значение цвета и, используя матрицу, вычислит новые значения. Например, новое значение красного (Rn) он посчитает так:
Т.е. значения исходного цвета (R,G,B,A) перемножаем на первые 4 значения (rR, rG, rB, rA) из первой строки матрицы и прибавляем пятое значение (rT) из этой же строки.
Самостоятельно считать ничего не нужно, фильтр сам всё рассчитает. Вы должны только эту матрицу предоставить.
Новое значение зелёного получается аналогично, используя исходные значения RGBA и вторую строку матрицы.
Синий и прозрачность – третья и четвёртая строки:
Создать объект ColorMatrix можно, используя конструктор без аргументов или перегруженные версии с аргументами.
Созданный объект применяется к объекту Paint через фильтр ColorMatrixColorFilter:
Конструктору без аргументов соответствует матрица:
Если применить данную матрицу к цвету, то все цвета останутся без изменений.
Для наглядности нарисуем несколько разноцветных квадратиков и один значок, и будем применять к ним фильтры (идея взята с сайта, ссылка на которую приведена в конце статьи).
Массив cmData содержит значения для матрицы, который мы передаём экземпляру класса ColorMatrix — переменной mColorMatrix.
Далее подготовленную матрицу передаём фильтру ColorMatrixColorFilter. Для применения фильтра используется метод setColorFilter().
В нашем примере фильтр применяется к кисти и мы можем наблюдать за изменениями цветов выводимых фигур.
Сейчас матрица выглядит следующим образом:
Если мы возьмём RGBA и применим матрицу, получим следующие результаты:
Как видите, новые значения равны исходным. Матрица составлена таким образом, что RGBA-значения любого цвета не изменяются. Запустив пример, вы увидите, что первый и второй ряд фигур идентичны.
Изучим, как происходят изменения цвета.
Был красный цвет c RGBA = (255,0,0,255). Применим фильтр:
Новые RGBA-значения получились такими (255,0,0,255) и они совпадают с исходным красным цветом. Поэтому красный квадрат остался без изменений.
Возьмём зелёный цвет. Его значение равно (0,255,0,255). Применяем фильтр:
Новые RGBA-значения зелёного = (0,0,0,255), а это чёрный цвет. Вот почему зелёный квадрат стал чёрным.
Такой же результат получается с синим цветом.
А белый цвет (255,255,255,255) после преобразования получит следующие значения:
Таким образом, белый цвет (255,255,255,255) трансформируется в красный цвет (255,0,0,255).
Применив данный фильтр, мы для всех цветов «обнулили» значения синего (B) и зелёного (G) составляющих и оставили только красную (R) составляющую. Это видно на значке.
Но тут важно понимать одну вещь. Мы не выкрасили всё в красный цвет. Мы полностью убрали зелёный и синий, а красный оставили в том значении, в каком он был. В красном квадрате значение красного было 255. Таким и осталось. В синем и зелёном квадратах значение красного было 0. Таким и осталось.
Если рассматривать значок, видно, что красный неоднородный, где-то светлее, где-то темнее. Т.е. изначально значок была нарисован разными оттенками, в которых были использованы различные RGB-комбинации. Мы в этих комбинациях убрали G и B, оставили только R. Где R был, например 50, остался 50. Где был 150 – остался 150. А G и B теперь везде равен 0.
Давайте настроим матрицу так, чтобы красный везде стал максимальным, независимо от первоначального значения. А синий и зелёный снова обнулим.
Мы убрали коэффициент 1 из первого числа первой строки. Теперь новое значение R уже не будет равно старому значение R, умноженному на 1. Теперь оно будет умножаться на 0, но последнее число первой строки равно 255. Оно будет прибавлено к нулю и мы получим полный красный цвет на замену первоначальным оттенкам красного.
Синий и зелёный также станут красными, так как G и B значения мы в них обнулим, а R будет равен 255, т.е. (255,0,0,255).
Теперь изменим матрицу так, чтобы обнулялся только синий компонент. Красный и зелёный компоненты останутся неизменными.
Получим следующий результат.
Убрав из синего цвета (0,0,255,255) синий компонент, мы получили чёрный цвет (0,0,0,255).
А убрав из белого цвета (255,255,255,255) синий компонент, мы получили жёлтый цвет (255,255,0,255).
Мы меняли компоненты цветов (RGB), теперь давайте попробуем поменять прозрачность (A). Напомню, что если A = 255, то цвет абсолютно непрозрачен. Если A = 0, то цвет совсем не виден.
Мы поставили коэффициент 0.3 для вычисления нового значения прозрачности (An = A * 0.3). Теперь все цвета станут прозрачными на 30% от текущего уровня.
Можно самостоятельно придумывать собственные комбинации для матрицы. Существуют уже готовые решения. Например, преобразование в чёрно-белый цвет.
Вот ещё одна комбинация, дающая серый цвет.
Как видите, можно играться настройками и добиваться нужных результатов.
Инвертирование цветов
Для инвертирования цветов используется следующая комбинация.
Аналогичный пример на Kotlin, добавьте на экран ImageView с картинкой и кнопку.
Метод setScale()
У ColorMatrix есть специальный метод setScale(), который позволяет указать на какие значения необходимо умножать RGBA значения цвета. В этом случае вам не нужно составлять свою матрицу, а просто указать в параметрах метода коэффициенты для R, G, B и A компонентов. Сама матрица будет иметь вид из первого примера, когда после её применения ничего не изменилось.
Вместо new ColorMatrix(cmData) используем пустой конструктор без указания матрицы new ColorMatrix(). А далее вызываем метод, в котором указываем нужные коэффициенты. Параметры (1, 0, 0, 1) соответствуют матрице из второго примера:
И мы видим тот же результат.
Если нужно сделать цвета полупрозрачными для нашего примера, то код был бы:
Напишем отдельный пример для загрузки изображения из Галереи и применим к нему метод setScale().
У компонента ImageView задан в качестве фона один из рисунков. При загрузке из Галереи новая картинка закроет фоновый рисунок. Но поменяв прозрачность, вы сможете увидеть фон снова.
Код для активности
Метод setSaturation()
Метод setSaturation() позволяет управлять насыщенностью цвета. Принимает на вход значения от 0 до 1. Если задать 0, то получим чёрно-белую картинку.
Загружаем из галереи рыжего бандита по кличке Рыжик и превращаем его в серого гангстера по кличке Васька.
Упрощённый пример на Kotlin. Добавьте на экран кнопку и ImageView с изображением кота.
Метод setRotate()
Пример применения метода setRotate().
Метод setConcat()
Метод setConcat() использует две матрицы. Пример можно найти здесь.
Источник
Manipulating images and Drawables with Android’s ColorFilter
Tinting, custom effects and reusability for visual Android app resources
Image and Drawable resources are an integral part of any Android app. Since day (i.e. API level) 1, the Android framework has provided a means of manipulating the colors of these assets on a per-pixel level, as they are drawn to screen, with the ColorFilter class.
The documentation for the base abstract class stipulates that “a color filter can be used with a Paint to modify the color of each pixel drawn with that paint”. This is a powerful tool that can be used for:
- Applying effects and adjustments to images
- Tinting of icons and logos
Ultimately this encourages the good practice of reusing resources for scenarios that require different color variations, resulting in reduced app size.
Note: ColorFilter is an abstract class that should never be used directly, and the constructor was deprecated in API level 26. Only the subclasses should be used, which we will discuss further down.
Where can this be used? 🤔
Before we explore the existing subclasses and their capabilities, we need to discuss where we can actually use ColorFilter .
Canvas
As hinted at in the documentation description, we can use ColorFilter when drawing to Canvas in a custom View . Specifically, a ColorFilter is set on a Paint which then affects everything that is drawn with it:
Drawable
A ColorFilter can be applied to a Drawable , which will affect how it is rendered in View s and layouts:
In addition to this, there exists a Drawable convenience function to apply a PorterDuffColorFilter (more on that later):
Lastly, it is important to note that a tint can be applied to a Drawable . This is separate and will be overridden if a ColorFilter is set via either one of the above functions.
ImageView
A ColorFilter can be applied to an ImageView , which will affect how its current image will be rendered:
As with Drawable , convenience functions exist for applying a PorterDuffColorFilter :
Introducing our sample image 🖼️
We are now going to dive into the three subclasses of ColorFilter ( PorterDuffColorFilter , LightingColorFilter and ColorMatrixColorFilter ). In order to demonstrate this, we will make use of a visual aid in the form of a sample image:
It has photographic detail while also having “shape” as a result of the transparent areas. This will allow all of the subclasses to be demonstrated.
PorterDuffColorFilter 1️⃣
We have already touched on this briefly. This is a color filter that accepts a single color that is applied to all source pixels along with a Porter-Duff composite mode. There are many modes that are suitable to different scenarios. Typically, this is used to apply a “blanket” color (eg. To tint an icon).
LightingColorFilter 2️⃣
This color filter can be used to simulate lighting effects on an image. The constructor accepts two parameters, the first to multiply the source color ( colorMultiply ) and the second to add to the source color ( colorAdd ).
While the source color alpha channel is not affected, the R, G and B channels are computed like so:
Note: The above is using Android KTX Color extension functions for accessing the red, blue and green channels (alpha is also available).
ColorMatrixColorFilter 3️⃣
This is arguably the most flexible (but also the most complex) color filter.
It is quite similar to LightingColorFilter , in that we can tweak each pixel’s color channels using values that are multiplicative and additive. However, a ColorMatrixColorFilter is constructed with a ColorMatrix , which is essentially a wrapper class for a 4×5 matrix. This gives us 20 values, used in a certain way, that allow us to transform colors using all of the existing channels, including alpha.
Before we dive into using matrices, let’s first take a look at some of the convenience functions offered by ColorMatrix :
Fear not if some of these color concepts do not make sense! The point here is that ColorMatrix offers a lot of flexibility, and these convenience functions are simply matrix operations under the hood.
We know that ColorMatrix wraps a 4×5 matrix. Given this matrix, how do we arrive at our resultant color channels? They are computed like so:
The 4 rows in fact represent the resultant R, G, B and A channels. For each channel, the 5 columns allow you to combine the existing R, G, B, and A channels and a wildcard value in a plethora of ways.
This provides us with a lot of possibilities. We can adjust the brightness/contrast of images, ignore some channels, invert an image or even create basic filters and effects.
Limitations and other options 🛠️
While ColorFilter is powerful and flexible, image manipulation is a vast field and it simply cannot cover everything.
For example, the current pixel value that is being passed through the filter would be handy to know, but is not available. Additionally, you are limited to the three subclasses that ColorFilter currently provides. It appears as if you cannot create a custom subclass, due to native code restrictions.
In instances like this, what other options do we have?
The graphics framework has other useful classes such as Shader and MaskFilter . We could turn to RenderScript , which offers Bitmap utilities that still keep us mostly within traditional Android graphics territory. There is also OpenGL, which is perhaps the most extreme power vs. complexity tradeoff, but opens up all of the possibilities of custom GLSL shaders.
Overall, ColorFilter is still a fantastic tool for working with app resources.
I hope this post has provided some insight into ColorFilter and the flexibility it provides when working with images and Drawable s. If you have any questions, thoughts or suggestions then I’d love to hear from you!
Источник
Полный список
— меняем цвет кисти с помощью ColorFilter
Сначала немного разъяснений по поводу цвета. Думаю, все уже в курсе, что в Android цвет мы обычно получаем путем сочетания трех цветов – красного (Red), зеленого (Green) и синего (Blue). Зовется это RGB. К этому набору можно еще добавить уровень прозрачности (Alpha). Получится ARGB.
Значения этих компонентов мы указываем от 0 до 255. Например, фон экрана, который я использую во всех уроках образуется так (80, 102, 204, 255). Соответственно значения: прозрачность – 80, красный – 102, зеленый – 204, синий – 255.
Если мы все RGB компоненты установим равными 0, то получим черный цвет. Если же все будут 255, то получим белый цвет.
Частенько значения указываются не в десятичной форме, а в шестнадцатеричной. Т.е. от 00 до FF, вместо 0 и 255. В этом случае обычно пишут не ARGB, а AARRGGBB. Например, красный цвет: FFFF0000. Если разбивать на AARRGGBB компоненты, получим AA = FF, RR = FF, GG = 00, BB = 00.
Существуют int-константы для указания цвета. Например Color.RED. Как получить такую int-константу из ARGB компонентов? Есть методы Color.rgb и Color.argb, куда вы передаете RGB или ARGB компоненты, а метод вернет вам int-значение цвета.
А метод Color.parseColor позволит получить вам int-значение из шестнадцатеричной формы: #RRGGBB или #AARRGGBB.
Наследники ColorFilter позволяют нам оказывать влияние на цвет, используемый при рисовании объектов.
ColorMatrixColorFilter
Начнем с ColorMatrixColorFilter. Этот вид фильтра влияет на цвет, используя значения 4х5 матрицы, которую мы ему выдадим.
Алгоритм немного нетривиальный, если вы незнакомы с матрицами из алгебры.
Мы задаем матрицу такого вида
rR, rG, rB, rA, rT
gR, gG, gB, gA, gT
bR, bG, bB, bA, bT
aR, aG, aB, aA, aT
4 строки, в каждой по 5 значений.
И пусть у нас есть некий цвет ARGB, к которому будем фильтр применять. Фильтр возьмет текущие значение цвета и из них, используя матрицу, вычислит новые. Например, новое значение красного (Rn) он посчитает так:
Rn = R * rR + G * rG + B * rB + A * rA + rT
Т.е. значения исходного цвета (R,G,B,A) перемножаем на первые 4 значения (rR, rG, rB, rA) из первой строки матрицы и прибавляем пятое значение (rT) из этой же строки.
Разумеется, нам ничего такого кодить не придется, фильтр сам все рассчитает. От нас требуется только матрицу ему предоставить. Здесь я просто показываю, как оно все внутри работает.
Новое значение зеленого (Gn) получается аналогично, используя исходные RGBA и вторую строку матрицы.
Gn = R * gR + G * gG + B * gB + A * gA + gT
Синий (Bn) и прозрачность (An) – третья и четвертая строки
Bn = R * bR + G * bG + B * bB + A * bA + bT
An = R * aR + G * aG + B * aB + A * aA + aT
Давайте рассмотрим это на примерах.
Project name: P1531_ColorFilter
Build Target: Android 2.3.3
Application name: ColorFilter
Package name: ru.startandroid.develop.p1531colorfilter
Create Activity: MainActivity
cmData – массив float, сюда пишем значения для матрицы.
Переменная cm – это и есть матрица – ColorMatrix. Ей мы даем массив cmData.
Эту матрицу мы указываем при создании фильтра filter. Теперь у фильтра есть матрица и он знает какие преобразования цвета ему необходимо будет произвести.
В методе drawObjects рисуем 4 квадрата – красный, зеленый, синий, белый, и выводим андроид-иконку. На этих объектах мы будет тестировать изменения цвета. Используем кисть paint.
В onDraw рисуем объекты метолом drawObjects, затем для кисти paint применяем фильтр методом setColorFilter и снова выводим объекты. Т.к. при рисовании объектов используется кисть paint, то применение фильтра к кисти повлияет на цвета рисуемых фигур.
Сейчас наша матрица выглядит следующим образом
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0
Если мы возьмем RGBA и применим матрицу, получим
Rn = R * 1 + G * 0 + B * 0 + A * 0 + 0 = R
Gn = R * 0 + G * 1 + B * 0 + A * 0 + 0 = G
Bn = R *0 + G * 0 + B * 1 + A * 0 + 0 = B
An = R * 0 + G * 0 + B * 0 + A * 1 + 0 = A
Новые значения равны исходным. Т.е. матрица настроена так, что RGBA значения любого цвета вообще не изменятся. Убедимся в этом. Запускаем приложение
Сверху (оригинал) и снизу (после применения фильтра) цвета одинаковы. Фильтр хоть и применился, но ничего не изменил в значениях цветов.
Теперь явно что-то поменялось. Давайте смотреть, что именно
Был красный цвет c RGBA = (255,0,0,255). Применим фильтр:
Rn = 255 * 1 + 0 * 0 + 0 * 0 + 255 * 0 + 0 = 255
Gn = 255 * 0 + 0 * 0 + 0 * 0 + 255 * 0 + 0 = 0
Bn = 255 * 0 + 0 * 0 + 0 * 0 + 255 * 0 + 0 = 0
An = 255 * 0 + 0 * 0 + 0 * 0 + 255 * 1 + 0 = 255
Новые RGBA значения получились такими (255,0,0,255). Т.е. для красного цвета ничего не поменялось. Это видно и на скриншоте, красный квадрат на месте.
А вот зеленый стал черным, смотрим почему
RGBA зеленого = (0,255,0,255). Применяем фильтр.
Rn = 0 * 1 + 255 * 0 + 0 * 0 + 255 * 0 + 0 = 0
Gn = 0 * 0 + 255 * 0 + 0 * 0 + 255 * 0 + 0 = 0
Bn = 0 * 0 + 255 * 0 + 0 * 0 + 255 * 0 + 0 = 0
An = 0 * 0 + 255 * 0 + 0 * 0 + 255 * 1 + 0 = 255
Новые RGBA значения зеленого = (0,0,0,255), а это черный. Тем же путем и синий (0,0,255,255) стал черным.
А белый (255,255,255,255) после преобразования
Rn = 255 * 1 + 255 * 0 + 255 * 0 + 255 * 0 + 0 = 255
Gn = 255 * 0 + 255 * 0 + 255 * 0 + 255 * 0 + 0 = 0
Bn = 255 * 0 + 255 * 0 + 255 * 0 + 255 * 0 + 0 = 0
An = 255 * 0 + 255 * 0 + 255 * 0 + 255 * 1 + 0 = 255
стал таким (255,0,0,255) – т.е. красным. Скриншот это подтверждает.
Т.е. применив фильтр, мы для всех цветов «обнулили» значения синего (B) и зеленого (G). Оставили только красную (R) составляющую. Это видно и на андроид-иконке.
Но тут важно понимать одну вещь. Мы не выкрасили все в красный цвет. Мы полностью убрали зеленый и синий, а красный оставили в том значении, в каком он был.
Т.е. в красном квадрате значение красного было 255. Таким и осталось.
В синем и зеленом квадратах значение красного было 0. Таким и осталось.
В андроид-иконке видно, что красный неоднородный, где-то светлее, где-то темнее. Т.е. изначально иконка была нарисована разными оттенками, в которых были использованы различные RGB комбинации. А мы в этих комбинациях убрали G и B, оставили только R. Т.е. где R был, например 50, остался 50. Где был 150 – остался 150. А G и B теперь везде = 0.
Давайте настроим матрицу так, чтобы красный везде стал максимальным. Независимо от первоначального значения. А синий и зеленый снова будем обнулять.
Мы убрали коэффициент 1 из первого числа первой строки. Т.е. теперь новое значение R уже не будет равно старое значение R умноженное на 1. Теперь оно будет умножаться на 0. Но последнее число первой строки = 255. Оно будет прибавлено к нулю и мы получим полный красный цвет на замену первоначальным оттенкам красного.
Синий и зеленый также станут красными. Т.к. G и B значения мы в них обнулим, а R будет равен 255, т.е. (255,0,0,255).
Теперь изменим матрицу так, чтобы обнулялся только синий. Красный и зеленый компоненты останутся неизменны.
Убрав из синего цвета (0,0,255,255) синюю компоненту мы получили черный (0,0,0,255).
А убрав из белого цвета (255,255,255,255) синюю компоненту мы получили желтый (255,255,0,255).
красный + зеленый + синий = белый
белый – синий = красный + зеленый = желтый.
Мы меняли компоненты цветов (RGB), теперь давайте попробуем поменять прозрачность (A). Напомню, что если A = 255, то цвет абсолютно непрозрачен. Если A = 0, то цвет совсем не виден.
Мы поставили коэффициент 0.3 для вычисления нового значения прозрачности. Т.е. An = A * 0.3. Т.е. все цвета станут прозрачными на 30% от текущего уровня.
Еще пара примеров матриц, которые я нашел в инете:
У матрицы также есть несколько методов, которые позволяют настраивать ее значения.
setScale – позволяет нам указать на какие значения необходимо умножать RGBA значения цвета.
Перепишем конструктор DrawView:
При создании ColorMatrix мы не использовали массив, и матрица создалась такой, какая была у нас в самом первом примере, т.е. ничего не меняющая. Но сейчас мы ее поднастроим.
Используем метод setScale. Его входящие параметры – это коэффициенты соответственно для R, G, B и A компонентов.
Т.е. значения 1, 1, 0, 0.5f настроят матрицу так, что
Т.е. красный и зеленый останутся прежними, синий обнулится, а прозрачность станет в 0.5 от старого значения.
Похоже на то, что было в четвертом примере, только прозрачность другая.
Также есть интересный метод setSaturation. Отвечает за насыщенность цветов. Принимает на вход значения от 0 до 1.
Если задать 0, то получим черно-белую картинку.
Если например 0.5f, то будет половина насыщенности
По умолчанию значение этого метода = 1, все цвета при этом будут такими, какими должны.
Есть еще метод setRotate. Это поворот относительно одного из RGB компонентов на указанное кол-во градусов. Я, честно говоря, его не понимаю и объяснить его смысл не могу. Возможно, он будет понятен тем, кто активно использует графические редакторы и работает с цветом.
LightingColorFilter
LightingColorFilter принимает на вход два цвета
mul – RGB-компоненты этого цвета будут умножены на соответствующие RGB-компоненты исходного цвета. Причем, компоненты mul надо рассматривать, как числа в диапазоне от 0 до 1. Т.е. если в mul компонент, например, R равен 255, то R-компонент исходного цвета останется без изменений (умножение на 1). Если в mul компонент R = 0, то R-компонент обнулится (умножение на 0). Если в mul компонент R = 127, то R-компонент уменьшится в два раза (умножение на 0.5)
add — RGB-компоненты этого цвета будут прибавлены к соответствующим RGB-компонентам исходного цвета, а результат урезан до 255.
В общем, должен сказать, что все эти игры с цветом для меня темный лес. Я не очень понимаю, какую практическую пользу можно вынести их этих механизмов. Но теперь мы имеем понятие как они работают и как ими пользоваться. Возможно, когда-нибудь это пригодится при работе с графикой.
Есть еще третий ColorFilter класс: PorterDuffColorFilter. О нем поговорим в одном из следующих уроков.
На следующем уроке:
— разбираем PorterDuff-режимы используя PorterDuffXfermode
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник