Привет!
В этот раз речь пойдёт о сугубо развлекательном эксперименте. Статья претендует исключительно на пятничное чтиво и ничего феноменального в ней нет. В ней повествуется об истории создания и разработке приложения McPaintio, которое может рисовать изображения в любом* контексте любой** программы рисования. Статья будет интересна людям, увлекающимся программированием ботов и графической анимацией. Ave, добро пожаловать!
Вместо вступления.
Давным-давно, в одной соцсети наткнулся я на интересное приложение. Это была коллаборативная (совместная многопользовательская) онлайн-рисовалка. Что-то в духе flockdraw.com и webcanvas.com, только в любой момент времени пользователей онлайн там было много… очень много… гораздо больше, чем сейчас. Просто сейчас такие игры уже не актуальны; редко бывает один-два человека зайдут, что-то намалюют и, непробыв и минуты, выйдут. Скукота. Такое развлечение в наши дни умерло как класс, а ведь раньше народ хавал подобные забавы на ура! Ах, эти золотые времена одномегабайтовых флеш-игрушек и гиф-анимаций.
Так вот, о чём это я?! Я об этой вот игре говорю. Сейчас, конечно, там практически никого не бывает. А году эдак в 2007 каждая комната буквально кишила людьми. Ну человек за сто точно было онлайн. И днём и ночью. И людей это развлечение реально развлекало, причём от мала до велика. Круто считалось нарисовать что-то реально красивое. Все видели сам процесс твоего рисования, могли выразить восторг, могли что-нибудь пририсовать, а могли и испортить. Из инструментов — смешно вспомнить — тогда был только карандаш (pencil) и рука (pan) для прокручивания бесконечного полотна. Даже линии прямой нельзя было нарисовать, всё — от руки. Ни тебе резинки, ни настройки цвета или толщины линии. Чата никакого не было, так что общались так: писали текст прямо на канве рисования, получалось жутко долго и очень коряво, ведь мышка — далеко не идеальный инструмент для рисования от руки. Отвечали другие игроки ниже под твоей надписью, ещё ниже — следующие, и холст превращался в сплошную стену текста. Благо стена бесконечная. Весело было.
Особенно крутые были те, у кого дома в распоряжении имелся графический планшет. Они писали от руки красиво, быстро, чистенько, да и рисунки у них были на порядок лучше. Никто не мог их переплюнуть. Лично у меня планшета не было, так что ваш покорный слуга первые недели три развлекался мышью. Но потом такой ход дел мне надоел. Захотелось мне быть не боярыней дворянскою, а владычицей морскою крутым художником и классно рисовать очень сложные рисунки.
Вначале я стал тренироваться «на кошках»: взял в распоряжение метаскриптовый язык AutoIT под Windows и написал в нём пару простеньких программ. Первая рисовала просто окружность, а вторая — прямую линию. Ну, как рисовала… программно ставила курсор мыши в определённое место, программно зажимала левую кнопку мыши и программно вела курсор по прямой или кривой линии. Приложение, в котором осуществлялись такие действия, «думало», что это пользователь двигает мышку и нажимает кнопки. На самом деле всё рисовал робот AutoIT (что-то типа cnc-станка). Браузер интерпретировал движения и нажатия в контексте игры как процесс рисования, и игра транслировала рисунок на сервер. Другие пользователи же не видят, что это не я рисую, а робот. Вот такая хитрость =) По сути — бот!
Таким образом, я научился рисовать идеальные окружности и прямые линии. Для школьников (а подваляющее большинство играющих было именно школьники), сидевших в игре, я был прямо скажем таки неким божеством. Я был божеством даже для людей с планшетом, так как даже на планшете, используя стилус, не нарисуешь идеальную окружность. И в моих руках теперь были инопланетные технологии рисования! На каждый нарисованный мною идеальный круг набрасывалась целая толпа рисующих, и превращала его то в рисованного смешарика, то в часы, то в рожицу, ну и в какой угодно другой рисунок, в основе которого подразумевается круг. Оставшиеся же разделились на два лагеря: те, кто завидовали, и те, кто восхищались. Завидовывшие из злости писали «читер», «чмо» и так далее, ну а восхищавшиеся перечёркивали высказывания завидующих и выражали восторг в письменной форме: «воу круто», «как у тебя это получается?», «это планшет?», «какая программа?» и так далее… попёрло!
Буквально за неделю я добился такой популярности, что ни один мой штрих в игре не оставался незамеченным. Но развлечения с окружностями и линиями надоели мне довольно быстро, так как душа перфекциониста всегда хочет чего-то большего и трубеут создания идеального инструмента. Хотелось такого, чтоб любой рисунок, любой файл, любую графику можно было перенести на холст. Тогда я сел за создание того, что сейчас называется McPaintio.
McPaintio — это программа, преобразующая изображение в набор мышиных команд, рисующих это самое изображение. Эдакий мета-рисовальщик. А раз любое изображение можно аналитически перерисовать, то есть возможность нарисовать что угодно в любой канве любого приложения! Представьте, что вы берёте рисунок с замысловатым орнаментом и немедленно рисуете сложнейший сплайн, описывающий этот орнамент, прямо в окне 3DMax! Не правда ли, это реально круто?!
Я реально заморочился и оснастил McPaintio клёвыми фишками. Например, если рисунок, который необходимо нарисовать, больше, чем канва для рисования, которую предоставляет приложение, и приложение поддерживает операцию pan, то McPaintio может нарисовать изображение поблочно, переключая в нужные моменты времени инструменты pan и pen и рисуя правильные фрагменты большого изображения в правильных местах холста! Далее. В McPaintio присутствуют аж три разных алгоритма рисования, предназначенных для выполнения разного рода трассировок изображения и перенесения его в другую канву. Эти алгоритмы в различной степени удовлетворяют классическому trade-off'у между скоростью рисования и точностью рисования. Я также оснастил McPaintio возможностью рисования изображений в оттенках серого несколькими различными алгоритмами. Например, если требуется нарисовать изображение в оттенках серого и канва предоставляет инструмент для выбора цвета, то можно настроить McPaintio так, чтобы он сам открывал палитру в приложении, выбирал нужный цвет, закрывал палитру и продолжал процесс рисования нужным цветом. McPaintio также умеет писать пользовательский текст. McPaintio имеет в арсенале гибкую настройку кнопок управления и таймингов, позволяющую подстроить процесс рисования под самые требовательные приложения.
В общем, поработал я на славу. Правда, приложение получилось с очень скомканным интерфейсом. Это результат его неправильного планирования: со временем дописывались всё новые и новые функции в рандомном порядке, все они отразились так или иначе в интерфейсе, получилась куча мала, а переделывать лень.
Пример 1.
Начнём с самого простого. Скачали приложение (проверили его размер) и открываем простой небольшой чёрно-белый bmp-файл. Необходимо транслировать из файла то, что нарисовано чёрным, в другую канву. Пока что для подопытной канвы выберем простой Microsoft Paint. Будем рисовать первым из представленных алгоритмов — «к ближайшей точке». Этот алгоритм движет перо от текущего пикселя в сторону ближайшего не закрашенного пикселя, закрашивая все промежуточные по пути. Если таких ближайших незакрашенных пикселей несколько, то выбирается случайный из них. Начинается рисование в случайном из ещё не закрашенных пикселе. В определённые моменты времени происходит перескок в новый случайный ещё не закрашенный пиксель (этот специально запрограммированный артефакт служит цели подстройки под интерфейс ещё первой программы колаборативного рисования, в которой ограничивалась длина максимальной линии, которую пользователь может нарисовать). Настраиваем кнопку старта трансляции на F10 (по умолчанию), а остановки — на F12. Сначала пробуем рисовать прямо в самом McPaintio (для наглядности). Ставим мышь примерно в верхний левый угол рисунка и жмём F10. Видим, как движется мышь. В заголовке программа показывает количество частей (разрывов линии, подъёмов левой кнопки мыши), которые она произвела. В этот момент программа полностью перехватила управление мышью, и даже если я буду пытаться ей шевелить, у меня ничего не выйдет, так как в уже следующий квант времени мышь будет программно перемещена в нужную программе точку. Фиолетовым видим уже закрашенные пиксели изображения. Происходит процесс закраски, который в любой момент можем остановить, нажав на F12. Можно заметить, что расстояние между ближайшими закрашенными пикселями не равно единице. Складывается впечатление, что программа пропускает некоторые пиксели. На самом деле это действительно так. И это не ошибка, а ещё один специально запрограммированный артефакт, который служит цели подстройки под интерфейс программы колаборативного рисования, в которой радиус пера составлял 2 пикселя. А раз так, то, закрасив пиксель (x, y), нет смысла закрашивать пиксели (x + 1, y) и (x, y + 1); их можно просто пропустить, так как на холсте они уже закрашены. Для игнорирования этого артефакта и принудительного процессинга абсолютно всех пикселей служит галочка «точно».
Когда показательный прогон показывает приемлемый результат, не дожидаясь окончания рисования останавливаем программу и переходим в Paint. Ещё раз: необходимо транслировать из файла то, что нарисовано чёрным (а не белым). Черное — это пиксели с интенсивностью 0. Белое — это пиксели с интенсивностью 255. 50% серое — это пиксели с интенсивностью 127. McPaintio позволяет выбрать те интенсивности пикселей, которые необходимо считать переносимыми (рисуемыми, обрабатываемыми). И не просто одну конкретную интенсивность (скажем, 0), а даже диапазон. Сделано это было для того, чтобы корректно отрисовывать даже те необработанные изображения, которые содержат цветовые флуктуации в районе нуля и чёрный цыет в них не всегда имеет интенсивность 0, а разбросан от 0 до, скажем, 8. Тогда, указав диапазон [0..8] программа корректно отрисует «чёрное». Для указания диапазона служат поля «расслаивать» — указываем чёрное как [0..8] и рисуем наш файл уже кистью в Paint.
Пример 2.
Тут я более наглядно показываю что означает выбор разных диапазонов для рисования. Рассмотрим простой градиент: слева — чёрное, справа — белое. Если выбрать диапазон [0..8], то, как я уже говорил и показывал, будет осуществляться рисование левой области градиента. Если же выбрать, скажем, [128..132], то будут отрисованы пиксели примерно из середины. И, наконец, если выбрать [242..255], то будут нарисованы самые правые участки градиента. Выбираем диапазон [0..32] и включаем рисование в Paint. Обратите внимание на то, насколько широкая часть полосы отрисовывается. Обратите внимание также и на то, что правый край полосы не ровный, а рваный. Это связано с тем, что градиент был подготовлен в Photoshop с использованием дизеринга. Посмотрите как перо плотно обтекает уже отрисованные участки.
Пример 3.
В этом примере я демонстрирую точный режим рисования, о котором говорил ранее. Обратите внимание на то, что теперь изображение обрабатывается попиксельно, без учёта закрашенности соседей.
Пример 4.
Теперь откроем новое изображение, являющееся контуром предыдущего. Хочу продемонстрировать рисование в «быстром» режиме, в котором отсутствуют дополнительные задержки в коде рисования. Они нужны для того, чтобы «медленные» программы типа Photoshop без ошибок хавали скрипт рисования. Замечено, что некоторые программы рисования отбрасывают часть оконных сообщений, посланных им. Итак, открываем файл, ставим галочку «быстро» и рисуем для большей наглядности в самом McPaintio. Обратите внимание на скорость рисования. Это максимальная скорость, с которой рисует McPaintio в этом режиме.
Пример 5.
Настало время продемонстрировать новый алгоритм рисования — «динамика линий». Этот алгоритм движет перо от текущего пикселя в сторону максимально отдалённого ещё не закрашенного пикселя, закрашивая все промежуточные по пути. Максимально отдалённый ещё не закрашенный пиксель выбирается не произвольным образом, а с некоторым ограничением: от него до текущего пикселя должна существовать прямая линия, проходящая а) только по обрабатываемым пикселям б) число уже закрашенных пикселей в линии не должно превышать 30% от общего числа пикселей линии. Такая техника максимально напоминает естественное рисование человеком от руки и была изобретена и введена взамен старого алгоритма «к ближайшей точке», который уж слишком явно разоблачал в программе колаборативного рисование факт рисования не человеком, а роботом. Сначала рисуем для большей наглядности в самом McPaintio, а потом кистью в Paint.
Пример 6.
Продемонстрирую теперь этот алгоритм на примере градиента. Буду рисовать два раза, в двух разных диапазонах. А раз так, то, чтобы итоговая картина получилась без разрывов, необходимо и в первом и во втором случае рисования поставить курсот мыши точно в определенную позицию. Назову это позицию точкой привязки. Отмечу так называемую точку привязки в Paint'е красным крестиком. Сначала открываю градиент и выбираю диапазон [0..8]. Это нарисует самый левый фрагмент градиента. Снимаю галку «быстро» и устанавливаю мышь примерно в точку привязки. Посмотрите как идёт процесс рисования, а точнее — закрашивания большого прямоугольника. Очень напоминает движение кисти человека. После окончания рисования выбираю диапазон, следующий ровно за текущий — [9..12]. Для наглядности выбираю в Paint'е другой цвет. Опять помещаю указатель в точку привязки и рисую. Обратите внимание, что новые линии расположились строго правее предыдущего рисунка, причём впритык. В этом и есть суть выбора рисуемого диапазона в этом режиме.
Пример 7.
Продемонстрирую теперь два таких параметра, как «пауза подъема мыши» и «время линии». McPaintio позволяет выставлять время, которое нужно рисующей программе на то, чтобы воспринять линию и отрисовать её. Этот параметр называется «пауза подъёма мыши» и выражается в милисекундах. Он фактически говорит McPaintio: «после проведения очередной линии отпусти левую кнопку мыши и подожди n милисекунд», где n — задаваемый параметр. Делов том, что такие программы, как, например, Paint не пропускают, не дропают и не отбрасывают оконные сообщения, которые были им посланы. Paint — это сверхпростая и сверхскоростная программа рисования, поэтому в ней такую паузу можно ставить хоть 0. Но есть грузовые и тормознутые программы, например, Photoshop, которые очень медленно рисуют линии (особенно гауссовыми мягкими кистями), которые ведут историю, которые могут дропать оконные сообщения, посланные им. Для таких программ «паузу подъема мыши» надо ставить даже 100 милисекунд, чтобы они успели сообразить что надо делать и сделаи это.
Аналогичным целям служит параметр «время линии». Он говорит сам за себя: это то время, за которое рисуется одна прямая линия, в милисекундах. Например, для Paint'а можно ставить этот параметр в 10 или даже 5, тогда как для Photoshop'а он должен равняться 50 или даже 100. Иначе он просто не успеет отрисовать эту линию, как уже придут оконные сообщения о рисовании следущей линии и в результате Photoshop порвёт текущую линию, дропнув часть оконных сообщений.
В видео я вначале выставляю эти параметры в 0 и 1 соответственно. Обратите внимание на то, как быстро рисуется изображение. Потом для демонстрации я ставлю эти параметры в 200 200. Теперь рисование происходит о-о-очень медленно. Наконец, в конце я ставлю средние значения для большинства программ — 25 25. Теперь всё в порядке.
Пример 8.
Теперь поиграем с текстом, ведь McPaintio может писать и его! Эта функция воодилась для реализации возможности ненапряжного письма для той самой первой программы коллаборативного рисования. В McPaintio есть специальной поле «текст», куда можно вписать текст. Для начала выбирем размер шрифта 40 и нажмём кнопку «написать». Программа сгенерировала изображение написанного текста, и теперь на общих основаниях будет его отрисовывать. Включим точный режим и посмотрим что из этого выйдет. Получилось весьма средне. Теперь поменяем размер шрифта на 80 и напишем то же самое жирной кистью. Так гораздо лучше!
Пример 9.
Теперь продемонстрирую один из интереснейших режимов рисования — рисование по блокам. Забегая вперёд скажу, что этот режим вводился ради рисования огромных изображений на бесконечном холсте через очень маленькой «отверстие» (окно) для рисования. Для всё той же первой коллаборативной программы рисования. Для подопытных экспериментов сейчас возьмём Photoshop. Итак, есть небольшое окно программы (фактически не больше 512 х 512 пикселей), через которое мы можем иметь доступ к большому холсту (фактически 2000 х 2000 пикселей). Необходимо отрисовать изображение, гораздо большее, чем просмотровое окно. Открываем в McPaintio огромное изображение, под стать холсту. Создаём в Photoshop новый документ размером 2000 х 2000 пикселей. Видим холст целиком только в 30% виде, в 100% же виде видим лишь его малую часть. Далее необходимо показать McPaintio те точки на экране, в которых находятся кнопки включения инструмента панорамирования (pan) и инструмента кисти (brush). Однажды указав их, теперь McPaintio сможет рисовать изображения поблочно, сам прокручивая холст в нужные позиции. Для обучения вначале поставим галочку «блоки» и укажем размер одного блока, как, скажем, 256 х 256 пикселей. То есть McPaintio будет рисовать избражение 1025 х 1024 пикселя через просмотровое окно размером всего 256 х 256 пикселей. Теперь необходимо стать курсором мыши на кнопку pan Photoshop'а и McPaintio запомнит её (чекбокс возле звуздочки станет отмеченным). Аналогично необходимо сделать для кнопки brush. Поставим правильные тайминги для Photoshop'а 20 и 20. Начнём рисование и полюбуемся поблочной отрисовкой. Сначала отрисовывается верхний левый угол изображения. После окончания отрисовки McPaintio переходит в режим панорамирования и подтаскивает холст так, что новая порция данных будет отрисована впритык к старой. И так далее для всех блоков. В конце видео можно полюбоваться готовым изображением.
Пример 10.
It's online time! Теперь сделаем то же самое, только в той самой коллаборативной рисовалке. Поставим размер блока на этот раз в 512 х 480 и немного переобучим McPaintio кнопкам pan и pen. Любуемся результатом. Обратите внимание, что ни в одной комнате ни души! Это свидетельствует о том, что спустя 7 лет люди понаходили себе более интересные игры, чем коллаборативное рисование.
Пример 11.
Теперь перейдём к серьезному примеру! Необходимо отрисовать полноценное изображение в оттенках серого, а не чёрно-белое, как это было до сих пор. В качестве канвы для рисования будем использовать стену граффити ВКонтакте. Открываем изображение и включаем режим рисования «отложенная техника». Этот алгоритм на самом деле — это то же самое, что и «динамика линий», но за одним исключением. В этом алгоритме в начале McPaintio рисует наиболее длинные серии линий, а в конце — наиболее короткие, тогда как в «динамике линий» это зависит чисто от рандома. То есть «отложенная техника» — это отсортированная по длине серии линий «динамика линий». Вот и всё.
Итак, открыли изображение. Но ранее мы видели, что McPaintio умеет рисовать только один диапазон. Да ещё и только одним цветом, выбранным в рисующей программе. Неужели, чтобы отрисовать изображение в оттенках серого нам придётся вручную выставлять диапазон [0..0], выставлять в рисующей программе нужный цвет кисти (чёрный), отрисовывать всё чёрное, потом ставить диапазон [1..1], снова выставлять в рисующей программе нужный цвет кисти, рисовать, и так далее до 255?!?! На самом деле, конечно же нет! Во-первых, никто не требует такой подиапазонной точности. То есть, вместо набора диапазонов {[0..0], [1..1], [2..2],…, [255..255]} можно использовать диапазон с шагом не один, а, скажем, восемь: {[0..8], [9..16], [17..24],…, [248..255]}. Или даже 16. Или даже 32. Меньше будет возни. Правда, и финальное изображение будет подвергаться эффекту постеризации, но в разумных пределах это воплне допустимо. Например, с шагом в 8 вы даже не заметите разницы. Ну и, наконец, во-вторых: программу McPaintio можно обучить саму открывать цветовую палитру (!), саму выбирать нужный цвет (!!), саму закрывать палитру (!!!), и саму переключать диапазоны (!!!!) с определённым шагом.
Любопытный зритель, наверное, уже заметил эти странные «+0» возле диапазонов расслаивания. Это и есть настройка шага. Это работает так: устанавливаем, как обычно, диапазон расслаивания, в данном случае [0..32]. А +0 +0 на этот раз заменяем на +32 +32. Это значит, что шаг нижнего и шаг верхнего края диапазонов после каждой отрисовки будет меняться на +32 интенсивности. То есть сначала программа будет отрисовывать диапазон [0..32], потом [32..64] и так далее до 255. Шаг разумно назначать такой, чтобы диапазоны не перекрывались, а шли стык в стык. Далее настраиваем тайминги 10 10 (ввиду того, что граффити ВКонтакте довольно таки резво работают) и ставим галочку «градиентно»! Это и включает возможность автоматического шага и выбора цвета из палитры. Обучение автовыбору цвета заключается в указывании McPaintio 4 точек на экране: где находится кнопка «открыть палитру» в рисующем приложении, где в открытой палите находится белый цвет, где в открытой палите находится чёрный цвет, где находится кнопка «закрыть палитру» в рисующем приложении. Указав эти 4 кнопки McPaintio уже сам сможет выбирать цвет текущего диапазона. Замечу, что в некоторых простеньких программах ползунок выбора интенсивности (чёрный — белый) уже вынесен в интерфейс, так что в этом случае нужно указать только 2 точки: «в какой пиксель экрана надо нажать, чтобы выбрать белый цвет» и «в какой пиксель экрана надо нажать, чтобы выбрать чёрный цвет». McPaintio показывает галочками «o», «w», «b» и «x» то, что он запомнил расположение открытия, выбора белого, выбора чёрного и закрытие соответственно. Итак, начинаем рисовать и видим, как робот автоматически выбирает нужный цвет и производит рисование.
Пример 12.
Теперь проделаем такой же финт, но в Photoshop'е. Тут уже нужно открывать и закрывать палитру. Научим McPaintio делать это. Настроим тайминги 200 10 и полюбуемся прекрасным процессом рисования.
Пример 13.
It's online time again, и на этот раз мы посетим FlockDraw! Нарисуем логотип «AM» прямо на холсте. Для этого выберем знакомую технику и настроим тайминги. Дальше всё просто.
Пример 14.
Ну и на последок предлагаю посмотреть процесс точного рисования довольно большой картины на заказ.
Послесловие
Пожалуйста, напишите в комментариях о вашем опыте подобной графической автоматизиации. Очень интересно.
Спасибо за внимание!
Автор: andreymironov