Сообщений 3    Оценка 56        Оценить  
Система Orphus

Работа с регионами Windows GDI. Создание непрямоугольных окон.

Автор: Сапронов Андрей Юрьевич
Источник: RSDN Magazine #1-2003
Опубликовано: 28.05.2003
Исправлено: 13.03.2005
Версия текста: 1.0
Создание окна в виде эллипса (WinAPI)
Создание окна в виде эллипса (MFC)
Создание прямоугольного окна с закругленными краями (WinAPI)
Создание прямоугольного окна с закругленными краями (MFC)
Демонстрация CombineRgn (WinAPI)
Демонстрация CombineRgn (MFC)
Создание окна в виде забора (WinAPI)
Создание окна в виде забора (MFC)
Трансформации регионов (WinAPI)
Трансформации регионов (MFC)

Пример с использованием библиотеки MFC
Пример с использованием WinAPI

В статье рассматривается создание непрямоугольных окон с применением регионов Windows GDI. Помимо непосредственного описания создания непрямоугольных окон, описано использование функций WinAPI для работы с регионами, а также соответствующих им методов класса CRgn библиотеки MFC (далее будут рассматриваться функции WinAPI, и если нет специальных оговорок, то всё, что касается функции, относится и к одноименному методу класса CRgn). Особое внимание уделено редко описываемым функциям GetRegionData, ExtCreateRegion (CRgn::CreateFromData для MFC). В контексте этих функций показана работа со структурой XFORM. Статья не преследует цели формального описания функций. Частью статьи является демонстрационная программа (WinAPI- и MFC-версии), содержащая полный исходный код, фрагменты которого приводятся.

Графический объект "регион" (в некоторых источниках "region" переводят как "область") представляет (определяет) собой плоскую произвольную область. Предопределен также ряд простых форм региона: эллиптические, прямоугольные, прямоугольные с закругленными краями, полигональные. Однако геометрическая простота не подразумевает простоты внутренней реализации региона. Непосредственно при программировании можно идти двумя путями: использовать функции WinAPI или библиотеку MFC. При работе на WinAPI для манипулирования регионами используется хэндл HRGN и соответствующая группа функций. MFC предоставляет для работы класс CRgn. Windows позволяет создавать регионы любой формы и степени сложности. Однако за это приходится платить относительно большой ценой – памятью, а в случае применения региона для создания окон – процессорным временем при перерисовке окна.

Применение регионов достаточно разнообразно:

Для начала рассмотрим наиболее простые и понятные функции для работы с регионами, а также базовые функции для работы с окнами. В принципе, последних всего две: SetWindowRgn и GetWindowRgn. В своем примере я пользуюсь лишь первой. Уже из названия понятно назначение этой функции – установка региона окна. Никакая часть окна не будет отображаться вне заданного региона. Первый параметр функции SetWindowRgn(HWND hWnd, HRGN hRgn, BOOL bRedraw) – хэндл hWnd указывает, к какому окну применяется эта функция, второй, hRgn, определяет новую форму окна, а параметр bRedraw в случае ненулевого значения обеспечивает перерисовку окна (система посылает сообщения WM_WINDOWPOSCHANGING и WM_WINDOWPOSCHANGED).

У функции SetWindowRgn есть ряд особенностей:

Рассмотрим пример. Код, приведенный ниже, взят из обработчика события WM_SIZE. Он показывает создание окна в виде эллипса.

Создание окна в виде эллипса (WinAPI)

// где-то в программе
HRGN hRgn;    // регион

// в обработчике события WM_SIZE
RECT rc;
// получает прямоугольник окна
GetWindowRect(hWnd, &rc);

// так как необходимо задавать регион относительно левого верхнего угла окна,
// нужно его сместить так, чтобы верхний левый угол прямоугольника был (0, 0)
OffsetRect(&rc, - rc.left, - rc.top);
 
DeleteObject(hRgn);  // удаление ранее созданного региона
// создание региона в виде эллипса, ограниченного прямоугольником rc
hRgn = CreateEllipticRgnIndirect(&rc);

// установка созданного региона для окна и немедленная перерисовка этого окна
SetWindowRgn(hWnd, hRgn, TRUE);

Создание окна в виде эллипса (MFC)

// где-то в программе
CRgn m_rgn;  // регион

// в обработчике события WM_SIZE
CRect rc;
GetWindowRect(&rc);
rc -= rc.TopLeft();

m_rgn.DeleteObject();    // удаление ранее созданного региона
  
// создание региона в виде эллипса, ограниченного прямоугольником rc
m_rgn.CreateEllipticRgnIndirect(&rc);      

// установка созданного региона для окна и немедленная перерисовка этого окна
SetWindowRgn(m_rgn, TRUE);

В этом фрагменте регион создается функцией CreateEllipticRgnIndirect. Существует еще несколько аналогичных функций.

CreateRectRgnСоздание региона в виде прямоугольника
CreateRectRgnIndirectСоздание региона в виде прямоугольника, определенного структурой RECT
CreateEllipticRgnСоздание региона в виде эллипса
CreateEllipticRgnIndirectСоздание региона в виде эллипса, определенного структурой RECT
CreateRoundRectRgnСоздание региона в виде прямоугольника с закругленными краями

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

Создание прямоугольного окна с закругленными краями (WinAPI)

// где то в программе
HRGN hRgn;    // регион

// в обработчике события WM_SIZE
RECT rc;
// прямоугольник окна
GetWindowRect(hWnd, &rc);

OffsetRect(&rc,  - rc.left,  - rc.top);

DeleteObject(hRgn);

// создание региона в виде прямоугольника с закругленными краями
hRgn = CreateRoundRectRgn(
  rc.left, rc.top, rc.right, rc.bottom, 
  (rc.right - rc.left) / 2, (rc.bottom - rc.top) / 2);

// установка созданного региона для окна и немедленная перерисовка этого окна
SetWindowRgn(hWnd, hRgn, TRUE);

Создание прямоугольного окна с закругленными краями (MFC)

// где то в программе
CRgn m_rgn;  // регион

// в обработчике события WM_SIZE
CRect rc;
GetWindowRect(&rc);

rc -= rc.TopLeft();

m_rgn.DeleteObject();    // удаление ранее созданного региона

// создание региона в виде прямоугольника с закругленными краями
m_rgn.CreateRoundRectRgn(rc.left, rc.top, rc.right, rc.bottom, 
  rc.Width() / 2, rc.Height() / 2);

// установка созданного региона для окна и немедленная перерисовка окна
SetWindowRgn(m_rgn, TRUE);

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

После того, как для объекта CRgn задан регион, объект допускает изменение региона при помощи следующих методов:

CombineRgnУстанавливает регион эквивалентным объединению двух определенных CRgn-объектов
OffsetRgnСмещает регион на заданное количество точек по вертикали и/или горизонтали

Рассмотрим параметры функции CombineRgn(HRGN dest, HRGN src1, HRGN src2, int mode). Первый хэндл задает регион-“приемник” для результата объединения следующих двух регионов по правилу определяемому четвертым параметром mode:

Для облегчения комбинирования областей в файле 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)

Применение функции OffsetRgn уже было показано. Рассмотрим применение функции CombineRgn на примере создания окна в виде кольца с эллипсом посередине.

Демонстрация CombineRgn (WinAPI)

// внешняя граница кольца (создание большого эллипса)
DeleteObject(hRgn);
hRgn = CreateEllipticRgn(rc.left, rc.top, rc.right, rc.bottom);

// внутренняя граница кольца (создание эллипса немного меньшего размера –
// во временном буффере)
hHdrRgn = CreateEllipticRgn(  
  rc.left + (rc.right - rc.left) / 4,
  rc.top + (rc.bottom - rc.top) / 4, 
  rc.right - (rc.right - rc.left) / 4,
  rc.bottom - (rc.bottom - rc.top) / 4);

// создание кольцо путем “вырезания” в большом эллипсе отверстия
CombineRgn(hRgn, hRgn, hHdrRgn, RGN_XOR);

// создание маленького эллипса в центре (во временном буфере)
DeleteObject(hHdrRgn);
hHdrRgn = CreateEllipticRgn(
  (rc.right - rc.left) / 2 - (rc.right - rc.left) / 16,
  (rc.bottom - rc.top) / 2 - (rc.bottom - rc.top) / 16, 
  (rc.right - rc.left) / 2 + (rc.right - rc.left) / 16,
  (rc.bottom - rc.top) / 2 + (rc.bottom - rc.top) / 16 );

// объединение полученных регионов (кольца и маленького эллипса)
CombineRgn(hRgn, hRgn, hHdrRgn, RGN_OR);

Демонстрация CombineRgn (MFC)

// создание нового региона в виде эллипса 
m_rgn.DeleteObject();
m_rgn.CreateEllipticRgn(rc.left, rc.top, rc.right, rc.bottom);

// создание временного региона в виде эллипса, 
// который на четверть меньше, чем m_rgn
m_HdrRgn.DeleteObject();
m_HdrRgn.CreateEllipticRgn(rc.left + rc.Width() / 4, 
  rc.top + rc.Height() / 4, rc.right - rc.Width() / 4, 
  rc.bottom - rc.Height() / 4);

// вызов этого метода с параметром RGN_XOR 
// удаляет области пересечения регионов m_rgn и m_HdrRgn, 
// помещая результат в m_rgn. Т. о. в m_rgn у нас останется лишь узкое кольцо
m_rgn.CombineRgn(&m_rgn, &m_HdrRgn, RGN_XOR);

// в промежуточном буфере создается маленький эллипс
m_HdrRgn.DeleteObject();
m_HdrRgn.CreateEllipticRgn(rc.Width() / 2 - rc.Width() / 16, 
  rc.Height() / 2 - rc.Height() / 16,rc.Width() / 2 + rc.Width() / 16, 
  rc.Height() / 2 + rc.Height() / 16 );

// который в простом объединении (параметр RGN_OR) с m_rgn даст искомую фигуру
m_rgn.CombineRgn(&m_rgn, &m_HdrRgn, RGN_OR);

// установка созданного региона для окна и немедленная перерисовка этого окна
SetWindowRgn(m_rgn, TRUE);

Прокомментировать текст программы лучше всего схемами:


Рисунок 1.


Рисунок 2. Вызов функции CombineRgn с параметром RGN_OR.

Как было обещано, рассмотрим применение функции CreatePolygonRgn.

Создание окна в виде забора (WinAPI)

// Точки для создания элемента ("доска") региона ("забор"). 
// Имея одну “доску”, мы затем “размножим” ее в цикле
POINT pnt[5];

pnt[0].x = rc.left;
pnt[0].y = rc.bottom;
pnt[1].x = rc.left;
pnt[1].y = rc.top + (rc.bottom - rc.top) * 0.75;
pnt[2].x = rc.left + (rc.right - rc.left) / 8;
pnt[2].y = rc.top;
pnt[3].x = rc.left + (rc.right - rc.left) / 4;
pnt[3].y = rc.top + (rc.bottom - rc.top) * 0.75;
pnt[4].x = rc.left + (rc.right - rc.left) / 4;
pnt[4].y = rc.bottom;

//создадим первый элемент ("доску")
DeleteObject(hRgn);
hRgn = CreatePolygonRgn(pnt, 5, ALTERNATE);

//добавим еще три "доски"...
for(n = 0; n < 3; n ++)
{
  //каждый раз смещая все пять точек доски на четверть размера окна
  for(k=0; k < 5; k ++)
    pnt[k].x += (rc.right - rc.left) / 4;

  //создавая новую "доску"
  hHdrRgn = CreatePolygonRgn(pnt, 5, ALTERNATE);

  //и добавляя ее к уже сщуствующим
  CombineRgn(hRgn, hRgn, hHdrRgn, RGN_OR);
  
  DeleteObject(hHdrRgn);  //удаление промежуточного объекта
}

// установка созданного региона для окна и немедленная перерисовка этого окна
SetWindowRgn(m_rgn, TRUE);

Создание окна в виде забора (MFC)

// Точки для создания элемента ("доска") региона ("забор"). 
// Имея одну “доску”, мы затем “размножим” ее в цикле
CPoint pnt[5];
pnt[0].x = rc.left;
pnt[0].y = rc.bottom;
pnt[1].x = rc.left;
pnt[1].y = rc.top + rc.Height() * 0.75;
pnt[2].x = rc.left + rc.Width() / 8;
pnt[2].y = rc.top;
pnt[3].x = rc.left + rc.Width() / 4;
pnt[3].y = rc.top + rc.Height() * 0.75;
pnt[4].x = rc.left + rc.Width() / 4;
pnt[4].y = rc.bottom;

//создадим первый элемент ("доску")
m_rgn.DeleteObject();
m_rgn.CreatePolygonRgn(pnt, 5, ALTERNATE);

//добавим еще три "доски"...
for(UINT n = 0; n < 3; n++)
{
  // каждый раз смещая все пять точек доски на четверть размера окна
  for(UINT k=0; k < 5; k +  + )
    pnt[k].x += rc.Width() / 4;

  //создавая новую "доску"
  m_HdrRgn.CreatePolygonRgn(pnt, 5, ALTERNATE);

  //и добавляя ее к уже существующим
  m_rgn.CombineRgn(&m_rgn, &m_HdrRgn, RGN_OR);

  m_HdrRgn.DeleteObject();
}

// установка созданного региона для окна и немедленная перерисовка этого окна
SetWindowRgn(m_rgn, TRUE);

Результат работы этого фрагмента будет таков:


Рисунок 3.

Использование параметров ALTERNATE = 1 или WINDING = POLYFILL_LAST = 2 в данном случае не имеет значения. Эти определения играют роль в случае сложных пересекающихся регионов. При использовании этой функции следует обязательно учитывать направление обхода. Классическим примером, демонстрирующим различия между этими параметрами, является создание региона в виде звезды. При одном и том же передаваемом массиве вершин мы получаем разные регионы:


Рисунок 4

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

ExtCreateRegion
(CreateFromData – MFC)
Создает новый регион из передаваемого региона и данных о преобразовании, определяемых структурой XFORM
GetRegionDataЗаполняет буфер данными, описывающими регион

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

Поля структуры XFORM
ДействиеeM11eM12eM21eM22
ПоворотКосинус угла поворотаСинус угла поворотаОтрицательный синус угла поворотаКосинус угла поворота
МасштабированиеГоризонтальный коэффициентВертикальный коэффициент
СмещениеГоризонтальный коэффициентВертикальный коэффициент
ОтображениеГоризонтальный коэффициентВертикальный коэффициент

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

Трансформации регионов (WinAPI)

// трансформации
XFORM xf, xf2;

// буфер для хранения данных описывающих первый "луч"
// здесь вызов GetRegionData(HRGN, 0, NULL) с “нулями” 
// возвращает число байт, необходимых для хранения информации 
// о регионе. В данной статье содержимое структуры LPRGNDATA не имеет 
// значения. Имеет значение то, что она представляет определенный регион.
LPRGNDATA lpRgnData;

// прямоугольник описанный вокруг региона
RECT rt; 

//массив точек для создания первого "луча"
POINT pnt[4];
pnt[0].x = rc.left;
pnt[0].y = rc.top + (rc.bottom - rc.top) / 2;
pnt[1].x = rc.left + (rc.right - rc.left) * 3 / 4;
pnt[1].y = rc.top + (rc.bottom - rc.top) * 3 / 4;
pnt[2].x = rc.left + (rc.right - rc.left) / 2;
pnt[2].y = rc.top + (rc.bottom - rc.top) / 2;
pnt[3].x = rc.left + (rc.right - rc.left) * 3 / 4;
pnt[3].y = rc.bottom - (rc.bottom - rc.top) * 3 / 4;

// создадим первый "луч"
DeleteObject(hRgn);
hRgn = CreatePolygonRgn(pnt, 4, ALTERNATE);
_ASSERT(hRgn);
    
//буфер для хранения данных описывающих первый "луч"
lpRgnData = GlobalAlloc(GMEM_FIXED, 
  sizeof(RGNDATA) * GetRegionData(hRgn, 0, NULL));
_ASSERT(lpRgnData);

//получение данных
GetRegionData(hRgn, GetRegionData(hRgn, 0, NULL), lpRgnData);
    
// в данном случае XFORM описывает зеркальное 
// отображение относительно оси ординат
xf.eM11 = - 1; // 
xf.eM22 = 1; // 
xf.eM12 = xf.eM21 = 0;  // 
xf.eDx  = xf.eDy = 0;  // 

// создание второго "луча" с использованием данные первого и структуры,
// описывающей необходимые изменения
hHdrRgn = ExtCreateRegion(&xf, GetRegionData(hRgn, 0, NULL), lpRgnData);
_ASSERT(hHdrRgn);

// смещение второго "луча" (т. к. ось ординат проходит через
// верхний левый угол)
OffsetRgn(hHdrRgn, rc.right - rc.left, 0);

// объединение первого и второго "лучей"
CombineRgn(hRgn, hRgn, hHdrRgn, RGN_OR);

// удаление промежуточного буфера
GlobalFree(lpRgnData);  

// еще буфер для хранения данных о двух первых "лучах"
lpRgnData = GlobalAlloc(GMEM_FIXED, 
  sizeof(RGNDATA) * GetRegionData(hRgn, 0, NULL));

// получение данных
GetRegionData(hRgn, GetRegionData(hRgn, 0, NULL), lpRgnData);

// здесь структура XFORM определяет поворот относительно 
// центра на 180 градусов
xf.eDx = xf.eDy = 0;    
xf.eM11 = xf.eM22 = 0;  //
xf.eM12 = 1;      //
xf.eM21 = -1;
// необходимо для масштабирования повернутых лучей.
xf2.eDx = xf2.eDy = 0;
xf2.eM21 = xf2.eM12 = 0;

// Так как в общем случае окно не квадратное, а прямоугольное
xf2.eM11 = (float)(rc.right - rc.left) / (rc.bottom - rc.top);
xf2.eM22 = (float)(rc.bottom - rc.top) / (rc.right - rc.left); 

// объединение двух трансформаций, создание повернутых и 
// масштабированных "вертикальных лучей"
CombineTransform(&xf, &xf, &xf2);  
DeleteObject(hHdrRgn);
hHdrRgn = ExtCreateRegion(&xf, GetRegionData(hRgn, 0, NULL), lpRgnData);

//удаление промежуточного буфера
GlobalFree(lpRgnData);  

// сместим полученные "вертикальные лучи", это также связано
// с положением центра координат в верхнем левом углу
GetRgnBox(hHdrRgn, &rt);
OffsetRgn(hHdrRgn, - rt.left + (rc.left + (rc.right - rc.left) / 2 
  - (rt.right - rt.left) / 2),  - rt.top + (rc.top));

//получение "звезды"
CombineRgn(hRgn, hRgn, hHdrRgn, RGN_OR);

//очистим дополнительный регион 
DeleteObject(hHdrRgn);

Трансформации регионов (MFC)

//создание окна в виде восьмиконечной звезды
//массив точек для создания первого "луча"
CPoint pnt[4];
pnt[0].x = rc.left;
pnt[0].y = rc.top + rc.Height() / 2;
pnt[1].x = rc.left + rc.Width() * 3 / 4;
pnt[1].y = rc.top + rc.Height() * 3 / 4;
pnt[2].x = rc.left + rc.Width() / 2;
pnt[2].y = rc.top + rc.Height() / 2;
pnt[3].x = rc.left + rc.Width() * 3 / 4;
pnt[3].y = rc.bottom - rc.Height() * 3 / 4;

// создадим первый "луч"
m_rgn.DeleteObject();
VERIFY(m_rgn.CreatePolygonRgn(pnt, 4, ALTERNATE));
    
// Буфер для хранения данных описывающих первый "луч".
// Здесь вызов GetRegionData(0, NULL) с параметром NULL возвращает число 
// байт, необходимых для хранения информации о регионе. Для данной статьи
// содержимое структуры LPRGNDATA не имеет значения. Имеет значение то,
// что она представляет определенный регион.
LPRGNDATA lpRgnData = new RGNDATA[m_rgn.GetRegionData(0, NULL)];
VERIFY(lpRgnData);
    
//получение данных – заполнение буфера
VERIFY(m_rgn.GetRegionData(lpRgnData, m_rgn.GetRegionData(0, NULL)));
    

// Структура, определяющая трансформацию массива данных, 
// описывающего регион. В данном случае на описывает зеркальное
// отображение относительно оси ординат.
XFORM xf; 
xf.eM11 =  - 1; 
xf.eM22 = 1; //о
xf.eM12 = xf.eM21 = 0; 
xf.eDx  = xf.eDy  = 0; 
            
//создание второго "луча" с использованием данных первого и структуры xf,
//описывающей необходимые изменения
m_HdrRgn.DeleteObject();
VERIFY(m_HdrRgn.CreateFromData(&xf, m_rgn.GetRegionData(0, NULL), lpRgnData));

//смещение второго "луча" (т. к. ось ординат проходит через
//верхний левый угол)
m_HdrRgn.OffsetRgn(rc.Width(), 0);

//объединение первого и второго "лучей"
VERIFY(m_rgn.CombineRgn(&m_rgn, &m_HdrRgn, RGN_OR));

delete lpRgnData; //удаление промежуточного буфера
    
//еще буфер для хранения данных о двух первых "лучах"
lpRgnData = new RGNDATA[m_rgn.GetRegionData(0, NULL)];
VERIFY(lpRgnData);
    
//получение данных
VERIFY(m_rgn.GetRegionData(lpRgnData, m_rgn.GetRegionData(0, NULL)));
    
xf.eDx = xf.eDy = 0; //здесь структура XFORM
xf.eM11 = xf.eM22 = 0; //определяет поворот
xf.eM12 = 1; //относительно центра
xf.eM21= - 1; //на 180 градусов
    
XFORM xf2; //еще одна трансформация
xf2.eDx = xf2.eDy = 0; // необходимая для масштабирования
xf2.eM21 = xf2.eM12 = 0; // повернутых лучей.Так как в общем случае
xf2.eM11 = (float)rc.Width() / rc.Height(); // окно не квадратное,
xf2.eM22 = (float)rc.Height() / rc.Width(); // а прямоугольное

// структура, определяющая трансформацию массива данных, описывающего регион
// теперь использование xf будет эквивалентно последовательному 
// использованию, определенных ранее двух структур XFORM
VERIFY(CombineTransform(&xf, &xf, &xf2));
    
// и создание повернутых и отмасштабированных "вертикальных лучей" 
m_HdrRgn.DeleteObject();
VERIFY(m_HdrRgn.CreateFromData(&xf, m_rgn.GetRegionData(0, NULL), lpRgnData));
  
delete lpRgnData; //удаление промежуточного буфера

// сместим полученные "вертикальные лучи", это так же связано
// с положением центра координат в верхнем левом углу
CRect rt; m_HdrRgn.GetRgnBox(&rt);
m_HdrRgn.OffsetRgn( - rt.left + (rc.left + rc.Width() / 2 - rt.Width() / 2), 
  - rt.top + (rc.top));

//получение "звезды"
VERIFY(m_rgn.CombineRgn(&m_rgn, &m_HdrRgn, RGN_OR));

// установка созданного региона для окна и немедленная 
// перерисовка этого окна
SetWindowRgn(m_rgn, TRUE);

Используемая в листинге функция

GetRegionData(HRGN hRgn, DWORD dwCount, LPRGNDATA lpRgnData);

заполняет буфер lpRgnData данными о регионе hRgn. Параметр dwCount передает количество байтов, необходимых для заполнения. Это количество можно узнать, если вызвать эту функцию с параметром dwCount, равным нулю. В этой статье не рассматривается содержимое структуры RGNDATA, нужное в очень редких случаях.

Считается, что использование структуры XFORM сопряжено с координатными преобразованиями, которые работают только на платформе NT 3.1 и выше. К использованию регионов для создания окон координатные преобразования практически никакого отношения не имеют. Исключение составляет функция CombineTransform, которая осуществляет объединение двух трансформаций и действительно работает только на NT 3.1 и выше. Для того, чтобы данный код исполнялся на платформе Win 9x, можно обойтись применением двух последовательных трансформаций без объединения их в одной.

СОВЕТ

Если используется структура XFORM, то никакие макросы и отладчики не заменят лист бумаги и карандаш.

Кроме того, необходимо помнить о немного нетрадиционной системе координат для окна (центр – левый верхний угол, направление вертикальной оси - вниз). Однако это можно изменить, для чего в Win API существует целый класс функций для координатных преобразований.

В результате выполнения этой части программы на экране появится окно изображенное на рисунке 5.


Рисунок 5

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


Эта статья опубликована в журнале RSDN Magazine #1-2003. Информацию о журнале можно найти здесь
    Сообщений 3    Оценка 56        Оценить