Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Основы программирования для информатиков и инженеров. Часть 2 Прикладное программирование в Windows.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
603.45 Кб
Скачать

9.5. Режимы отображения и области вывода

 

Единицами измерения координат при выводе текста и графики в Windows являются логические единицы. При выводе информации на конкретное устройство логические единицы преобразуются в физические (пиксели). Процесс преобразования единиц измерения из логических в физические определяется теку­щим режимом отображения (mappind mode). По умолчанию одна логическая единица равна одному пикселю. Однако, изменив режим отображения, можно изменить соотношение логических и физических единиц.

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

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

 

Установка режима отображения. Для установки текущего режима отображения (mappind mode) используется функция SetMapMode():

 

int SetMapMode(HDC hdc, int mode);

 

Рис. 9.2. Вывод графических объектов:

а – линии, б – прямоугольники, в – эллипсы

 

Дескриптор контекста устройства задается параметром hdc. Параметр mode опре­деляет задаваемый режим отображения и может принимать одно из следующих значений (табл. 9.3):

 

Таблица 9.3

Текущий режим отображения (mappind mode)

 

Макрос

Режим отображения

1

MM_ANISOTROPIC

Режим логических единиц, определенных программистом, с различным масштабированием по осям координат

2

MM_HIENGLISH

Одна логическая единица равна 0,001 дюйма

3

MM_HIMETRIC

Одна логическая единица равна 0,01 миллиметра

4

MM_ISOTROPIC

Режим логических единиц, определенных программистом, с эквивалентным масштабированием по осям координат (соотношение координат по различным осям устанавли­вается один к одному)

5

MM_LOMETRIC

Одна логическая единица равна 0,1 миллиметра

6

MM_LOENGLISH

Одна логическая единица равна 0,01 дюйма

7

ММ_ТЕХТ

Одна логическая единица равна 1 пикселю

8

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.