Режим рисования (Drawing Mode).
На практике рисование на контексте устройства рассматривается как сложный процесс переноса подготовленного вами изображения (например, образа рисуемой линии) на уже существующее в этом месте изображение. В процессе переноса выполняется некоторая операция, в результате которой получается результирующее изображение. Обычно такая операция — простое копирование нового изображения на старое. Однако вы можете задать необходимую операцию, которую надо выполнять в процессе переноса изображения.
Направление рисования дуг эллипсов (Arc Direction).
Влияет только на результат рисования дуги эллипса (функции Arc, Pie и Chord). По умолчанию дуги рисуются против хода часовой стрелки, но вы можете изменить это направление. Пользоваться этой возможностью, вообще говоря, не рекомендуется, так как применение этой функции ограничено — в Win32 API в расширенном режиме задания координат (см. SetGraphicsMode, GM_ADVANCED) дуги рисуются всегда против хода часовой стрелки и изменить это направление нельзя.
Собственно для рисования прямых линий необходимо всего две функции:
DWORD MoveTo (hDC, nX, nY); 0
BOOL MoveToEx (hDC, nX, nY, lpPoint);
BOOL LineTo (hDC, nX, nY);
Функция MoveToEx перемещает текущую точку пера в указанное место, не выполняя рисования. Последующий вызов функции LineTo осуществит рисование от текущей точки до указанной конечной отрезка. Нарисованный отрезок линии не включает в себя последнюю точку! Это сделано для удобства рисования ломанных линий из нескольких отрезков — в этом случае любая точка ломанной будет нарисована только один раз. Если вам необходимо нарисовать отрезок обязательно включая последнюю точку — продлите отрезок на один пиксель дальше (не забывайте, что вы задаете логические координаты, а удлинить отрезок надо на одну единицу устройства!). Если это сделать трудно (например отрезок наклонен под каким–то углом) то можно дополнительно нарисовать крохотный отрезок длиной 1 пиксель в любую сторону.
В случае Windows API часто применялась функция MoveTo, а не MoveToEx; Функция MoveTo возвращала информацию о прежнем положении текущей точки, упакованную в двойное слово. В Win32 API такая упаковка невозможна — каждая координата уже представляет собой двойное слово — поэтому функция MoveTo больше не поддерживается.
Возможен случай, когда надо нарисовать ломаную линию, состоящую из нескольких отрезков. Конечно, это легко сделать с помощью серии вызовов функции LineTo, но иногда может быть проще воспользоваться другой функцией:
BOOL Polyline (hDC, lpPoints, nCount);
Эта функция рисует ломаную линию, начальная, конечная и все точки перегиба которой заданы в массиве структур типа POINT (параметр lpPoints), содержащем nCount точек. Линия рисуется, начиная от первой точки и далее, соединяя последовательно отрезками все точки, вплоть до последней. Ломаная линия может быть незамкнутой, GDI не проводит замыкающего отрезка от последней точки к первой.
Еще одна функция GDI предназначена для рисования дуг:
BOOL Arc (hDC, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);
Для задания рисуемой дуги эллипса вы должны задать сначала сам эллипс, дуга которого будет изображаться, а затем задать начальную и конечную точки дуги. Эллипс задается описывающим его прямоугольником (причем нижняя и правая границы не включаются) — параметры xLeft, yTop, xRight и yBottom, а начальная и конечная точки задаются параметрами xStart, yStart и xEnd, yEnd.
Рисунок 4. Рисование дуг эллипсов
Начальная и конечная точки могут не лежать на эллипсе. В этом случае GDI вычисляет точку пересечения эллипса с отрезком, соединяющим центр эллипса с указанной вами точкой. Дуга будет рисоваться между точками пересечения отрезков, направленных от центра эллипса к начальной и конечной точкам дуги в направлении против хода часовой стрелки (см. раздел «Направление рисования дуг»).
В случае применения Win32 API вы можете использовать еще несколько функций для рисования линий. Так две новых функции предназначены для рисования дуг:
BOOLArcTo (hDC, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd); 1
BOOL AngleArc (hDC, nX, nY, dwRadius, eStartAngle, eSweepAngle); 1
Функция ArcTo аналогична функции Arc, за исключением того, что она сначала рисует отрезок от текущей точки до начальной точки дуги, затем рисует саму дугу и перемещает текущую точку в конечную точку нарисованной дуги. Функция AngleArc рисует дугу окружности, не эллипса. Для нее надо задать центр окружности (параметры nX, nY), радиус (dwRadius), начальный угол (eStartAngle) и угловую величину рисуемой дуги (eSweepAngle), в градусах. GDI не проверяет угловую величину дуги, она может превышать 3600.
Еще пара новых функций предназначена для рисования ломаных линий:
BOOL PolylineTo (hDC, lpPoints, nCount); 1
BOOL PolyPolyline (hDC, lpPoints, lpuCounts, nPolyCount); 1
Функция PolylineTo отличается от Polyline тем, что начинает рисование с отрезка от текущей точки до первой точки, указанной в массиве, а после прорисовки всех отрезков перемещает текущую точку в последнюю точку массива. Функция PolyPolyline может за одну операцию отобразить несколько ломаных линий. Координаты всех точек всех линий задаются массивом структур POINT (параметр lpPoints), число точек в каждой ломаной линии задается массивом целых чисел lpuCounts, а число ломаных, рисуемых этой функцией — параметром nPolyCount.
Кроме того в Win32 API существуют функции для рисования кривых Безье:
BOOL PolyBezier (hDC, lpPoints, cPoints); 1
BOOL PolyBezierTo (hDC, lpPoints, cPoints); 1
Функция PolyBezier рисует линию состоящую из сегментов кривых Безье. Для задания каждого сегмента требуется указать четыре точки: начальную, две направляющие и конечная. Так как рисуется линия из нескольких сегментов, то конечная точка одного сегмента является в свою очередь начальной точкой другого сегмента. Таким образом кривая будет определяться набором из Npt = 1 + Nsegments*3; здесь Npt — число точек для задания кривой, Nsegments — число сегментов в кривой.
Используя функцию PolyBezier вы должны задать массив точек, определяющих начальную точку, две направляющие и конечную точку для первого сегмента кривой, затем по две направляющих и одной конечной для каждого последующего сегмента. Текущая точка при этом не используется.
Функция PolyBezierTo отличается тем, что текущая точка используется в качестве начальной точки первого сегмента. В этом случае в массиве должно содержаться на одну точку меньше — только по две направляющих и одной конечной для каждого сегмента кривой. И, кроме того, после рисования кривой текущая точка будет перемещена в конечную точку последнего нарисованного сегмента.
Последняя рассматриваемая функция предназначена для рисования целого набора из прямых отрезков и кривых Безье за одну операцию:
BOOL PolyDraw (hDC, lpPoints, lpbyTypes, cCount); 1
Массивы структур POINT (lpPoints) и байтов (lpbyTypes) имеют одинаковое количество элементов; каждый элемент массива lpbyTypes определяет тип рисуемой линии из текущей точки до точки, задаваемой соответствующим элементом массива lpPoints. Допустимы следующие значения для типов линий:
PT_MOVETO | линия не рисуется, текущая точка перемещается в указанную позицию |
PT_LINETO | рисуется отрезок от текущей точки, до указанной; текущая точка перемещается в конечную точку отрезка. |
PT_BEZIERTO | рисуется кривая Безье; для задания кривой надо определить подряд три точки типа PT_LINETO: первые две точки — направляющие, последняя — конечная; текущая точка будет начальная точка кривой, а после рисования текущая точка переместится в конец нарисованного сегмента. |
PT_CLOSEFIGURE | этот флаг может быть объединен со значениями PT_LINETO или PT_BEZIERTO; при его указании фигура будет замкнута проведением отрезка от последней точки нарисованного сегмента до первой точки фигуры (точки типа PT_MOVETO или точки, заданной функцией MoveTo перед вызовом PolyDraw. |
Перо
Для проведения линий используется специальный объект GDI, который определяет вид проводимой линии, ее цвет и толщину. По аналогии с обычным рисованием на бумаге такой объект получил название перо (pen). Перо является объектом GDI, следовательно к нему применяются все правила работы с объектами GDI, рассмотренные ранее.
GDI предоставляет возможность использовать одно из трех стандартных перьев или создавать собственные перья, имеющие нужные свойства. Функция GetStockObject позволяет получить стандартное перо; они отличаются только цветом, проводимая ими линия всегда сплошная, шириной 1 единицу устройства (пиксель). Вместо функции GetStockObject можно использовать макрос GetStockPen из windowsx.h. Как и все стандартные объекты GDI эти перья нельзя уничтожать.
HANDLE GetStockObject (nIndex);
HPEN GetStockPen (nIndex); 2
BLACK_PEN | — черное перо |
WHITE_PEN | — белое перо |
NULL_PEN | — прозрачное перо |
Куда больше возможностей предоставляют функции, создающие перья. Две из них — CreatePen и CreatePenIndirect отличаются только способом передачи параметров. Функция CreatePen получает все характеристики создаваемого пера в виде отдельных параметров, а функция CreatePenIndirect использует структуру LOGPEN, описывающую создаваемое перо. Функционально обе функции тождественны. Эта же структура используется функцией GetObject для получения информации о пере.
HPEN CreatePen (nPenStyle, nWidth, crColor);
HPEN CreatePenIndirect (lpLogPen);
typedef struct tagLOGPEN {
WORD lopnStyle; // стильпера
POINT lopnWidth; // шириналинии
COLORREF lopnColor; // цветпера
} LOGPEN;
Стиль пера может быть:
PS_SOLID | сплошная тонкая или толстая линия | |
PS_DASH | штриховая тонкая линия | |
PS_DOT | пунктирная тонкая линия | |
PS_DASHDOT | штрих–пунктирная тонкая линия | |
PS_DASHDOTDOT | штрих–точка–точка тонкая линия | |
PS_NULL | (прозрачный) | |
PS_INSIDEFRAME | сплошная тонкая или толстая линия |
Ширина пера задается в логических единицах, причем в случае функции CreatePenIndirect для задания толщины используется структура типа POINT, в которой используется только поле x, а поле y — нет. Ширина пера задается в логических единицах. Так как логическая единица в общем случае может не совпадать с физической, то для создания тонких перьев (ширина которых равна 1 пикселю или одной строке растра) надо указать требуемую ширину равной 0 — тогда будет создано перо шириной 1 пиксель. Все прерывистые линии (PS_DOT, PS_DASH, PS_DASHDOT, PS_DASHDOTDOT), шириной больше физической 1 воспроизводятся как PS_SOLID.