Знакомство с рисованием с помощью WinApi. Рисование дома, дерева, Солнца и озера

Это аналог темы graphics.h Рисование дома, дерева, Солнца и озера. Рисунок будет приблизительно тот же:

Дом, озеро, солнце, WinApi, C++

Всё в этой публикации раскрыто, конечно, не будет, но, возможно, эта статья поможет сделать кому-нибудь свой старт в компьютерном рисовании. Несколько раз ко мне читатели сайта обращались по теме графики. Бывает, что на форумах ищут информацию по работе с graphics.h, но graphics.h — это прошлое. Поскольку graphics.h иногда использовать просто не выйдет, поскольку современные компиляторы не поддерживают, эта статья — небольшой бонус нуждающимся.
К сожалению, в C++ встроенных стредств для работы с графикой нет. Для программного рисования используются графические библиотеки. Впрочем, это касается любой области: для звука звуковые, для сети сетевые и т. д. Каждая бибилиотека по-своему удобна. Да и к тому же использование библиотек позволяет не создавать велосипеды и использовать проверенный людьми код. Изначально в C++ была встроена библиотека graphics.h (замечательный инструмент своего времени, который всё никак не могут убить окончательно). Время прошло. Компиляторы, использующие такую библиотеку, устарели. Появились другие средства работы с графикой: OpenGL, sfml, DirectX, WinApi… В зависимости от задачи и средств для решения выбор инструмента может варьироваться:
Одно дело, когда достаточно на чём угодно написать, другое, когда используется конкретная среда разработки. Например, в Windows в VisualStudio для тех, кому хоть на чём писать, может и WinAPi подойти. Если какая-то библиотека не работает с нужным компилятором, как graphics.h c Visual Studio, то вместо того, чтобы заставить бибилиотеку работать, иногда лучше подумать, что можно в Visual Studio использовать. Так вы потратите меньше времени. Также другое дело, если нужно написать игру. В работе игр важны плавность анимации, скорость анимации, качественное кадрирование… В таком случае WinApi может не подойти, может быть лучше задействовать уже готовые для программирования графики библиотеки: SFML, OpenGL, DirectX и подобные. А ещё у кого-то может быть Linux установлен, а не Windows, в таком случае, опять же, WinApi уже как-то не подходит. WinApi — это для Windows. (Windows application programming interfaces)
Отмечу, что для компьютерного рисования используются одни и те же алгоритмы, которые никак не зависят от того WinAPi там или DirectX какой. Если надо вращать какую-то фигуру в пространстве, то в любом случае вращать её можно одинаковым образом самописно или используя любую графическую библиотеку.
Так как сегодня Windows весьма распространённая операционная система, то с помощью WinApi я покажу пример рисования дома, дерева, озера, солнца.
Перед тем, как я начну кодить, нужно усвоить следующее:

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

  • Как и setfillstyle() в MS-DOS, кисть закрашивает какую-то область в какой-то цвет. В зависимости от выбора кисти, закрашивание может происходить в полосочку, в клеточку, по диагонали… Есть два способа объявить кисть. Первый — задать сплошную заливку. Второй — указать стиль. Для таких вещей существуют функции: CreateSolidBrush() и CreateHatchBrush()
Для рисования фона будем использовать стилевую кисть.

Сейчас не будет ничего нарисовано. Это только выбор инструмента для рисования. Сейчас всё готово, чтобы нарисовать какую-то закрашенную фигуру. Так как выбрана только кисть, то будет нарисована та часть фигуры, которая закрашивается. Чтобы нарисовать фон, нужно закрасить всё окно. Чтобы закрасить всё окно, имеет смысл узнать ширину окна и высоту окна.

  • Чтобы нарисовать прямоугольник с помощью WinApi, используется функция Rectangle(…)
Вообще, когда рисуете с помощью WinApi, во многих функциях нужно указать прицепленное окно. У нас консольное окно прицеплено в переменную dc, вот такая переменная "Прицепленное окно" и уходит первым параметром вовнутрь функции, рисующей какой-то примитивный объект. Дальше всё зависит от того, какой объект выбран. Так, для прямоугольника нужно указать четыре координаты: верхний левый и нижний правый углы (координаты начала и конца диагонали).
RGB — это палитра цветов, где R — красный, G — зелёный, B — синий. Для каждого из этих трёх цветов выбирается одно из 256-и значений [0..255], что влияет на яркость и цвет. В моём коде красному соответствует ноль, зеленому 255, синему ноль — значит в сумме это не красный и не синий, но зелёный. Можно комбинировать числовыми значениями. Но я не буду об этом рассказывать. Сосредоточимся не на подборе цвета, а на рисовании запланированного рисунка.

Вот что получилось:

WinApi, C++, рисование

  • Для каждого CreateObject обязательно нужно вызывать соответствующий DeleteObject, чтобы память не утекала.

Какой-то одинаковый стиль может потребоваться для закрашивания/окраски разных фигур в разное время в рамках выполнения одной программы, тогда такие инструменты рисования лучше удалять не сразу, а отложить на потом. Дело в том, что CreateObject выделяет место в памяти, а операции выделения памяти и очистки памяти — это дорогостоящее: выполняются они долго. В нашем случае пока только ознакамливение, поэтому этот совет проигнорируем, но забывать не станем.

Получилось вот это:

WinApi, C++, рисование

  • Для рисования окружности используют функцию Ellipse(…)
Приступим к рисованию самого сложного элемента: к рисованию дерева. Для того, чтобы нарисовать несколько копий деревьев, да и для того, чтобы больше концентрировать внимание на нужном, буду использовать класс, объект которого станет рисунком.
Рисование это долгое, нудное и неприятное. Я использую относительное позиционирование, где рисунок начинается от точки, но вы можете упростить себе это дело и подгонять прямо числами. Это проще, но тоже очень нудно и долго. Координаты можно вписывать сразу числами, но лучше позиционироваться относительно какой-то точки: это поможет при создании копий. В конце статьи будут созданы копии рисунков.

Получилось вот так:

WinApi, C++, рисование

Если вы попробуете нарисовать что-то своё, то непременно возникнут затруднения в попадании на нужную точку. Здесь, например, очень сложно было попасть листиком на ветку дерева. Но я поделюсь маленьким секретом облегчения такой задачи. Чтобы попасть в точку, имеет смысл нарисовать какую-то точку и гонять её по экрану, а после удачного попадания использовать её координаты. Ориентироваться в осях X и Y всё равно придётся. На экране ось X уходит слева направо, а ось Y сверху вниз (не как в школе учили). Лучше делать не совсем обычную точку, а жирную большую. Для этого подходит эллипс. Координаты эллипсу задаются так X1 + dx1,Y1 + dy1, X2 + R + dx2, Y2 + R + dy2. Но, догадываюсь, что кому-то это ничего не скажет. Пример для экспериментов (игра со значениями может помочь выбирать координаты быстрее, если двигать точку и стараться попасть в нужное место):

Суть проста, но понять может быть не совсем просто. Суть: видеть точку на экране и сдвигать её до тех пор, пока она не попадёт в нужное место. После этого можно провести расчёт координат (в X + dx1 подставить значения X и dx1 и расчитать для остальной части так же).
Перейдём к рисунку. Осталась последняя часть. Надо нарисовать дом. Дом состоит из многоугольников: треугольник, параллелограмм, прямоугольники.

  • Для рисования закрашенного многоугольника использованием WinApi можно использовать функцию Poligon(…).

Получилось так:

WinApi, C++, рисование

Здесь немного уныло смотрятся один дом и одно дерево. Благодаря подходу, ориентированному на работу с классами, можно легко построить ещё несколько домов и посадить несколько деревьев:

В итоге получился следующий рисунок:

WinApi, C++, рисование

5 комментариев на «“Знакомство с рисованием с помощью WinApi. Рисование дома, дерева, Солнца и озера”»

  1. Аноним:

    Отлично!

     

  2. Аноним:

    все доступно и понятно, спасибо

     

  3. Марат:

    Как исправить ошибку undefined reference to `__imp_CreateSolidBrush’ ?

    • Нужно добавить в командной строке линкера : -lgdi32

      Например, в CodeBloks: Settings —> Compiler -> Linker Settings -> Other Linker Options
      Вписать -lgdi32

      Суть — достучаться до командной строки линкера. В разных IDE дизайн различен, поэтому угадывать, как именно должно быть у кого бы то ни было по ту сторону монитора, мне не представляется возможным.

  4. Алексей:

    Это лучшая статья по данной теме! Полностью раскрыта суть, спасибо!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Поиск

 
     

Случайная книга в электронном формате

https://www.litres.ru/igor-savchuk/otyavlennyy-programmist-layfhaking-iz-pervyh-ruk/?lfrom=15589587
Яндекс.Метрика