Прямоугольники часто используются для объявления каких–либо областей внутренней области окна неверными, то есть нуждающимися в перерисовке или, наоборот, верными — после прорисовки указанной области. Для этого предназначены две функции
BOOLInvalidateRect (hWnd, lpRect, fEraseBkgnd);
BOOLValidateRect (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:
intCopyRgn (hrgnDest, hrgnSrc); 2
intIntersectRgn (hrgnDest, hrgnSrc1, hrgnSrc2); 2
intSubtractRgn (hrgnDest, hrgnSrc1, hrgnSrc2); 2
intUnionRgn (hrgnDest, hrgnSrc1, hrgnSrc2); 2
intXorRgn (hrgnDest, hrgnSrc1, hrgnSrc2); 2
Существует еще одна функция, которая может изменить тип региона, она позволяет заменить указанный вами любой регион на регион прямоугольной формы:
void SetRectRgn (hrgnSrc, lpRect);
Таким образом, применяя функции создания регионов и их комбинируя мы можем описать области очень сложной формы. Теперь нам надо разобраться с основными способами применения регионов.
Во–первых, мы можем применять регионы как абстрактные объекты, и выполнять над ними какие-либо операции, например перемещение, аналогично операциям над прямоугольниками:
int OffsetRgn (hrgnSrc, nDeltaX, nDeltaY);
или проверять совпадение регионов:
BOOL EqualRgn (hrgnSrc1, hrgnSrc2);
Кроме того мы можем проверить принадлежность точки или прямоугольника региону:
BOOL PtInRegion (hrgnSrc, nX, nY);
BOOLRectInRegion (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);
voidValidateRgn (hWnd, hrgnSrc);
Если сообщение WM_PAINT генерируется в результате появления неверного региона, то полученный с помощью функции BeginPaint контекст устройства может применяться только для рисования внутри неверного региона.
В–четвертых, регион, являясь объектом GDI, может быть выбран в контекст устройства. Регион, выбранный в контекст устройства, определяет область этого контекста, на которой возможно рисование. При этом он является как бы "маской" через которую видно рисуемое изображение.
Рисунок 15. Исходное изображение (слева), регион (в центре) и нарисованное изображение (справа). Светло–серым цветом показан неизменяемый данным рисунком фон.
Для того, что бы выбрать регион в контекст устройства, вы должны воспользоваться функцией
int SelectClipRgn (hDC, hrgnSrc);
Эта функция возвращает целое число, указывающее тип выбранного региона (SIMPLEREGION, COMPLEXREGION, NULLREGION). При выборе региона в контекст устройства он копируется, поэтому вы можете удалить его или использовать иным образом сразу после выбора в контекст устройства.
Кроме функции SelectClipRgn вы можете воспользоваться функцией SelectObject с той же целью. При этом функция SelectObject будет использоваться точно также, как и функция SelectClipRgn, и вернет не хендл предыдущего региона, а тип выбранного вами.
Растровые изображения и метафайлы
В Windows существует возможность хранить изображения в виде картинок, сохраняющих рисунок в виде информации о цветах отдельных точек. Такие изображения иногда называются растровыми, так как информация о цвете точек группируется по строкам растра изображения, или битмапами (bitmap), иногда термин bitmap даже переводят дословно — битовая карта. В ранних версиях Windows битмапы точно соответствовали образу в графической памяти устройства, на которое осуществлялся вывод. При этом информация о передаче цветов соответствовала в точности формату цветовой памяти устройства. Такие битмапы получили название зависимых от устройства битмапов (device–depended bitmap, DDB)
Так, например, для четырехцветных видеоадаптеров CGA каждый пиксель кодировался двумя последовательными битами в видеопамяти — такой–же была организация битмапов, отображаемых на дисплее. А если использовался 16ти–цветный адаптер EGA, в котором для задания каждого пикселя требовалось задать 4 бита лежащих в различных цветовых плоскостях (planes), то и битмап создавался аналогично — каждая строка растра была представлена 4 раза, для каждой цветовой плоскости по разу. Несомненным достоинством таких изображений была простота их отображения на конечном устройстве и высокая скорость вывода.
Но были и недостатки — главный из них — цвет конкретной точки определяется непосредственно настройками аппаратуры и остается независим от самого растрового изображения. То есть попытка отобразить одно и то же изображение при различных настройках (допустим, при различных используемых палитрах), приводила к искажению цветов исходного изображения. Помимо этого при попытке отобразить одно и тоже изображение на разных устройствах требовалось преобразовывать информацию о кодировании цветов — что опять же требовало дополнительных данных о назначении цветов в обоих устройствах.