Область в виде многоугольника
Можно создать область в виде произвольного многоугольника. Для этого следует воспользоваться функцией CreatePolygonRgn :
HRGN WINAPI CreatePolygonRgn( const POINT FAR* lppt, // адрес массива точек int cPoints, // размер массива int fnPolyFillMode); // режим заполненияФункция CreatePolyPolygonRgn создает область, состоящую из нескольких многоугольников:
HRGN WINAPI CreatePolyPolygonRgn( const POINT FAR* lppt, // адрес массива точек int FAR* lpnPolyCounts, // адрес массива количества точек // в многоугольниках int cPolygons); // количество многоугольниковНазначение параметров описанных выше двух функций аналогично назначению параметров функций рисования многоугольников Polygon и PolyPolygon (за исключением того, что при создании области не требуется указывать идентификатор контекста отображения).
Область эллиптической формы
Область эллиптической формы (или, как частный случай, круглой формы) можно создать при помощи функции CreateEllipticRgn :
HRGN WINAPI CreateEllipticRgn( int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);Параметры этой функции описывают координаты воображаемого прямоугольника, в который вписана область эллиптической формы.
Функция CreateEllipticRgnIndirect также используется для создания области в форме эллипса:
HRGN WINAPI CreateEllipticRgnIndirect(const RECT FAR* lprc);В отличие от функции CreateEllipticRgn координаты прямоугольника задаются с помощью структуры типа RECT, указатель на которую передается через параметр lprc.
Комбинирование областей
Функция CombineRegion позволяет вам изменить существующую область, скомбинировав ее из двух других:
int WINAPI CombineRgn( HRGN hrgnDest, // новая область HRGN hrgnSrc1, // первая исходная область HRGN hrgnSrc2, // вторая исходная область int fnCombineMode); // режим комбинированияПеред вызовом функции вам надо определить все три области. Функция объединит области hrgnSrc1 и hrgnSrc2, изменив соответствующим образом область hrgnDest.
Способ комбинирования областей зависит от значения параметра fnCombineMode:
Значение параметра fnCombineMode | Способ образования области hrgnDest |
RGN_AND | Пересечение областей hrgnSrc1 и hrgnSrc2 |
RGN_OR | Объединение областей hrgnSrc1 и hrgnSrc2 |
RGN_XOR | Объединение областей hrgnSrc1 и hrgnSrc2 с исключением перекрывающихся областей |
RGN_DIFF | Область hrgnSrc1, которая не входит в область hrgnSrc2 |
RGN_COPY | Область hrgnSrc1 |
В зависимости от результата выполнения операции функция CombineRegion может вернуть одно из следующих значений:
Значение | Описание |
ERROR | Ошибка |
NULLREGION | Новая область пустая |
SIMPLEREGION | Новая область не является самопересекающейся (т. е. граница созданной области не пересекает саму себя) |
COMPLEXREGION | Создана самопересекающаяся область |
Для облегчения комбинирования областей в файле windowsx.h определены макрокоманды, предназначенные для копирования, пересечения, объединения и вычитания областей. Все они созданы на базе только что описанной функции CombineRegion :
#define CopyRgn (hrgnDst, hrgnSrc) \ CombineRgn(hrgnDst, hrgnSrc, 0, RGN_COPY) #define IntersectRgn (hrgnResult, hrgnA, hrgnB) \ CombineRgn(hrgnResult, hrgnA, hrgnB, RGN_AND) #define SubtractRgn (hrgnResult, hrgnA, hrgnB) \ CombineRgn(hrgnResult, hrgnA, hrgnB, RGN_DIFF) #define UnionRgn (hrgnResult, hrgnA, hrgnB) \ CombineRgn(hrgnResult, hrgnA, hrgnB, RGN_OR) #define XorRgn (hrgnResult, hrgnA, hrgnB) \ CombineRgn(hrgnResult, hrgnA, hrgnB, RGN_XOR)
Перерисовка области
Вы можете отметить область как требующую перерисовки, вызвав функцию InvalidateRgn . В результате этого приложению будет передано сообщение WM_PAINT .
Приведем прототип функции InvalidateRgn:
void WINAPI InvalidateRgn(HWND hwnd, HRGN hrgn, BOOL fErase);Через параметр hwnd следует передать идентификатор окна, содержащего обновленную область hrgn.
Параметр fErase определяет необходимость стирания фона окна перед перерисовкой. Если этот параметр имеет значение TRUE, фон стирается, если FALSE - нет.
Если ваше приложение обновило содержимое области, но не во время обработки сообщения WM_PAINT, оно может удалить область из списка областей, подлежащих перерисовке, вызвав функцию ValidateRgn :
void WINAPI ValidateRgn(HWND hwnd, HRGN hrgn);Другие операции
Для изменения прямоугольной области можно воспользоваться функцией SetRectRgn , изменяющей координаты существующей прямоугольной области:
void WINAPI SetRectRgn( HRGN hrgn, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);Изменение области выполняется быстрее ее удаления с последующим повторный созданием новой.
Для перемещения области предназначена функция OffsetRgn :
int WINAPI OffsetRgn(HRGN hrgn, int nX, int nY);Эта функция сдвигает область на nX логических единиц по горизонтали и на nY логических единиц по вертикали, возвращая такие же значения, что и функция CombineRegion.
Вы можете сравнить две области при помощи функции EqualRgn :
BOOL WINAPI EqualRgn(HRGN hrgn1, HRGN hrgn2);Эта функция возвращает TRUE, если области совпадают, и FALSE - если нет.
С помощью функции GetRgnBox можно узнать координаты воображаемого прямоугольника, в который вписана область:
int WINAPI GetRgnBox(HRGN hrgn, RECT FAR* lprc);Эта функция возвращает такие же значения, что и функция CombineRegion
Иногда требуется определить, находится ли заданная точка или прямоугольная область внутри другой области. Это можно сделать при помощи функций PtInRegion и RectInRegion .
Функция PtInRegion возвращает TRUE, если точка (nX,nY) находится внутри области hrgn:
BOOL WINAPI PtInRegion(HRGN hrgn, int nX, int nY);Аналогично для прямоугольной области:
BOOL WINAPI RectInRegion(HRGN hrgn, const RECT FAR* lprc);Закрашивание области
Для закрашивания области кистью, выбранной в контекст отображения, предназначена функция PaintRgn :
BOOL WINAPI PaintRgn(HDC hdc, HRGN hrgn);Функция FillRgn также закрашивает область, но, в отличие от функции PaintRgn, эта функция использует кисть, идентификатор которой передается ей в качестве параметра hbrush:
BOOL WINAPI FillRgn(HDC hdc, HRGN hrgn, HBRUSH hbrush);С помощью функции FrameRgn вы можете обвести заданную область (закрасить ее границу), используя кисть hbrush:
BOOL WINAPI FrameRgn(HDC hdc, HRGN hrgn, HBRUSH hbrush, int nWidth, int nHeight);Параметры nWidth и nHeight определяют, соответственно, ширину и высоту кисти в пикселах, используемой для рисования границы.
Функция InvertRgn инвертирует цвета в указанной области:
BOOL WINAPI InvertRgn(HDC hdc, HRGN hrgn);Все эти функции возвращают TRUE при успешном завершении или FALSE при ошибке.
Графический редактор битовых изображений Paint Brush, который поставляется вместе с операционной системой Windows, умеет закрашивать внутренние области замкнутых фигур. Для закраски вам достаточно, находясь в соответствующем режиме редактора, указать мышью любую точку внутри фигуры.
Для реализации описанной выше операции в программном интерфейсе GDI предусмотрены функции FloodFill и ExtFloodFill.
Для функции FloodFill необходимо указать идентификатор контекста отображения hdc, координаты точки nX и nY, а также цвет контура clrref, ограничивающего область:
BOOL WINAPI FloodFill(HDC hdc, int nX, int nY, COLORREF clrref);Функция возвращает TRUE при успешном завершении или FALSE при ошибке (ошибка возникает в том случае, когда указанная точка имеет цвет clrref или она лежит вне области ограничения данного контекста отображения).
Для закраски используется кисть, выбранная в контекст отображения.
Функция ExtFloodFill аналогична функции FloodFill:
BOOL WINAPI ExtFloodFill(HDC hdc, int nX, int nY, COLORREF clrref, UINT fuFillType);Эта функция имеет дополнительный параметр fuFillType, определяющий способ закраски области. Параметр может принимать значения FLOODFILLBORDER или FLOODFILLSURFACE . В первом случае закрашивается внутренняя область фигуры, ограниченная контуром, имеющим цвет clrref (как и при использовании функции FloodFill). Во втором случае закрашивается вся область, имеющая цвет clrref.
Функция ExtFloodFill возвращает TRUE при успешном завершении или FALSE при ошибке. Если значение параметра fuFillType равно FLOODFILLBORDER, ошибка может возникнуть из-за тех же причин, что и при выполнении функции FloodFill. Если же значение параметра fuFillType равно FLOODFILLSURFACE, ошибка может возникнуть из-за того, что цвет точки (nX,nY) не равен clrref.
Область ограничения
Приложение может использовать область для маски, ограничивающей вывод. Для этого область следует выбрать в контекст отображения функцией SelectClipRgn :
int WINAPI SelectClipRgn(HDC hdc, HRGN hrgn);В качестве значения параметра hrgn вы можете использовать значение NULL. В этом случае для ограничения вывода будет использована внутренняя область окна.
Приложение REGIONS
Для демонстрации использования комбинированных областей мы подготовили приложение REGIONS. Это приложение создает три области: прямоугольную и две эллиптические разного размера. Прямоугольная область объединяется с первой эллиптической областью. Из полученного результата вырезается вторая эллиптическая область (меньших размеров).
Полученная комбинированная область используется в качестве маски, через которую в окно приложения выводится текст (рис. 2.27). Для большей наглядности границы области ограничения обведены зеленой кистью при помощи функции FrameRgn.
Рис. 2.27. Вывод текста с использованием области ограничения
Исходный текст приложения приведен в листинге 2.6.
Листинг 2.6. Файл regions/regions.cpp
// ----------------------------------------// Приложение REGIONS// Демонстрация использования области ограничения// ---------------------------------------- #define STRICT#include <windows.h>#include <windowsx.h>#include <mem.h> // Прототипы функцийBOOL InitApp(HINSTANCE);LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM); // Имя класса окнаchar const szClassName[] = "RegionsClass"; // Заголовок окнаchar const szWindowTitle[] = "Regions"; // Размеры внутренней области окнаshort cxClient, cyClient; // Размеры символов int cxChar, cyChar; // Текст для вывода в окне приложенияchar const szText[] = "В интерфейсе GDI есть средства, позволяющие приложениям" " создавать области достаточно сложной формы из" " прямоугольных, многоугольных и эллиптических областей." " Такие области можно закрашивать или использовать" " в качестве маски при выводе графического изображения. " "В последнем случае область называется областью" " ограничения. Она должна быть выбрана в контекст" " отображения."; // =====================================// Функция WinMain// =====================================#pragma argsused int PASCALWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow){ MSG msg; // структура для работы с сообщениями HWND hwnd; // идентификатор главного окна приложения // Инициализируем приложение if(!InitApp(hInstance)) return FALSE; // После успешной инициализации приложения создаем // главное окно приложения hwnd = CreateWindow( szClassName, // имя класса окна szWindowTitle, // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна CW_USEDEFAULT, // задаем размеры и расположение CW_USEDEFAULT, // окна, принятые по умолчанию CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, NULL); // Если создать окно не удалось, завершаем приложение if(!hwnd) return FALSE; // Рисуем главное окно ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); // Запускаем цикл обработки сообщений while(GetMessage(&msg, 0, 0, 0)) { DispatchMessage(&msg); } return msg.wParam;} // =====================================// Функция InitApp// Выполняет регистрацию класса окна// ===================================== BOOLInitApp(HINSTANCE hInstance){ ATOM aWndClass; // атом для кода возврата WNDCLASS wc; // структура для регистрации // класса окна // Записываем во все поля структуры нулевые значения memset(&wc, 0, sizeof(wc)); wc.lpszMenuName = NULL; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszClassName = (LPSTR)szClassName; // Регистрация класса aWndClass = RegisterClass(&wc); return (aWndClass != 0);} // =====================================// Функция WndProc// ===================================== LRESULT CALLBACK _exportWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){ HDC hdc; PAINTSTRUCT ps; static TEXTMETRIC tm; static HRGN hrgn1, hrgn2, hrgn3, hrgnTemp, hrgnClip; HBRUSH hbrush; switch (msg) { case WM_CREATE: { // Определяем метрику шрифта hdc = GetDC(hwnd); GetTextMetrics(hdc, &tm); ReleaseDC(hwnd, hdc); // Высота и средняя ширина букв cyChar = tm.tmHeight + tm.tmExternalLeading; cxChar = tm.tmAveCharWidth; // Область ограничения не задана hrgnClip = NULL; return 0; } // При изменении размеров окна сохраняем // новые значения для ширины и высоты, // а также определяем область ограничения case WM_SIZE: { cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); // Если область ограничения была определена раньше, // удаляем ее if(hrgnClip) DeleteRgn(hrgnClip); // Формируем область ограничения hrgnClip = CreateEllipticRgn(0, 0, cxClient, cyClient); // Временная область ограничения hrgnTemp = CreateEllipticRgn(0, 0, cxClient, cyClient); // Первая эллиптическая область hrgn1 = CreateEllipticRgn(0, 0, cxClient, cyClient); // Вторая эллиптическая область hrgn2 = CreateEllipticRgn(cxClient/3, cyClient/3, 2*(cxClient/3), 2*(cyClient/3)); // Прямоугольная область hrgn3 = CreateRectRgn(cxClient/20, cyClient/20, 19*(cxClient/20), 19*(cyClient/20)); // Комбинируем области UnionRgn(hrgnTemp, hrgn1, hrgn3); SubtractRgn(hrgnClip, hrgnTemp, hrgn2); // Удаляем временные области DeleteRgn(hrgn1); DeleteRgn(hrgn2); DeleteRgn(hrgn3); DeleteRgn(hrgnTemp); return 0; } // Рисование в окне case WM_PAINT: { RECT rc; // Получаем контекст отображения для // рисования во внутренней области окна hdc = BeginPaint(hwnd, &ps); // Выбираем встроенную кисть зеленого цвета hbrush = CreateSolidBrush(RGB(0, 0xff, 0)); // Обводим границы области FrameRgn(hdc, hrgnClip, hbrush, 2, 5); // Выбираем область ограничения в контекст // отображения SelectClipRgn(hdc, hrgnClip); // Определяем координаты прямоугольной // области для вывода текста rc.left = cxChar; rc.top = 0; rc.right = cxClient - cxChar; rc.bottom = cyClient; // Вывод текста DrawText(hdc, szText, lstrlen(szText), &rc, DT_LEFT | DT_WORDBREAK); // Освобождаем контекст отображения EndPaint(hwnd, &ps); return 0; } case WM_DESTROY: { // удаляем область ограничения DeleteRgn(hrgnClip); PostQuitMessage(0); return 0; } default: break; } return DefWindowProc(hwnd, msg, wParam, lParam);}Во время создания окна обработчик сообщения WM_CREATE определяет метрику шрифта и записывает значение NULL в переменную hrgnClip. Эта переменная будет использоваться для хранения идентификатора области ограничения.
Область ограничения формируется каждый раз заново при изменении размеров окна. Обработчик сообщения WM_SIZE сохраняет ширину и высоту окна в переменных cxClient и cyClient.
Затем он проверяет содержимое переменной hrgnClip. Если была задана область ограничения, она удаляется, так как при изменении размеров окна нужно сформировать новую область ограничения.
После этого приложение создает области ограничения и комбинирует их в одну область hrgnClip, используя макрокоманды UnionRgn и SubtractRgn.
Далее все области, кроме hrgnClip, удаляются, так как они больше не нужны.
Приложение REGIONS рисует в окне во время обработки сообщения WM_PAINT.
Для большей наглядности обработчик этого сообщения обводит контуры области ограничения, вызывая функцию FrameRgn:
hbrush = CreateSolidBrush(RGB(0, 0xff, 0));FrameRgn(hdc, hrgnClip, hbrush, 2, 5);Далее область hrgnClip выбирается в контекст отображения для использования в качестве маски при выводе текста:
SelectClipRgn(hdc, hrgnClip);Вывод текста выполняется при помощи функции DrawText.
Перед завершением своей работы (при обработке сообщения WM_DESTROY) приложение удаляет область hrgnClip, вызывая макрокоманду DeleteRgn:
DeleteRgn(hrgnClip);Файл определения модуля для приложения REGIONS приведен в листинге 2.7.
Листинг 2.7. Файл regions/regions.def
; =============================; Файл определения модуля; =============================NAME REGIONSDESCRIPTION 'Приложение REGIONS, (C) 1994, Frolov A.V.'EXETYPE windowsSTUB 'winstub.exe'STACKSIZE 8120HEAPSIZE 1024CODE preload moveable discardableDATA preload moveable multiple