№ | Макрос | Режим отображения |
MM_ANISOTROPIC | Режим логических единиц, определенных программистом, с различным масштабированием по осям координат | |
MM_HIENGLISH | Одна логическая единица равна 0,001 дюйма | |
MM_HIMETRIC | Одна логическая единица равна 0,01 миллиметра | |
MM_ISOTROPIC | Режим логических единиц, определенных программистом, с эквивалентным масштабированием по осям координат (соотношение координат по различным осям устанавливается один к одному) | |
MM_LOMETRIC | Одна логическая единица равна 0,1 миллиметра | |
MM_LOENGLISH | Одна логическая единица равна 0,01 дюйма | |
ММ_ТЕХТ | Одна логическая единица равна 1 пикселю | |
MM_TWIPS | Одна логическая единица равна 1/12 точки принтера, т.е. примерно 1/1440 дюйма |
Функция SetMapMode() возвращает значение, соответствующее прежнему режиму отображения, или нуль в случае возникновения ошибки. По умолчанию устанавливается режим ММ_ТЕХТ.
Существует несколько причин для изменения режима отображения в программе. Во-первых, Вам может понадобиться, чтобы размеры объектов, отображаемых программой, соответствовали размерам реальных физических объектов, – в этом случае нужно установить один из режимов, соответствующих реальным метрическим единицам, например MM_LOMETRIC. Во-вторых, Вы можете пожелать задействовать в своей программе те единицы измерения, которые используются для объектов реального мира. В-третьих, может возникнуть необходимость изменить масштаб изображения (увеличить или уменьшить его, сохраняя или изменяя при этом пропорции объектов). И, наконец, может потребоваться установить соотношение размеров по осям X и Y один к одному; в последнем случае каждая логическая единица по оси X будет иметь тот же физический размер, что и логическая единица по оси Y.
Замечание: Изменение режима отображения изменяет способ преобразования логических единиц координат в физические (пиксели).
Определение логических размеров окна. Режимы отображения MM_ISOTROPIC и MM_ANISOTROPIC позволяют задать размеры окна в логических единицах. Задав один из этих режимов, необходимо также определить размеры окна (поскольку режимы MM_ISOTROPIC и MM_ANISOTROPIC используются только для программно определенных логических единиц, физические размеры Окна остаются неопределенными до тех пор, пока Вы не зададите их явно). Для задания размеров окна (extents) используется функция API SetWindowExtEx(), имеющая следующий прототип:
BOOL SetWindowExtEx(HDC hdc, int Xextent, int Yextent, LPSIZE size);
Параметр hdc содержит дескриптор контекста устройства. Параметры Xextent и Yextent задают размеры (extents) окна по горизонтали и вертикали в логических единицах. Предыдущие размеры окна копируются в структуру типа SIZE, указатель на которую задается параметром size. Если этот параметр равен NULL, предыдущие размеры окна не возвращаются. Функция возвращает ненулевое значение в случае успешного завершения либо нуль при возникновении ошибки. Вызов функции SetWindowExtEx() имеет смысл только в том случае, если установлены режимы отображения MM_ISOTROPIC и MM_ANISOTROPIC.
Структура SIZE определяется следующим образом:
typedef struct tagSIZE
{
LONG ex; // Ширина
LONG су; // Высота
} SIZE;
Заметьте, что, изменяя логические размеры окна, Вы не изменяете его физические размеры на экране: Вы просто определяете размеры окна в установленных Вами логических единицах (или, точнее, определяете соотношение логических единиц, используемых окном, и физических единиц – пикселей, используемых устройством отображения). Так, одно и то же реальное окно может иметь логические размеры 100 на 100 или 50 на 75. Разница будет только в соотношении логических единиц и пикселей при выводе графических объектов.
Определение области вывода. Как уже упоминалось, область вывода (viewport) – это область, в которую физически выводится информация. Размеры области вывода можно определить при помощи функции SetViewportExtEx():
BOOL SetViewportExtEx(HOC hdc, int Xextent, int Yextent, LPSIZE size};
Здесь параметр hdc задает дескриптор контекста устройства, а параметры Xextent и Yextent – размеры области вывода по горизонтали и вертикали в пикселях. Функция возвращает ненулевое значение при нормальном завершении и нуль при возникновении ошибки. Прежние размеры области вывода возвращаются в структуре SIZE, указатель на которую задается параметром size. Если этот указатель равен NULL, прежние размеры области вывода игнорируются. Вызов функции SetViewportExtEx() имеет смысл только после установки режима отображения MM_ISOTROPIC или MM_ANISOTROPIC.
Можно установить любые размеры для области вывода, поэтому она может быть и больше, и меньше окна. Для режима отображения MM_TEXT, устанавливаемого по умолчанию, размер области вывода всегда равен размеру окна.
Вывод программы автоматически отображается из контекста устройства окна (в логических единицах) в область вывода (в пикселях) и соответственно масштабируется. Поэтому, изменяя размеры области вывода, Вы изменяете видимые размеры отображаемых в ней объектов. Таким образом, при увеличении размеров области вывода содержимое ее тоже увеличится и, наоборот, – при уменьшении размеров области вывода ее содержимое уменьшится. Это будет показано в следующем примере.
Задание начала области вывода. По умолчанию область вывода начинается в точке 0,0 в рабочей области окна. Однако начальной можно сделать другую точку при помощи функции SetViewportOrgEx(), имеющей следующий прототип:
BOOL SetViewportOrgEx(HDC hdc, int X, int Y, LPPOINT OldOrg);
Параметр hdc должен задавать дескриптор контекста устройства. Новая точка начала области вывода задается параметрами X и Y. Координаты прежнего начала области вывода записываются в структуру POINT, указатель на которую задается параметром OldOrg. Этот параметр может быть равен NULL, и тогда координаты предыдущей области вывода будут игнорироваться.
Изменение начала области вывода приводит к тому, что выводимая в окно информация будет отображаться в другом месте. В приведенном ниже примере будет показано, как это можно осуществить.
Пример 9-2. Приведенная ниже программа представляет собой расширенную версию программы предыдущего примера. В нее добавлены операции установки режима отображения, логических размеров окна и размеров области вывода. Программа устанавливает режим отображения MM_ANISOTROPIC, логические размеры окна 200´200 и размеры области вывода 10´10. При каждом выборе команды меню Размер размеры области вывода увеличиваются на 10, что влечет за собой увеличение изображения на экране. По команде Начало вывода координаты начала области вывода увеличиваются на 50 пикселей.
// Установка режимов вывода, размеров окна и области
// вывода
#include <Windows.h>
#include <String.h>
#include <Stdio.h>
#include "Graph.h"
LRESULT CALLBACK WindowFunc(HWND,UINT,WPARAM,LPARAM);
char szWinName[] = "МоеОкно"; // Имя класса окна
char str[255]; // Буфер строки вывода
int maxX, maxY; // Размеры экрана
int X=10, Y=10; // Размеры области вывода
int orgX=0, orgY=0; // Координаты области вывода
HDC memdc; // DC виртуального окна
HBITMAP hbit; // Растр - это виртуальное окно
HBRUSH hbrush, hOldbrush; // Дескрипторы кистей
HPEN hOldpen; // Дескриптор прежнего пера
HPEN hRedpen,hGreenpen,hBluepen,hYellowpen; // Перья
int WINAPI WinMain (HINSTANCE hThisInst,
HINSTANCE hPrevInst,
LPSTR lpszArgs,
int nWinMode)
{
HWND hwnd;
MSG msg;
WNDCLASS wcl;
HACCEL hAccel;
// Определить класс окна
wcl.hInstance=hThisInst; // Дескриптор приложения
wcl.lpszClassName=szWinName; // Имя класса окна
wcl.lpfnWndProc=WindowFunc; // Функция окна
wcl.style=0; // Стиль по умолчанию
wcl.hIcon=LoadIcon(NULL,IDI_APPLICATION); // Иконка
wcl.hCursor=LoadCursor(NULL,IDC_ARROW); // Курсор
wcl.lpszMenuName="MYMENU"; // Меню
wcl.cbClsExtra=0; // Без дополнительной
wcl.cbWndExtra=0; // информации
// Определить заполнение окна белым цветом
wcl.hbrBackground=
(HBRUSH)GetStockObject(WHITE_BRUSH);
if(!RegisterClass(&wcl)) // Зарегистр. класс окна
return 0;
// Создать окно
hwnd=CreateWindow(szWinName, // Имя класса
"Работа с графикой: режимы вывода",
WS_OVERLAPPEDWINDOW,// Стиль окна
CW_USEDEFAULT, // Х-координата
CW_USEDEFAULT, // Y-координата
CW_USEDEFAULT, // Ширина окна
CW_USEDEFAULT, // Высота окна
HWND_DESKTOP, // Нет родит. окна
NULL, // Нет меню
hThisInst, // Дескрип. приложения
NULL); // Без дополит. аргументов
// Загрузить акселераторы
hAccel=LoadAccelerators(hThisInst,"MYMENU");
ShowWindow(hwnd,nWinMode); // Показать окно и
UpdateWindow(hwnd); // перерисовать содержимое
// Запустить цикл обработки сообщений
while(GetMessage (&msg,NULL,0,0))
if(!TranslateAccelerator(hwnd,hAccel,&msg))
{
TranslateMessage(&msg); // Использ.Клавиатуры
DispatchMessage (&msg); // Возврат к Windows
}
return msg.wParam;
}
// Следующая функция вызывается операционной системой
// Windows и получает в качестве параметров сообщения
// из очереди сообщений данного приложения
LRESULT CALLBACK WindowFunc(HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT paintstruct;
switch(message)
{
case WM_CREATE: // Получаем размеры экрана
maxX=GetSystemMetrics(SM_CXSCREEN);
maxY=GetSystemMetrics(SM_CYSCREEN);
hdc=GetDC(hwnd); // Совмест. с окном растр
memdc=CreateCompatibleDC(hdc);
hbit=CreateCompatibleBitmap(hdc,maxX,maxY);
SelectObject(memdc,hbit);
hbrush=(HBRUSH)GetStockObject(WHITE_BRUSH);
SelectObject(memdc,hbrush);
PatBlt(memdc,0,0,maxX,maxY,PATCOPY);
// Создать новые перья
hRedpen =CreatePen(PS_SOLID,1,RGB(255,0,0));
hGreenpen=CreatePen(PS_SOLID,1,RGB(0,255,0));
hBluepen =CreatePen(PS_SOLID,1,RGB(0,0,255));
hYellowpen=
CreatePen(PS_SOLID,1,RGB(255,255,0));
hOldpen=(HPEN)SelectObject(memdc,hRedpen);
SelectObject(memdc,hOldpen); // Старое перо
ReleaseDC(hwnd,hdc);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_LINES:
// Вывести 2 черных пикселя
SetPixel(memdc,40,14,RGB(0,0,0));
SetPixel(memdc,40,15,RGB(0,0,0));
// Вывести прямую линию
LineTo(memdc,100,50);
MoveToEx(memdc,100,50,NULL);
// Установить зеленое перо
hOldpen=
(HPEN)SelectObject(memdc,hGreenpen);
LineTo(memdc,200,100);
// Установить желтое перо
SelectObject(memdc,hYellowpen);
LineTo(memdc,0,200);
// Установить синее перо
SelectObject(memdc,hBluepen);
LineTo(memdc,200,200);
// Установить красное перо
SelectObject(memdc,hRedpen);
LineTo(memdc,0,0);
// Вернуться к прежнему перу
SelectObject (memdc, hOldpen);
// Провести дугу
Arc(memdc,0,0,300,300,0,50,200,50);
// Показать линии, отрезающие дугу
MoveToEx(memdc,150,150,NULL);
LineTo(memdc,0,50);
MoveToEx(memdc,150,150,NULL);
LineTo(memdc,200,50);
InvalidateRect(hwnd,NULL,1);
break;
case ID_RECTANGLES:
// Прямоугольники не заполняются
hOldbrush=(HBRUSH)SelectObject(memdc,
GetStockObject(HOLLOW_BRUSH));
// Рисуем прямоугольники
Rectangle(memdc,50,50,300,300);
RoundRect(memdc,125,125,220,240,15,13);
// Выбираем красное перо
SelectObject(memdc,hRedpen);
Rectangle(memdc,100,100,200,200);
SelectObject(memdc,hOldpen);
// Вернуться к прежнему перу
// Восстановить прежнюю кисть
SelectObject(memdc,hOldbrush);
InvalidateRect(hwnd,NULL,1);
break;
case ID_ELLIPSES:
// Создаем синюю кисть
hbrush=CreateSolidBrush(RGB(0,0,255));
hOldbrush=
(HBRUSH)SelectObject(memdc,hbrush);
// Рисуем эллипсы с синим заполнением
Ellipse(memdc,50,200,100,280);
Ellipse(memdc,75,25,280,100);
// Рисуем красным пером, зеленой кистью
SelectObject(memdc,hRedpen);
DeleteObject(hbrush); // Удалить кисть
// Создать зеленую кисть
hbrush=CreateSolidBrush(RGB(0,255,0));
SelectObject(memdc,hbrush);
Ellipse(memdc,100,100,200,200);
// Рисуем сектор
Pie(memdc,200,200,340,340,225,200,
200,250);
SelectObject(memdc,hOldpen);
// Вернуться к прежнему перу
SelectObject(memdc,hOldbrush);
// Восстановить прежнюю кисть и удалить
DeleteObject(hbrush); // зеленую кисть
InvalidateRect(hwnd,NULL,1);
break;
case ID_SIZE:
X += 10;
Y += 10;
InvalidateRect(hwnd,NULL,1);
break;
case ID_ORG:
orgX += 10;
orgY += 10;
InvalidateRect(hwnd,NULL,1);
break;
case ID_RESET:
// Восстановить текущую позицию в 0,0
MoveToEx(memdc,0,0,NULL);
// Стереть изображение при помощи
// перерисовки фона
PatBlt(memdc,0,0,maxX,maxY,PATCOPY);
InvalidateRect(hwnd,NULL,1);
break;
case ID_HELP:
MessageBox(hwnd,
"F2: линии\n"
"FЗ: прямоугольники\n"
"F4: эллипсы\n"
"F5: увеличить область\n"
"F6: изменить координаты\n"
"F7: сброс",
"Работа с графикой",MB_OK);
break;
}
break;
case WM_PAINT: // Перерисовка окна
// Получить DC */
hdc=BeginPaint(hwnd,&paintstruct);
// Установить режим вывода, границы окна и
// области вывода
SetMapMode(hdc,MM_ANISOTROPIC);
SetWindowExtEx(hdc,200,200,NULL);
SetViewportExtEx(hdc,X,Y,NULL);
SetViewportOrgEx(hdc,orgX,orgY,NULL);
// Теперь копируем растр из памяти на экран
BitBlt(hdc,0,0,maxX,maxY,memdc,0,0,SRCCOPY);
// Освободить DC
EndPaint(hwnd,&paintstruct);
break;
case WM_DESTROY: // Завершение программы
// Удалить созданные перья
DeleteObject(hRedpen);
DeleteObject(hGreenpen);
DeleteObject(hBluepen);
DeleteObject(hYellowpen);
DeleteDC(memdc); // Удалить виртуальное окно
PostQuitMessage(0);
break;
default:
// Все сообщения, не обрабатываемые в данной
// функции, направляются на обработку по
// умолчанию
return DefWindowProc(hwnd,message,
wParam,lParam);
}
return 0;
}
Эта программа требует следующий файл ресурсов:
#include <Windows.h>
#include "Graph.h"
MYMENU MENU
{
MENUITEM "&Линии", ID_LINES
MENUITEM "&Прямоугольники", ID_RECTANGLES
MENUITEM "&Эллипсы", ID_ELLIPSES
MENUITEM "&Размер", ID_SIZE
MENUITEM "&Начало вывода", ID_ORG
MENUITEM "&Cбpоc", ID_RESET
MENUITEM "Помощь", ID_HELP
}
MYMENU ACCELERATORS
{
VK_F2, ID_LINES, VIRTKEY
VK_F3, ID_RECTANGLES, VIRTKEY
VK_F4, ID_ELLIPSES, VIRTKEY
VK_F5, ID_SIZE, VIRTKEY
VK_F6, ID_ORG, VIRTKEY
VK_F7, ID_RESET, VIRTKEY
VK_F1, ID_HELP, VIRTKEY
}
Результат работы этой программы представлен на рис. 9.3.
Рис. 9.3. Режимы вывод графических объектов: а – с уменьшением,
б – с увеличением, в – с изменением начала области вывода
Следует учесть, что файл определений Graph.h должен быть дополнен двумя константами, и иметь следующий вид:
#define ID_LINES 100
#define ID_RECTANGLES 101
#define ID_ELLIPSES 102
#define ID_SIZE 103
#define ID_ORG 104
#define ID_RESET 105
#define ID_HELP 106
В следующей главе мы вернемся к разговору об элементах управления, приступив к рассмотрению общих элементов управления в Windows.