Как сделать прозрачную кисть

   Почти во всех программах, предназначенных для рисования, используются кисти с прозрачным цветом, да ещё с нечёткими, размытыми краями. Разумеется, делаются такие кисти программистами, и сугубо компьютерными методами. Однако я не смог найти в интернете описания подходящего для меня алгоритма. В интернете легко находится нечто другое - как в Фотошопе настроить кисть. А зачем мне это? Я делаю свою программу. Хотя, какие бывают кисти, и что они могут делать, знать конечно интересно. Но настроить кисть в готовой программе, это одно, а вопрос о том, как самому достичь такого эффекта, это совсем другая песня.

  Дело в том, что я программирую в Бейсике, а точнее - в Визуал бейсике 6, а возможности этой среды по части рисования очень скудны.
  Точка - то есть круг заданного диаметра с центром X,Y, легко создаётся оператором бейсика Pset(X,Y), но круг этот во-первых непрозрачен, а во-вторых  края у него чёткие, а хотелось бы иметь края размытые, такие края, через которые просвечивал бы фон.

   Диаметр круга, которым изображается точка, задаётся свойством  DrawWidth, и можно было бы положить DrawWidth=1  и затем заполнять круг малюсенькими точками, размером в пиксель. Для каждой точки можно было бы функцией  Point(X,Y)  определять цвет фона и затем смешивать этот цвет в определённой пропорции с тем цветом, который мы выбрали, и которым мы покрываем наш рисунок.
  Прозрачность кисти должна увеличиваться к краям, следовательно, для каждой точки нужно определять радиус - её расстояние до центра круга, и устанавливать пропорцию смешения цветов в соответствии с этим радиусом.
   Вы представляете, что это за работа?
   Пока мы будем проделывать её для каждой точки, много времени пройдёт, что вовсе не безразлично для пользователя , даже если компьютер у него весьма быстродействующий. Наш процесс рисования полупрозрачного круга будет постоянно зависать.

   И как быть?
   Решение состоит в том, чтобы те вычисления, которые должны проводиться при нанесении точки и которые могут быть проведены заранее, проводились заранее.
   Заранее можно очертить круг, заранее можно определить координаты каждой точки в нём, заранее можно вычислить для каждой точки её радиус. Эти вещи делаются в самом начале - при запуске программы.
   Немного чаще, при каждом нажиме на левую кнопку мыши с целью рисовать,  считывается установленный  радиус круга,  и для радиусов меньших и несколько больших этого, вычисляется и запоминается степень прозрачности кроющего цвета.
   А дальше всё идёт как по маслу, то есть быстро. 
   При каждом сдвиге по рисунку на один пиксель происходит обегание по координатам точек, входящих в заполняемую круговую область, и ставится точка, размером в один пиксель, с цветом, получаемым из пропорции между цветом точки фона и кроющим эту точку цветом. Коэффициент этой пропорции, как уже говорилось, зависит от радиуса.

     НЕКОТОРЫЕ ТЕХНИЧЕСКИЕ ПОДРОДНОСТИ

   Итак, для того, чтобы процесс покрывания рисунка полупрозрачной краской с помощью кисти у нас был успешным и быстрым, нам надо позаботиться о том, чтобы нужные для этого процесса данные были заранее занесены в некие массивы.
   Что это за массивы?
   У меня в программе это массивы RXY(1400, 2) As Integer, R20(21) As Integer, RK20(21) As Single

   Первый массив содержит радиусы и относительные (относительно центра) координаты точек, входящих в круг с радиусом 20 пикселей. Кисти большего радиуса я не использую.
   Второй массив содержит ссылки на первый массив, это номера конечных точек, находящихся в круге радиуса 1, 2, 3 и т.д. до 20. В третьем массиве находятся коэффициенты прозрачности для каждого радиуса.

   Результат тестовой проверки заполнения последних двух массивов можно видеть на иллюстрации к статье.
   Чёрный кружок, это тот самый круг радиусом 20 пикселей, о котором идёт речь. Слева от него, сверху 1327 - число пикселей в этом кружке, 20 - радиус кружка, 20 -1 это координаты последнего тестируемого пикселя.
   Чтобы заполнить массив RXY(1400, 2) координатами 1327 точек пришлось сооружать отдельную программу. Алгоритм работы этой программы поясняется на рисунке схемой.
   Сперва очерчивается окружность радиусом 1 пиксель, и в массив зачисляются пиксели лежащие на ней. Таких пикселей, плюс центральный, оказывается 9.
   Затем, с шагом по углу dU=.5R очерчивается окружность следующего радиуса. В круге радиуса R=2 оказывается уже 21 точка. Ну и так далее, до двадцати. В двух столбцах на иллюстрации слева показаны радиусы окружностей, и число пикселей, заключённых в них.
   Третий столбец - это коэффициент изменения цвета, рассчитанный в качестве теста для внешнего радиуса R=20 и для изменения цвета на 50% в центре круга. Видно, что имеются две зоны - сильно окрашиваемый центр, и незаметно окрашиваемая периферия. Переход между зонами приходится на R=10, именно таков видимый радиус окрашиваемого круга.
   Для моделирования такого рода плавного перехода очень удобным оказалось использовать функцию косинус.

    Как практически проявляют себя прозрачные кисти разного диаметра можно видеть на рисунке. Плотность кисти в центре удобно регулировать, её можно увеличивать   или уменьшать. Специальная опция позволяет задать цвет фона, к которому краска с кисти не пристаёт. Окружив таким фоном окрашиваемый предмет, можно не беспокоиться о том, что краска с кисти попадёт ещё куда-нибудь кроме этого предмета. Пример такого окрашивания показан на иллюстрации снизу.
   Обратите внимание на то, что прозрачный красный цвет красиво ложится на поверхность оранжевого оттенка, а вот по отношению к зелёному цвету прозрачный красный цвет - антагонист. При закраске зелёного получаются серые оттенки.
  И наконец, для чего всё это делается? Можно раскрасить лимончик, и не испортить при этом окружающую лимончик белую поверхность своими широкими кистями.
_________
6.07.2019

Если вы думаете, что всё написанное мною, сложно, то посмотрите сюда - https://pythonworld.ru/gui/pyqt5-painting.html
Я умер бы на старте, не подходя к финишу, если бы следовал рекомендациям этой статьи. Удивляюсь программистам, как они любят усложнять свою работу! И всё почему? Потому что лелеют любимый ими принцип объектно ориентированного программирования. Используя библиотеки с уже готовыми решениями, они попросту ленятся искать решения на более низком уровне, и ещё не факт, что имеющиеся в библиотеке методы будут быстродействующими. Мне же Бейсик, с его оператором GoTo и с более простым пониманием функционирования готового кода, в сто раз милее. )))


Рецензии