Отображение основных графических объектов



 

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

При отображении отдельных пикселей атрибуты контекста устройства практически не используются — вы просто указываете цвет и положение пикселя, который хотите отобразить. На результат влияют только область отображения и характеристики устройства.

При рисовании линий на результат начинает влиять множество других атрибутов, определяющих вид отображаемой линии и то, каким образом осуществляется это отображение.

При рисовании заполненных фигур сам процесс рисования можно представить в виде двух фаз — рисование контура и заполнение внутренней области. Если рисование контура осуществляется также, как и рисование линий, то на заполнение фона начинают влиять дополнительные атрибуты.

 


Задание цвета

 

При отображении любых графических объектов необходимо задавать их цвета. Сложности задания цвета начинаются из–за того, что различная аппаратура может воспроизводить только некоторое конечное число цветов. Одновременно вам может понадобиться осуществлять вывод сразу на несколько устройств, возможности которых по отображению цвета существенно различаются. Например — любой текстовой или графический редактор может взаимодействовать как с дисплеем, способным воспроизводить от нескольких цветов до миллионов цветов и принтером, который часто является монохромным.

Для обеспечения универсальности в GDI принято, что цвет обозначается с помощью 24х битового параметра типа COLORREF. Этот параметр реально соответствует одному двойному слову, самый старший байт которого не используется. Младшие три байта задают интенсивности трех основных компонент цвета — красной, зеленой и синей. Каждая компонента задается своим байтом, так что предельный диапазон ее изменения лежит от 0 до 255. Это позволяет задавать до 16 777 216 различных цветов.

Для удобного задания параметра типа COLORRREF используется специальный макрос RGB, который из трех по отдельности указанных компонент формирует одно двойное слово.

COLORREF RGB (byRed, byGreen, byBlue);

Конечно, Windows не всегда может использовать 24х битовый цвет непосредственно, так как аппаратура может поддерживать от 2 цветов (монохроматические устройства) до более чем 16 млн. цветов (режимы TrueColor и HighColor для современных видеоадаптеров). Вместо отображения указанного вами цвета, GDI будет подбирать ближайший цвет, который в состоянии воспроизвести данная аппаратура в данном режиме; такой цвет называется чистый (pure color).

Внимание! на монохромных устройствах существует только два чистых цвета: черный и белый. Поэтому все цвета, имеющие сумму яркостей трех составляющих менее 381, представляются черными, а больше — белыми. Это может привести к неразличимости образа, если, например, вы по черному фону рисуете красную линию, или по белому — светло–циановую.

В некоторых случаях GDI может использовать вместо чистого цвета смесь точек разных цветов, наиболее точно передающих желаемый цвет. Такой цвет называется смешанным (dithered color). Однако такая операция выполняется далеко не всегда. Так при выводе текста цвет символов будет чистым цветом; аналогично линии (за небольшим исключением) рисуются тоже чистым цветом.

 

Работа с отдельными пикселями

 

Рисование по отдельным точкам крайне редко используется в Windows, так как скорость осуществления подобной операции является исключительно низкой. Во всех случаях рекомендуется найти какой–либо другой способ отображения, а не рисовать по отдельным точкам.

Собственно, для рисования точек существуют всего две функции:

СOLORREF GetPixel (hDC, nX, nY);

COLORREF SetPixel (hDC, nX, nY, crColor);

Координаты nX и nY являются логическими координатами, определяющими позицию пикселя, цвет которого мы узнаем или изменяем. Независимо от принятой системы координат отображается один пиксель, даже если его размер соответствует нескольким логическим единицам.

Кроме того, задаваемый цвет пикселя автоматически преобразуется к ближайшему чистому цвету, который может быть воспроизведен аппаратурой.

Если используемый пиксель находится вне области рисования, то обе функции возвращают -1, а если пиксель находится внутри области рисования, то возвращаемое значение соответствует текущему цвету пикселя. Для функции SetPixel оно может отличаться от заданного crColor, указывая примененный чистый цвет (если ваша аппаратура не может отобразить требуемый цвет). Этот прием иногда используется для проверки возможностей устройства, хотя эффективнее анализировать возможности устройства с помощью функции GetDeviceCaps.

 

Рисование линий

 

В процессе рисования линии участвует сразу несколько атрибутов GDI (помимо атрибутов, выбирающих систему координат). Перед рисованием нужной вам линии вы должны настроить контекст устройства в соответствии с выполняемой операцией, установив нужные значения атрибутов. Коротко назначение этих атрибутов следующее:

Текущее перо (Pen).

Определяет вид проводимой линии — толщину, цвет, стиль (сплошная или прерывистая). Перо является объектом GDI, поэтому при его использовании необходимо соблюдать правила работы с объектами GDI.

Текущая позиция пера (Pen Position).

Определяет точку из которой начнется рисование следующего отрезка линии. После рисования этого отрезка текущая точка перемещается в конец отрезка.

Цвет фона (Background Color).

Заполняет промежутки между штрихами прерывистой линии (а также фон под символами выводимого текста или промежутки между линиями штрихованных кистей — об этом см. в обсуждении рисования заполненных фигур и обсуждении вывода текста).

Режим заполнения фона (Background Mode).

Определяет, надо–ли закрашивать промежутки между штрихами прерывистой линии цветом фона или оставить фон без изменений. Аналогично — при выводе текста.

Режим рисования (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 вы можете использовать еще несколько функций для рисования линий. Так две новых функции предназначены для рисования дуг:

BOOL ArcTo (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.

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

Широкая линия проводится таким образом, что бы ее центр соединял указанные точки. Перо применяется не только для рисования линий, но и для рисования контура фигур. Если при этом используется широкая линия, то может быть удобно, что бы вся линия лежала внутри контура фигуры. Для таких целей предназначен стиль линии PS_INSIDEFRAME. Линии стиля PS_INSIDEFRAME отличаются еще одной особенностью: если ее ширина больше или равна 2 единицам устройства, то эта линия может рисоваться не чистым цветом, а смешанным.

При использовании Win32 API вы можете воспользоваться еще одной функцией для создания перьев:

HPEN ExtCreatePen (dwPenStyle, dwWidth, lpLogBrush, dwStyleCount, lpdwStyle); 1

typedef struct tagLOGBRUSH {

UINT lbStyle;

COLORREF lbColor;

LONG lbHatch;

} LOGBRUSH;

Перья, создаваемые этой функцией делятся на два типа: косметические (cosmetic) и геометрические (geometric). Для описания характеристик пера используется структура LOGBRUSH, обычно применяемая для описания кистей (см. раздел “Кисть”).

Косметические перья создаются, если в параметре dwPenStyle установлен стиль PS_COSMETIC. Они соответствуют тонким сплошным или прерывистым линиям (штриховым, штрих–пунктирным и пунктирным); всегда имеют ширину 1 пиксель (параметр dwWidth должен быть задан 1) и рисуются чистым цветом (цвет задается полем lbColor структуры LOGBRUSH; поле lbStyle обязательно должно быть BS_SOLID, а поле lbHatch не используется). Помимо стиля PS_COSMETIC надо задать один из дополнительных стилей:

Стили PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT, PS_DASHDOTDOT, PS_NULL, PS_INSIDEFARME в основном соответствуют уже рассмотренным стилям обычных перьев. (Для косметических ширина всегда равна 1 пикселю).

Стиль PS_ALTERNATE означает, что точки линии будут рисоваться через одну.

Если используется стиль PS_USERSTYLE, то два последних параметра функции ExtCreatePen задают стиль прерывистой линии: параметр lpdwStyle указывает на массив целых чисел, задающих длину штрихов и промежутков между ними; параметр dwStyleCount число записей в этом массиве. Внимание! длина штрихов и промежутков задается не в логических единицах, а в единицах устройства, непосредственно в процессе рисования линии.

Другой вид перьев — геометрические — создаются, если в параметре dwPenStyle указан стиль PS_GEOMETRIC. Такие перья могут быть произвольной толщины и цвета, включая узорчатые перья (рисунок на которых задается указанной кистью). Кроме того, при создании геометрических кистей есть возможность задать дополнительные стили. Так, в параметре dwPenStyle вы должны определить стиль PS_GEOMETRIC, стиль линии, стиль оформления окончаний и стиль сопряжения линий.

Стиль линии может быть PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT, PS_DASHDOTDOT, PS_NULL, PS_INSIDEFARME или PS_USERSTYLE. Стиль PS_ALTERNATE для геометрических перьев не поддерживается.

Стиль оформления окончаний играет роль для широких линий и может быть PS_ENDCAP_ROUND (конец линии полукруглый), PS_ENDCAP_SQUARE (в виде угла) и PS_ENDCAP_FLAT (прямой).

Стиль сопряжения линий влияет, если несколько последовательно проводимых отрезков сопрягаются под небольшим углом. Он может быть PS_JOIN_BEVEL (линии стыкуются под углом), PS_JOIN_ROUND (сопряжение скругляется) и PS_JOIN_MITER (сопряжение спрямляется). Если используется перо со стилем PS_JOIN_MITER, то дополнительно можно задать предельную величину спрямления сопрягаемых линий с помощью функции

BOOL SetMiterLimit (hDC, eNewLimit, lpeOldLimit); 1

BOOL GetMiterLimit (hDC, lpeOldLimit); 1

Величина спрямления задается относительно ширины линии.

Ширина геометрической линии может быть любой, она задается параметром dwWidth в логических единицах. Параметр lpLogBrush описывает кисть, которая задает цвет и узор линии; кисть может быть однотонной, смешанной, штрихованной или даже созданной по образцу — зависимому от устройства битмапу. Использование независимых от устройства битмапов в качестве образцов при описании пера недопустимо. О кистях и битмапах см. разделы “Кисть” и “Растровые изображения”.

Как и в случае косметических перьев, параметры dwStyleCount и lpdwStyle определяют стиль штриховки, если задан стиль линии PS_USERSTYLE, иначе они не используются. Однако в отличие от косметического пера, ширина штрихов и промежутков задается в логических единицах.

После использования созданного пера вы должны его уничтожить, причем перед этим он должен быть освобожден из контекста устройства (или весь контекст должен быть освобожден). Перо, как и все объекты GDI, уничтожается с помощью функции:

BOOL DeleteObject (hGDIobj);

BOOL DeletePen (hPen);


Текущая позиция пера

 

Следующий атрибут контекста устройства, используемый при рисовании линии, называется текущей позицией пера. Этот атрибут используется очень небольшим числом функций, поэтому мы вкратце рассмотрим их всех, даже если они не связаны с рисованием линий.

С первыми тремя функциями мы уже познакомились — они–то и используются для рисования линий.

DWORD MoveTo (hDC, nX, nY); 0

BOOL MoveToEx (hDC, nX, nY, lpPoint);

BOOL LineTo (hDC, nX, nY);

Помимо них с текущей позицией пера имеют дело функции GetCurrentPosition и GetCurrentPositionEx а также, в некоторых случаях, функции TextOut и ExtTextOut.

DWORD GetCurrentPosition (hDC); 0

BOOL GetCurrentPositionEx (hDC, lpPoint);

Эти функции возвращают позицию пера. Как обычно — GetCurrentPosition возвращает ее в двойном слове и применятся только в 16ти разрядном Windows API, тогда как функция GetCurrentPositionEx возвращает результат в структуре типа POINT и может применяться как в Windows API, так и в Win32 API.

Две другие функции — TextOut и ExtTextOut обычно не используют текущую позицию пера, вы должны им отдельно указывать позицию для вывода текста. Однако, иногда может быть удобно перейти в специальный режим привязки текста TA_UPDATECP (см. функцию SetTextAlign), при котором текст будет выводиться от текущей позиции, а после вывода текста текущая позиция переместится в конец выведенного фрагмента.


Дата добавления: 2019-02-12; просмотров: 232; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!