Привязка кисти (brush alignment)



 

Существует совершенно особый прием при работе с кистями — так называемая привязка кисти. При закраске внутренней области какой–либо фигуры с помощью кисти GDI многократно воспроизводит ее изображение. Однако при этом возникает вопрос: с какого места начинает воспроизводиться изображение кисти? Считается, что GDI по умолчанию повторяет кисть начиная с верхнего левого угла экрана — то есть от точки с координатами устройства (0,0). Это так называемая начальная точка кисти (brush origin).

 

Рисунок 10. Пояснения к атрибуту начальная точка кисти (brush origin)

 

Если кисть представлена каким–либо изображением или штриховкой, то все закрашиваемые фрагменты попадают в одну "фазу". В некоторых случаях надо изменять начало отсчета кисти для того, что бы рисунок или штриховка был согласован с закрашиваемой поверхностью. Для этого применяется прием, называемый выравнивание кисти (brush alignment):

POINT pt;

// brush origin устанавливается в координатах устройства,

// а точка по которой мы будем выравнивать обычно определена

// в логических координатах, поэтому требуется преобразование.

pt.x= 0; pt.y= 0; // выбираем логические координаты новой точки

LPtoDP (hDC, &pt, 1); // переводим их в координаты окна

ClientToScreen (hWnd, &pt); // а теперь в координаты экрана

//-если мы применяем систему координат MM_TEXT(по умолчанию),то

// мы можем не использовать функцию LPtoDP;

// - если контекст соответствует иному устройству, чем дисплей,

// то мы не используем функцию ClientToScreen.

// кисть имеет размер 8x8 пикселей, поэтому координаты начальной точки

// лучше задавать в диапазоне 0..7, то есть остаток от деления на 8

pt.x %= 8; pt.y %= 8;

// теперь нам известны координаты устройства нового brush origin

UnrealizeObject (hNewBrush);

SetBrushOrg (hDC, pt.x, pt.y);

// функция UnrealizeObject разрешает назначить для кисти новую начальную

// точку; это назначение произойдет при выборе кисти в контекст устройства,

// причем начальная точка назначается именно контексту, а не кисти.

SelectObject (hDC, hNewBrush);

При выравнивании кисти надо придерживаться нескольких ограничений:

запрещено применять функцию UnrealizeObject ко всем стандартным кистям;

выравнивать можно только кисть, не выбранную в контекст устройства;

 

Режим заполнения многоугольников

 

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

 

 

Рисунок 11. Примеры многоугольников с перекрывающимися поверхностями.

 

В такой ситуации поведение GDI будет определяться текущим режимом заполнения многоугольников (polygon filling mode). Вы можете его изменить или узнать с помощью функций

UINT GetPolyFillMode (hDC);

UINT SetPolyFillMode (hDC, nIndex);

допустимы два значения параметра nIndex: ALTERNATE и WINDING. В режиме ALTERNATE GDI закрашивает на каждой строке развертки отрезок между сторонами с нечетным и четным последовательными номерами. Очень упрощенно — область, которая повторно закрашивается сохранит первоначальный вид. В режиме WINDING применяется более сложный алгоритм, который позволяет вычислить и закрасить все внутреннюю область многоугольника. Например, заполнение многоугольника в виде пятиконечной звездочки в различных режимах заполнения многоугольников выглядит так:

 

Рисунок 12. Заполнение пятиконечной звездочки в различных режимах заполнения многоугольников.

 

Прямоугольники и регионы

 

Исторически сложилось так, что прямоугольник является базовой фигурой при работе с графическими устройствами. Значительная часть примитивов GDI требует задания описывающего прямоугольника, окна опять–же имеют форму прямоугольника (не считая возможности использовать эллиптические окна в Windows–95), области окон, нуждающиеся в перерисовке — неверные области — в ранних версиях Windows описывались прямоугольником, растровые изображения — битмапы — имеют форму прямоугольника и так далее. Естественно, что в Windows были включены специальные средства для выполнения математических операций над прямоугольниками и некоторый вспомогательный набор функций, осуществляющий операции закраски, обводки контура, инверсии цвета и прочего в указанной вами прямоугольной области.

По мере развития Windows многие функции прямоугольников были переданы более сложным объектам — регионам (region), которые могут описывать области сложной формы. При этом развился параллельный набор функций, ориентированных на применение регионов вместо прямоугольников.

Функции, ориентированные на работу с прямоугольниками и с регионами достаточно разнородны, относятся к самым разным подсистемам Windows, не только к GDI, смотря по тому, как будет применяться указанный прямоугольник или регион. Большая часть этих функций рассматривается в этом разделе, хотя в других разделах те же самые функции будут рассматриваться дополнительно, с углубленным обсуждением их использования.

 

Прямоугольники

 

Рассмотрение операции над прямоугольниками мы начнем с математических функций. Эти функции рассматривают прямоугольник как некую математическую абстракцию, описываемую структурой типа RECT.

typedef struct tagRECT {

int left;

int top;

int right;

int bottom;

} RECT;

Над этой структурой можно производить некоторые операции, как–то инициализировать нулем, задать ее поля, прибавить ко всем полям определенные величины и так далее. Использование этих функций не всегда целесообразно, так как того же эффекта можно достичь более простыми средствами. Так, функция

void SetRect (lpRect, xLeft, yTop, xRight, yBottom);

позволяет заполнить структуру типа RECT указанными значениями, функция

void SetRectEmpty (lpRect);

обнуляет поля структуры RECT, а функция

void CopyRect (lpRectDst, lpRectSrc);

копирует одну структуру RECT в другую. Рассмотренные функции заменяются на более простые конструкции самым тривиальным образом, причем получаемый код оказывается компактнее и существенно быстрее. Еще две функции осуществляют перемещение прямоугольника по координатной плоскости (OffsetRect) и изменение его размеров (InflateRect):

void OffsetRect (lpRect, nDeltaX, nDeltaY);

void InflateRect (lpRect, nDeltaWidth, nDeltaHeight);

void InsetRect (lpRect, nDeltaWidth, nDelatHeight); 2

Макрос InsetRect соответствует вызову функции:

InflateRect (lpRect, - (nDeltaWidth), - (nDeltaHeight)).

Некоторые функции для работы с прямоугольниками, предоставляемые Windows, все же достаточно удобны, что бы их не заменять собственными:

BOOL IsRectEmpty (lpRect);

проверяет, является ли данный прямоугольник пустым, или нет; Функция

BOOL EqualRect (lpRect, lpRect);

проверяет совпадение прямоугольников (имеют ли они одинаковые размеры и положение); а функция

BOOL PtInRect (lpRect, lpPoint);

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

BOOL IntersectRect (lpRectDst, lpRectSrc1, lpRectSrc2);

BOOL UnionRect (lpRectDst, lpRectSrc1, lpRectSrc2);

BOOL SubtractRect (lpRectDst, lpRectSrc1, lpRectSrc2);

 

Рисунок 13. Пересечение, объединение и два варианта исключения прямоугольников.

 

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

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

void InvertRect (hDC, lpRect);

инвертирует цвет указанного прямоугольника, выполняя операцию BITWISE NOT над всеми пикселями прямоугольника. Эта функция удобна для выделения какого-либо прямоугольника, так как повторное выполнение этой операции восстанавливает первоначальный вид прямоугольника.

В некоторых случаях бывает удобно просто закрасить указанной кистью необходимую область. Конечно, это можно сделать с помощью функции Rectangle. Однако этот способ не всегда хорош, так как при рисовании прямоугольника он окружается линией, нарисованной текущим карандашом. Этот карандаш, во–первых всегда представлен чистым цветом, а, во–вторых, прямоугольник не всегда надо ограничивать линией (использование прозрачного карандаша приводит к частой смене карандашей). Для этого Windows содержит две дополнительные функции:

int FillRect (hDC, lpRect, hBrush);

int FrameRect (hDC, lpRect, hBrush);

Функция FillRect закрашивает указанный прямоугольник требуемой кистью, а функция FrameRect проводит вокруг указанного прямоугольника каемку опять же кистью (не карандашом). Ширина проводимой каемки 1 пиксель как по горизонтали, так и по вертикали.

Вместо функции FillRect можно иногда применять функцию PatBlt, которая позволяет закрасить фон текущей кистью. Эта функция поддерживается непосредственно драйверами устройств, так что ее применение дает наиболее быстрый исполняемый код. Подробнее о функции PatBlt см. в разделе «Операции передачи образов»

Частный случай — закраска прямоугольной области не кистью, а конкретным цветом. Очевидный способ — создание однотонной кисти и закраска прямоугольника с помощью функции FillRect или Rectangle — во–первых достаточно громоздок и, во–вторых, не гарантирует закраски именно чистым цветом — кисть может оказаться смешанной из точек разных цветов. Наиболее быстрый способ — использовать функцию ExtTextOut, указав ей пустую строку, ограничивающий прямоугольник и необходимость закраски прямоугольника цветом фона (флаг ETO_OPAQUE).

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

BOOL InvalidateRect (hWnd, lpRect, fEraseBkgnd);

BOOL ValidateRect (hWnd, lpRect);

Если вместо адреса структуры RECT указать NULL, то система будет подразумевать прямоугольник, совпадающий со всей внутренней областью окна.

 

Регионы

 

Такое количество функций, предназначенных для работы с прямоугольниками связано с тем, что прямоугольник можно назвать основным примитивом Windows, так как он используется практически повсеместно. Однако применение прямоугольников не всегда эффективно. Например, если прямоугольники используются для объявления неверной области окна, то объединение, скажем, двух небольших неверных прямоугольников в противоположных углах окна приведет к объявлению всей внутренней области нуждающейся в перерисовке. Часто вместо прямоугольников эффективнее использовать области сложной формы и, соответственно, регионы (region) как объекты, описывающие области сложной формы.

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

Мы можем создавать прямоугольные регионы, прямоугольные со скругленными углами, эллиптические и регионы в виде многоугольников. Для этого предназначены следующие функции:

HRGN CreateRectRgn (xLeft, yTop, xRight, yBottom);

HRGN CreateRectRgnIndirect (lpRect);

HRGN CreateRoundRectRgn (xLeft, yTop, xRight, yBottom, xRound, yRound);

HRGN CreateEllipticRgn (xLeft, yTop, xRight, yBottom);

HRGN CreateEllipticRgnIndirect (lpRect);

HRGN CreatePolygonRgn (lpPoints, nCount, nPolyFillMode);

HRGN CreatePolyPolygonRgn (lpPoints, lpCounts, nPolyCount, nPolyFillMode);

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

В результате вызова одной из функций Create...Rgn создается специальный объект, описывающий регион, а нам возвращается хендл этого объекта.

Как и всякий объект GDI регион удаляется с помощью функции DeleteObject.

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

int CombineRgn (hrgnDest, hrgnSrc1, hrgnSrc2, nMode);

Данная функция позволяет выполнить определенную параметром nMode операцию над двумя (или одним) исходными регионами и результат записать в третий регион. При этом новый регион не создается, вы должны предварительно создать какой-либо регион и его хендл передать в качестве hrgnDest. В этом регионе будет размещен результат выполнения операции. Такое, на первый взгляд странное, правило позволяет несколько уменьшить количество создаваемых объектов.

Итак, с помощью функции CombineRgn, мы можем выполнять различные операции, задавая номер нужной операции в параметре nMode:

RGN_AND — получить пересечение двух регионов (точки, входящие в оба региона одновременно)
RGN_OR — получить объединение регионов (точки, входящие хотя бы в один из двух регионов)
RGN_XOR — получить объединение без перекрывающихся областей
RGN_DIFF — получить часть первого региона, не входящую во второй регион
RGN_COPY — скопировать первый регион (второй регион не используется)

 

При этом функция возвращает информацию о том, какой регион получен:

 

SIMPLEREGION — если итоговый регион состоит из не перекрывающихся примитивов
COMPLEXREGION — если примитивы, входящие в итоговый регион, перекрываются
NULLREGION — итоговый регион пустой (не имеет общих точек)
ERROR — возникла ошибка (например, недостаточно памяти)

 

В заголовочном файле windowsx.h включено несколько макросов, основанных на функции CombineRgn:

int CopyRgn (hrgnDest, hrgnSrc); 2

int IntersectRgn (hrgnDest, hrgnSrc1, hrgnSrc2); 2

int SubtractRgn (hrgnDest, hrgnSrc1, hrgnSrc2); 2

int UnionRgn (hrgnDest, hrgnSrc1, hrgnSrc2); 2

int XorRgn (hrgnDest, hrgnSrc1, hrgnSrc2); 2

Существует еще одна функция, которая может изменить тип региона, она позволяет заменить указанный вами любой регион на регион прямоугольной формы:

void SetRectRgn (hrgnSrc, lpRect);

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

Во–первых, мы можем применять регионы как абстрактные объекты, и выполнять над ними какие-либо операции, например перемещение, аналогично операциям над прямоугольниками:

int OffsetRgn (hrgnSrc, nDeltaX, nDeltaY);

или проверять совпадение регионов:

BOOL EqualRgn (hrgnSrc1, hrgnSrc2);

Кроме того мы можем проверить принадлежность точки или прямоугольника региону:

BOOL PtInRegion (hrgnSrc, nX, nY);

BOOL RectInRegion (hrgnSrc, lpRect);

И еще одна функция позволяет получить прямоугольник, описанный вокруг указанного региона:

int GetRgnBox (hrgnSrc, lpRect);

Во–вторых, регионы могут отображаться на контексте устройства, например для закраски областей или обведения контура области сложной формы:

BOOL InvertRgn (hDC, hrgnSrc);

BOOL PaintRgn (hDC, hrgnSrc);

BOOL FillRgn (hDC, hrgnSrc, hbrBrush);

BOOL FrameRgn (hDC, hrgnSrc, hbrBrush, nFrameWidth, nFrameHeight);

Функция InvertRgn осуществляет операцию BITWISE NOT над всеми точками, входящими в указанный регион; она аналогична функции InvertRect. Функция PaintRgn закрашивает регион текущей кистью. Она подобна функции FillRgn, которая закрашивает регион указанной вами, а не текущей, кистью. Самая интересная функция — FrameRgn, которая проводит вокруг региона каемку указанной ширины и указанной кистью. То есть эта функция аналогична функции FrameRect, за исключением того, что область может быть сложной формы и вы можете задать ширину каемки, причем как по горизонтали, так и по вертикали.


Рисунок 14. Применение регионов для закраски и областей и обведения области контуром.

 

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

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

void InvalidateRgn (hWnd, hrgnSrc, fEraseBkgnd);

void ValidateRgn (hWnd, hrgnSrc);

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

В–четвертых, регион, являясь объектом GDI, может быть выбран в контекст устройства. Регион, выбранный в контекст устройства, определяет область этого контекста, на которой возможно рисование. При этом он является как бы "маской" через которую видно рисуемое изображение.


Рисунок 15. Исходное изображение (слева), регион (в центре) и нарисованное изображение (справа). Светло–серым цветом показан неизменяемый данным рисунком фон.

 

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

int SelectClipRgn (hDC, hrgnSrc);

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

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

 


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

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






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