    Работа с графикой 

    При работе в Windows в распоряжении программиста имеется богатый набор 
графических функций. Это и неудивительно, ведь Windows — графическая 
операционная система. Неожиданным может показаться лишь то, насколько тесно 
графика интегрирована с подсистемой вывода на экран. По сути, многое из того, 
что было рассказано в предыдущей главе о работе с текстом, применимо и для 
графики. Например, та же самая кисть, которая использовалась для заполнения 
фона окна, может быть использована и для заливки графического объекта. В этой 
главе мы познакомимся подробней с некоторыми графическими функциями. 
    Кроме того, в главе рассказывается о том, как управлять отображением 
графической информации в окне. В частности, рассматривается, как установить 
определенный режим отображения, как изменить систему логических координат окна 
и задать область вывода. Все это оказывает существенное влияние на 
представление графики и текста на экране. 

    213 

    Учтите, что в данной главе будут затронуты лишь некоторые основные вопросы 
работы с графикой. Графическая подсистема в Windows достаточно мощная, и, при 
желании, вы сможете в дальнейшем продолжить ее изучение самостоятельно. 

    Система графических координат 

    При работе с графикой используется та же система координат, что и при 
работе с текстом (см. главу 8). Это означает, что по умолчанию верхний левый 
угол окна имеет координаты (0,0), а логические единицы эквивалентны пикселям. 
Однако не следует забывать, что и система координат, и способ преобразования 
логических единиц в пиксели контролируются программистом, а следовательно, 
могут быть изменены. Мы рассмотрим этот вопрос в данной главе. 
    В Windows поддерживается так называемый текущий указатель (current 
pointer), положение которого используется и изменяется некоторыми графическими 
функциями. При запуске программы текущий указатель устанавливается в точку 
(0,0). Его местоположение на экране невидимо, т.е. никакой графический 
"курсор" появляться не будет. Текущий указатель обозначает лишь позицию в 
окне, с которой начинают выводить информацию некоторые графические функции. 

    Кисти и перья 

    В Windows графическая подсистема основывается на двух важнейших типах 
объектов: кистях (brushes) и перьях (pens). О стандартных кистях говорилось в 
главе 8. Вся эта информация применима и для графических функций. По умолчанию 
замкнутые графические фигуры, такие как прямоугольники и эллипсы, заполняются 
согласно цвету и стилю текущей кисти. Перья являются ресурсами, которые 
предназначены для рисования прямых линий и дуг, определяемых различными 
графическими функциями. По умолчанию установлено перо черного цвета толщиной 
одн;; пиксель. Но эти атрибуты можно, конечно, изменять. 
    В предыдущих главах работа велась с готовыми объектами. В этой главе мь: 
научимся создавать собственные кисти и перья. 

    Графические функции 

    В Windows определено несколько графических API-функций. В MFC они 
инкапсулируются классом CDC. В данном параграфе рассматриваются только 
основные т них. Следует помнить, что число вспомогательных графических функций 
очень велико, среди них есть и перегруженные версии тех функций, которые 
описываются! здесь. Вы сможете изучить их самостоятельно. 

    Отображение точки 

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

    COLORREF CDC::SetPixel(int X, int У, COLORREF Color); 

    *   Подразумевается, что точка на экране уже есть, а вы лишь меняете ее 
цвет. — Примеч. ред. 

    214                                    MFC: основы программирования 

    Здесь координаты точки задаются параметрами Л" и У, а ее цвет — параметром 
Color. Функция возвращает значение того цвета, который был установлен 
вдействительности, причем оно может отличаться от указанного в параметре 
Color, что связано с ограничениями, накладываемыми графическим устройством. В 
случае ошибки (или при указании координат, выходящих за пределы окна) 
возвращается -1. 
    Примечание: Для многих графических функций, включая SetPixel(), в MFC 
имеются перегруженные версии, в которых координаты задаются с помощью 
структуры типа POINT. Такими функциями удобнее пользоваться, если координаты 
заданы как один объект. 

    Рисование линий 

    Для отображения на экране линии используется функция LineTo(). Она рисует 
линию с помощью текущего пера. Прототип этой функции таков: 

    BOOL CDC::LineTo(int X, int У); 

    Линия рисуется от позиции текущего указателя до точки с координатами (X, 
Y). После этого текущий указатель также перемещается в точку (X, Y). При 
успешном завершении (т.е., когда линия нарисована) функция возвращает 
ненулевое значение, в противном случае возвращается ноль. 
    Некоторых может удивить, что функция LineToQ использует позицию текущего 
указателя в качестве начальной точки и что сам указатель при этом перемещается 
в конечную точку рисуемой прямой, а не остается на прежнем месте. Этому есть 
веская причина. Дело в том, что при построении линий очень часто следующая 
линия начинается с конца предыдущей. В такой ситуации использование функции 
LineToQ оказывается чрезвычайно эффективным, так как позволяет избежать 
повторения начальной пары координат. Кроме того, перед функцией LineToO всегда 
можно вызвать функцию MoveTo(), чтобы изменить позицию текущего указателя. 

    Изменение позиции текущего указателя 

    Упомянутая только что функция MoveToQ имеет такой прототип: 

    CPoint CDC::MoveTo(int X, int У); 

    Координаты новой позиции указателя задаются параметрами Х и Y. Функция 
возвращает координаты предыдущей позиции, указываемые в виде объекта типа 
CPoint, который содержит стандартную структуру POINT. Эта структура определена 
следующим образом: 

    typedef struct tagPOINT 
    ( 
    LONG x; 
    LONG у; 
    ) POINT 


    9. Работа с графикой                                              215 


    Рисование дуг 

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

    BOOL CDC::Arc(int upX, int црУ, int lowX, int lowY, 
    int startX, int startY, int endX, int endY) ; 

    Дуга задается двумя объектами. Во-первых, дуга является частью эллипса, 
который вписан в прямоугольник. Координаты верхнего левого угла прямоугольника 
(upX,upY), а правого нижнего — (lowX.lowY). Та часть эллипса, которая 
действительно будет отображена (т.е. сама дуга), начинается на пересечении с 
линией, проведенной из центра прямоугольника в точку с координатами 
(startX.startY), и заканчивается на пересечении с линией, проведенной из 
центра прямоугольника в точку с координатами (endX,endY). Дуга рисуется против 
часовой стрелки, начиная с первой точки пересечения (рис. 9.1). Функция Агс() 
возвращает ненулевое значение при успешном завершении и ноль — в противном случае. 


    Описанный прямоугольник 

    Рис. 9.1. Принцип построения эллипса функцией Ак() 


    Рисование прямоугольников 

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

    BOOL CDC::Rectangle(int UpX, int upY, int lowX, int lowY); 

    Координаты верхнего левого угла прямоугольника задаются параметрами upXи 
upY, а координаты правого нижнего угла — параметрами lowX и lowY. Функция 
возвращает ненулевое значение при успешном завершении и ноль — в противном 
случае. Нарисованная прямоугольная область автоматически заполняется с помощью 
текущей кисти. 

    216 MFC: основы программирования 

    Функция RoundRectQ позволяет нарисовать прямоугольник со скругленными 
углами. Ее прототип таков: 

    BOOL CDC::RoundRect(int upX, int upY, int lowX, int lowY, int curveX, int curveY); 

    Первые четыре параметра аналогичны параметрам функции Rectangle(). 
Параметры curveX и curveY задают ширину и высоту эллипса, определяющего дугу 
округления углов данного прямоугольника. Функция возвращает ненулевое значение 
при успешном завершении и ноль — в противном случае. Нарисованная 
прямоугольная область автоматически заполняется с помощью текущей кисти. 

    Рисование эллипсов и секторов 

    Функция Ellipse() предназначена для рисования эллипса или окружности с 
помощью текущего пера. Нужная нам версия этой функции имеет следующий прототип: 

    BOOL CDC::Ellipse(int upX, int upY, int lowX, int lowY) ; 

    Эллипс определяется координатами описанного вокруг него прямоугольника. 
Координаты верхнего левого угла этого прямоугольника задаются параметрами upX 
и upY, а координаты правого нижнего угла — параметрами lowX и lowY. Если 
необходимо нарисовать окружность (частный случай эллипса), то в качестве 
описанного прямоугольника должен быть задан квадрат. Функция EllipseQ 
возвращает ненулевое значение при успешном завершении и ноль — в противном 
случае. Нарисованный эллипс заполняется с помощью текущей кисти. 
    С понятием эллипса связано и понятие сектора. Сектор — это объект, 
включающий в себя дугу и две линии, проведенные от концов дуги к центру 
эллипса. Для рисования секторов предназначена функция Pie(). Ее прототип таков: 

    BOOL CDC::Pie(int upX, int црУ, int lowX, int lowY, int startX, int 
startY, int endX, int endY) ; 

    Назначение этих параметров точно такое же, что и у функции Агс(). Принципы 
построения дуги в обоих случаях аналогичны. Только в функции Pie() 
дополнительно рисуются две линии, формирующие вместе с дугой сектор. 
    Сектор рисуется с помощью текущего пера и заполняется с помощью текущей 
кисти. Функция Pie() возвращает ненулевое значение при успешном завершении и 
ноль — в противном случае. 

    Работа с перьями 

    Графические объекты рисуются с помощью текущего пера. В MFC перья 
описываются классом СРеп. По умолчанию устанавливается черное перо толщиной 1 
пиксель. Всего имеется три стандартных пера: черное, белое и нулевое (т.е. 
прозрачное). Их можно получить с помощью функции CreateStockObjectO, которая 
рассматривалась ранее. Со стандартными перьями связаны константы BLACK_PEN, 
WHITEJPEN и NULLJPEN, соответственно. 

    9. Работа с графикой                                              217 

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

    BOOL CPen::CreatePen(int StyJe, int Width, COLORREF Color); 

    Параметр Style определяет стиль создаваемого пера. Его значением должна 
быть одна из следующих констант (табл. 9.1): 

    Таблица 9.1. Стили перьев 

    Константа  Стиль пера

PSDASH 
PSDASHDOT
PSDASHDOTDOT
PSDOT 
PSJNSIDEFRAME 
PSNULL 
PSSOL1D	

Штриховая линия (состоящая из отрезков)
Штрихпунктирная линия (состоящая из последовательности точек и тире)
Штрихпунктирная линия (состоящая из последовательности "тире-точка-точка")
Пунктирная линия (состоящая из точек) 
Непрерывная линия, рисуемая в заданных пределах 
Прозрачное перо 
Непрерывная линия 

    Пунктирные и/или штриховые стили могут применяться только к перьям, 
имеющим толщину, равную единице. Стиль PS_INSIDEFRAME соответствует сплошному 
перу, которое располагается точно в пределах рисуемого объекта независимо от 
толщины самого пера. Например, если перо, имеющее стиль PSJNSIDEFRAME и 
толщину более единицы, используется для рисования прямоугольника, то наружная 
часть линии будет располагаться в пределах координат данного прямоугольника. 
(При использовании толстого пера другого стиля эта линия может частично 
выходить за границы объекта.) 
    Толщина пера в логических единицах задается параметром Width. Цвет пера 
задается параметром Color, имеющим тип COLORREF (он рассматривался в главе 8). 
В данной главе все цвета задаются с помощью функции RGB(). 
    Функция CreatePen() возвращает ненулевое значение при успешном завершении 
и ноль — в противном случае. 
    Иногда легче создавать перо непосредственно при объявлении объекта типа 
СРеп. Это делается с помощью конструктора класса СРеп, один из прототипов 
которого таков: 

    СРеп (int StyJe, int Width, COLORREF Color}; 

    Все параметры здесь имеют то же значение, что и в функции CreatePenQ. При 
возникновении ошибки конструктор СРеп() обращается к классу CResourceException. 
    После того как перо создано, оно должно быть выбрано в контекст устройства 
с помощью функции Select0bject(). 

    218                                    MFC: основы программирования 


    Создание пользовательских кистей 

    Пользовательские кисти создаются подобно перьям. Только они описываются 
классом CBrush. 
    Существуют различные стили кистей. Наиболее распространенной является 
сплошная кисть. Она создается с помощью функции CreateSolidBrush(), которая 
имеет такой прототип: 

    BOOL CBrush::CreateSolidBrush(COLORREF Color); 

    Цвет кисти задается параметром Color. Функция возвращает ненулевое 
значение при успешном завершении и ноль — в противном случае. 
    Класс CBrush также содержит конструктор, с помощью которого можно 
создавать сплошную кисть одновременно с объявлением объекта кисти. Прототип 
этого конструктора следующий: 

    CBrush(COLORREF Color); 

    При использовании такого конструктора создание и инициализация объекта 
кисти выполняются за один шаг. При возникновении ошибки конструктор CBrush() 
обращается к классу CResourceException. 
    После того как кисть создана, она должна быть выбрана в контекст 
устройства с помощью функции SelectObjectQ. 
    Предлагаем вам самостоятельно изучить другие типы кистей, такие как кисти 
со штриховкой и кисти с шаблоном заполнения, которые создаются с помощью 
функций CBnisli::CreateHatcliBnish() и CBrush::CreatePatternBrush(), соответственно. 

    Удаление кистей и перьев 

    Любая пользовательская кисть или перо, созданные в программе, до ее 
завершения должны быть удалены из памяти. Обычно об этом не приходится 
беспокоиться, так как данные объекты автоматически удаляются при вызове 
деструкторов соответствующих классов. Но можно выполнять это и программным 
путем с помощью | функции DeleteObjectQ. Помните, что нельзя удалить 
стандартный объект. Кроме того, удаляемый объект не должен находиться (т.е. 
быть выбранным) ни в одном контексте устройства. 

    Демонстрационная программа работы с графикой 

    В следующей программе демонстрируется работа с графическими функциями, 
которые мы рассмотрели. Для вывода информации на экран в программе 
используется метод виртуального окна, описанный в главе 8. Вся информация 
направляется в контекст области памяти, а при получении сообщения WMJPAINT 
копируется в физическое окно. (Это также позволяет автоматически обновлять 
содержимое окна при получении данного сообщения.) 

    i 9. Работа с графикой                                              219 


    Вот текст файла ресурсов программы: 
    ; GRAPH.RC 
    #include <afxres.h> ftinclude "ids.h" 
    GraphicsMenu MENU ( 
    POPUP "Graphics Demo" ( 
    MENUITEM "&Lines\tF2", IDM_LINES 
    MENUITEM "&Rectangles\tF3", IDM_RECTANGLES 
    MENUITEM "&Ellipses\tF4", IDM_ELLIPSES 
    MENUITEM "&Reset\tF5", IDM_RESET 
    MENUITEM "E&xit\tF6", IDM_EXIT 
    } 
    MENUITEM "SHelp", IDM_HELP > 
    GraphicsMenu ACCELERATORS ( 
    VK_F2, IDM_LINES, VIRTKEY 
    VK_F3, IDM_RECTANGLES, VIRTKEY 
    VK_F4, IDM_ELLIPSES, VIRTKEY 
    VK_F5, IDM_RESET, VIRTKEY 
    VK_F6, IDM_EXIT, VIRTKEY 
    VK_F1, IDM_HELP, VIRTKEY } 

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

    // IDS.H 
    ftdefine IDM_LINES       100 
    #define IDM_RECTANGLES 101 
    ftdefine IDM_ELLIPSES    102 
    #define IDM_ENLARGE     103 
    ftdefine IDM_ORG         104 
    itdefine IDM_RESET       105 
    #define IDM_EXIT        106 
    #define IDM_HELP        107 

    Вот текст файла GRAPH.H, содержащего объявления порождаемых классов: т 

    // GRAPH.H                                                           | 
    // Это класс для создания главного окна 
    class CMainWin : public CFrameWnd                                   J 
    (                                                                          
                                                     I 
    CDC m memDC; // контекст устройства, связанный с                  ' 
    // виртуальным окном 


    220                                     MFC: основы программирования 


    CBitmap m bmp; // образ виртуального окна 
    CBrush m bkbrush; // кисть для виртуального окна 
    // объявление перьев CPen m RedPen, m YellowPen, m GreenPen, m BluePen; 
    CPen m_01dPen; 
    public: 
    CMainWin () ; 
    afx_msg void OnPaintO; 
    afx_msg void OnLinesO; 
    afx_msg void OnRectangles(); 
    afx_msg void OnEllipses(); 
    afx msg void OnResetO; 
    afx_msg void OnExitO; 
    afx_msg void OnHelpO; 
    DECLARE_MESSAGE_MAP() 
    ); 
    // Это класс для создания приложения 
    class CApp : public CWinApp 
    ( 
    public: 
    BOOL Initlnstance (); 
    ); 

    Это текст самой программы: 

    // Работа с графикой 
    ftinclude <afxwin.h> 
    tinclude "graph.h" 
    tinclude "ids.h" 
    int maxX, maxY; // размеры экрана 
    CMainWin::CMainWin () 
    ( 
    Create(NULL, "Demonstrate Graphics", 
    WS_OVERLAPPEDWINDOW, rectDefault, 
    NULL, "GraphicsMenu") ; 
    // Загрузка таблицы быстрого доступа 
    if('LoadAccelTable("GraphicsMenu")) 
    MessageBox("Cannot Load Accelerators", "Error"); 
    maxX = GetSystemMetrics(SM_CXSCREEN) ; 
    maxY = GetSystemMetrics(SM_CYSCREEN) ; 
    CClientDC DC(this); 


    9. Работа с графикой                                              221 


    // создание образа виртуального окна 
    m memDC.CreateCompatibleDC(&DC); 
    m_bmp.CreateCon-ipatibleBitmap (&DC, maxX, maxY); 
    m_memDC.SelectObject(&m_bmp) ; 
    // выбор стандартного фона 
    m_bkbrush.CreateStockObject(WHITE_BRUSH) ; 
    m memDC.SelectObject(&m bkbrush); 
    // закраска фона виртуального окна 
    m_memDC.PatBlt(0, 0, maxX, maxY, PATCOPY); 
    // создание перьев 
    m_RedPen.CreatePen(PS_SOLID, 1, RGB(255, 0, 0)) ; 
    m_GreenPen.CreatePen(PS_SOLID, 2, RGB(0, 255, 0)); 
    m_BluePen.CreatePen(PS_SOLID, 3, RGB (О, О, 255)); 
    m_YellowPen.CreatePen<PS_SOLID, 4, RGB(255, 255, 0)) ; 
    m_01dPen.CreateStockObject(BLACK_PEN) ; 
    } 
    // Инициализация приложения 
    BOOL CApp::Initlnstance() 
    ( 
    m_pMainWnd = new CMainWin; 
    m_pMainWnd->ShowWindow(m nCmdShow) ; 
    m pMainWnd->UpdateWindow(); 
    return TRUE; 
    } 
    // Это очередь сообщений приложения 
    BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) 
    ON_MM_PAINT() 
    ON_COMMAND(IDM_LINES, OnLines) 
    ON_COMMAND(IDM_RECTANGLES, OnRectangles) 
    ON_COMMAND(IDM_ELLIPSES, OnEllipses) 
    ON_COMMAND(IDM_RESET, OnReset) 
    ON_COMMAND(IDM_EXIT, OnExit) 
    ON_COMMAND(IDM_HELP, OnHelp) 
    END_MESSAGE_MAP() 
    // Вывод на экран содержимого виртуального окна 
    afx rnsg void CMainWin : :OnPaint () 
    ( 
    CPaintDC DC (this) ; 
    DC.BitBlt(0, 0, maxX, maxY, &m_memDC, О, О, SRCCOPY) ; 
    } 
    // Обработка сообщения IDM_EXIT 
    afx msg void CMainWin::OnExit() 
    { 
    int response; 
    response = MessageBox("Quit the Program?", "Exit", MB_YESNO) ; 


    222                                     MFC: основы программирования 


    if (response == IDYES) 
    SendMessage (WM CLOSE); // завершение приложения 
    ) 
    // Обработка сообщения IDM_HELP 
    afx msg void CMainWin::OnHelp () 
    f 
    MessageBox("Graphics Demo", "Help"); 
    ) 
    // Отображение линий и точек 
    afx_msg void CMainWin::OnLines () 
    ( 
    //отображение четырех точек 
    m_memDC.SetPixel(40, 14, RGB (2 55, 0, 0)); 
    m_memDC.SetPixel(41, 14, RGB(0, 255, 0)); 
    m_memDC.SetPixel(42, 14, RGB(0, 0, 255)); 
    m_memDC.SetPixel(43, 14, RGB(0, 0, 0)) ; 
    mJnemDC.LineTo(100, 50); 
    m_iTiemDC. MoveTo (100, 50); 
    // изменение цвета пера на зеленое 
    m mernDC.SelectObject(&m GreenPen) ; 
    m^memDC.LineTo(200, 1007; 
    // изменение цвета пера на желтое 
    m memDC.SelectObject(&m YellowPen); 
    m_memDC.LineTo(0, 200); 
    // изменение цвета пера на синее 
    m memDC.SelectObject(&m BluePen) ; 
    m^memDC.LineTo (200, 200У"; 
    // изменение цвета пера на красное 
    m_memDC.SelectObject(&m_RedPen) ; 
    ra_memDC.LineTo(0, 0) ; 
    m_memDC.LineTo(100, 50); 
    m memDC.MoveTo (О, О); 
    m^memDC.LineTo (100, 250); 
    m_memDC.MoveTo (0, 0) ; 
    m_memDC.LineTo (100, 350); 
    // восстановление первоначального пера 
    m_memDC.SelectObject(&m_01dPen) ; 
    m_memDC.Arc(О, О, 300, 300, 0, 50, 200, 0); 
    //отображение пересекающихся линий, которые определяют дугу 
    m_memDC.MoveTo (150, 150); 
    m_mem.DC.LineTo (0, 50); 
    m_memDC.MoveTo (150, 150); 
    m_memDC.LineTo (200, 0) ; 


    9. Работа с графикой                                               223 


    InvalidateRect(NULL) ; 
    i 
    // Отображение прямоугольников 
    afx_msg void CMainWin::OnRectangles() 
    ( 
    CBrush HollowBrush; 
    // создание "пустого" заполнения 
    HollowBrush.CreateStockObject(HOLLOW_BRUSH) ; 
    m_memDC.SelectObject(&HollowBrush) ; 
    // отображение нескольких прямоугольников 
    m__memDC.Rectangle(50, 50, 300, 300); 
    m_memDC.RoundRect(125, 125, 220, 240, 15, 13);                   ; 
    // использование красного пера 
    m_memDC.SelectObject(&m_RedPen) ; 
    m_memDC.Rectangle(100, 100, 200, 200); 
    m_memDC.SelectObject(&m_01dPen); // восстановление прежнего пера 
    // восстановление прежней кисти 
    m memDC.SelectObject(&m bkBrush); 
    InvalidateRect(NULL) ; 
    } 
    // Отображение эллипсов 
    afx_msg void CMainMin::OnEllipses () 
    ( 
    CBrush Brush; 
    // создание синей кисти 
    Brush.CreateSolidBrush(RGB(0, 0, 255)); 
    m niernDC . SelectObject (&Brush) ; 
    // отображение эллипсов, заполненных синим цветом 
    ni_memDC.Ellipse(50, 200, 100, 280); 
    m_memDC.Ellipse(75, 25, 280, 100); 
    // использование красного пера 
    m memDC.SelectObject(&m RedPen) ; 
    // создание зеленой кисти 
    Brush.DeleteObject (); // удаление синей кисти 
    Brush.CreateSolidBrush(RGB(0, 255, 0)); 
    m_memDC.SelectObject(&Brush); // выбор зеленой кисти 
    mJnemDC.Ellipse(100, 100, 200, 200); 
    // отображение сектора 
    m_memDC.Pie(200, 200, 340, 340, 225, 200, 200, 250); 
    m_memDC.SelectObject(&m_01dPen); // восстановление прежнего пера 
    m memDC.SelectObject(&m bkBrush); // восстановление прежней кисти 


    224                                     MFC: основы программирования 


    InvalidateRect(NULL) ; 
    ) 
    // Очистка окна 
    afx_msg void CMainWin::OnReset() 
    ( 
    m_memDC.PatBlt(0, 0, maxX, maxY, PATCOPY); 
    InvalidateRect(NULL) ; 
    CApp App; // создание экземпляра приложения 

    В программе имеется меню, с помощью которого можно выводить на экран 
линии, прямоугольники и эллипсы. Можно также обновлять окно, при этом его 
содержимое стирается и координаты текущего указателя обнуляются. Пример работы 
программы показан на рис. 9.2. 

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

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

    Задание режима отображения 

    Для задания текущего режима отображения используется функция SetMapMode(), 
которая имеет такой прототип: 
    virtual int CDC::SetMapMode(int Mode); 


    9. Работа с графикой                                              225 

    Рис. 9.2. Демонстрация возможностей графических функций 


    226                                          MFC: основы программирования 

    Параметр Mode определяет новый режим и может принимать одно из следующих 
значений (табл. 9.2): 

    Таблица 9.2. Режимы отображения 

    Константа 	Режим

MMANISOTROPIC
MMJ-HENGLISH 
MMHI METRIC 
MMJSOTROPIC
MMLO METRIC 
MMLOENGLISH 
MMTEXT 
MMJTWIPS
Логические единицы преобразуются в единицы, задаваемые программистом, с 
произвольным соотношением масштабов осей координат.
Одной логической единице соответствует 0,001 дюйма. 
Одной логической единице соответствует 0,01 миллиметра.
Логические единицы преобразуются в единицы, задаваемые программистом, с 
одинаковым масштабом для обеих осей координат. (Это означает, что 
соотношение масштабов осей равно единице.)
Одной логической единице соответствует 0,1 миллиметра. 
Одной логической единице соответствует 0,01 дюйма. 
Одной логической единице соответствует один пиксель.
Одной логической единице соответствует 1/12 точки принтера, или 
приблизительно 1/1440 дюйма. 

    Функция SetMapMode() возвращает значение предыдущего установленного режима 
или ноль, если возникла ошибка. 
    По умолчанию установлен режим ММ_ТЕХТ. Есть несколько причин, по которым 
может возникнуть необходимость в изменении текущего режима отображения. 
Например, если в программе весь вывод организуется в физических единицах, 
можно установить один из режимов реальных единиц, такой как MM_LOMETRIC. 
Во-вторых, в программе можно установить собственные логические единицы, 
которые наилучшим образом соответствуют тому, что выводится в данной 
программе. В-третьих, можно менять масштаб выводимого изображения (т.е. 
увеличивать или уменьшать его). И наконец, можно задавать равный масштаб для 
осей Х и Y* В этом режиме каждому отрезку, отложенному вдоль оси Х экрана, 
соответствует такой же отрезок, отложенный вдоль оси Y. 
    Помните: При изменении режима отображения меняется способ преобразования 
логических единиц в физические (пиксели). 

    Логические размеры окна 

    При выборе режима MM_ANISOTROPIC или MMJSOTROPIC появляется возможность 
задавать размеры окна в логических единицах. По сути, если установлен один из 
этих режимов, то размеры окна обязательно должны быть заданы. (Поскольку в 
этих режимах работа ведется с программно задаваемыми единицами, то размеры 
окна фактически остаются неопределенными до тех пор, пока они не будут 
определены самим программистом.) Для задания размеров окна по осям Х и Y 
необходимо использовать функцию SetWindowExt(). Нужная нам версия этой функции 
имеет такой прототип: 

    virtual CSize CDC::SetWindowExt(int Xextent, int Yextent} ; 

    *  Дело в том, что в реальных мониторах такое соотношение никогда не 
встречается. — Примеч. ред. 


    9, Работа с графикой                                              227 

    Параметры Xextent и Yextent определяют новый горизонтальный и вертикальный 
размер окна в логических единицах. Функция возвращает предыдущие размеры окна 
в объекте типа CSize (который включает стандартную структуру SIZE). Выполнение 
функции возможно только в режимах MM_ANISOTROPIC и MM_ISOTROPIC. 
    Следует помнить, что изменение логических пропорций окна не означает 
изменения его физических размеров. Они остаются прежними, меняется лишь размер 
окна в логических единицах. (Точнее говоря, изменяется соотношение между 
логическими единицами, используемыми окном, и физическими единицами 
(пикселями), используемыми дисплеем.) Например, одно и то же окно может иметь 
логические размеры 100х100 или 50х75. Единственная разница при этом будет 
заключаться только в пропорциях выводимого изображения. 

    Определение размеров области вывода 

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

    virtual CSize CDC::SetViewportExt(int Xextent, int Yextent); 

    Параметры Xextentu Yextent задают новый горизонтальный и вертикальный 
размер области вывода в пикселях. Функция возвращает предыдущие размеры 
области. Выполнение этой функции возможно только в режимах MM_ANISOTROPIC и MMJSOTROPIC. 
    Область вывода может иметь произвольный размер. Она может охватывать все 
окно или только часть его. В установленном по умолчанию режиме ММ_ТЕХТ область 
вывода равна всему окну. 
    Отображаемая информация автоматически переводится из контекста окна 
(логические единицы) в область вывода (пиксели), при этом она пропорционально 
масштабируется. Поэтому изменение горизонтальных и вертикальных размеров 
области вывода фактически означает изменение пропорций и размеров изображения. 
выводимого в эту область. Таким образом, увеличение размеров области приводит 
к увеличению выводимого изображения. Данный прием будет использован в 
следующей программе. 

    Задание начала области вывода 

    По умолчанию область вывода начинается в точке окна с координатами (0,0). 
Эти координаты можно изменить с помощью функции SetViewPortOrgO, которая имеет 
такой прототип: 

    virtual CPoint CDC::SetViewPortOrg(int X, int У) ; 

    Новые координаты начала области вывода, указываемые в пикселях, задаются 
параметрами Х и У. Функция возвращает предыдущие координаты начала области, 
которые записываются в объект типа CPoint, включающий стандартную структуру POINT. 
    Изменение координат начала области вывода влечет за собой изменение 
положения изображения, выводимого в окне. 

    228                                    MFC: основы программирования 


    Пример программы работы с областью вывода и режимами отображения 

    Приводимая ниже программа представляет собой обновленную версию предыдущей 
программы работы с графикой, в которой теперь задается режим отображения, 
размеры окна и размеры области вывода. Пример работы программы показан на рис. 
9.3. В ней устанавливается режим MM_ANISOTROPIC, размеры окна 200х200 и 
начальный размер области вывода 10х10. После запуска программы при каждом 
выборе команды Magnify из меню Graphics Demo размеры области вывода будут 
увеличиваться на 10 единиц по обеим осям, в результате чего изображение, 
выводимое в окне, будет увеличиваться. Выбор команды Change Origin приведет к 
тому, что начало области вывода будет сдвинуто на 50 пикселей по оси Х и Y. 
    В программе используется такой же библиотечный файл IDS.H, что и ранее. 
Текст файла ресурсов следующий: 

    ; VIEWPORT.RC 
    tinclude <afxres.h> 
    #include "ids.h" 
    GraphicsMenu MENU 
    ( 
    POPUP "Graphics Demo" 
    ( 
    MENUITEM "&Lines\tF2", IDM_LINES 
    MENUITEM "&Rectangles\tF3", IDM_RECTANGLES 
    MENUITEM "&Ellipses\tF4", IDM_ELLIPSES 
    MENUITEM "&Magnify\tF5", IDM_ENLARGE 
    MENUITEM "&Change Origin\tF6", IDM_ORG 
    MENUITEM "&Reset\tF7", IDM_RESET 
    MENUITEM "E&xit\tF8", IDM_EXIT 
    } 
    MENUITEM "SHelp", IDM_HELP 
    } 
    GraphicsMenu ACCELERATORS 
    ( 
    VK_F2, IDM_LINES, VIRTKEY 
    VK_F3, IDM_RECTANGLES, VIRTKEY 
    VK_F4, IDM_ELLIPSES, VIRTKEY 
    VK_F5, IDM_ENLARGE, VIRTKEY 
    VK_F6, IDM_ORG, VIRTKEY 
    VK_F7, IDM_RESET, VIRTKEY 
    VK_F8, IDM_EXIT, VIRTKEY 
    VK_F1, IDM_HELP, VIRTKEY > 

    Вот текст файла V1EWPORT.H, содержащего объявления порождаемых классов: 

    // V1EWPORT.H 
    // Это класс для создания главного окна 
    class CMainWin : public CFrameWnd 

    9. Работа с графикой                                              229 

    ( 
    CDC m_rnemDC; // контекст устройства, связанный с 
    // виртуальным окном 
    CBitmap m bmp; // образ виртуального окна 
    CBrush m_bkbrush; // кисть для виртуального окна 
    // объявление перьев 
    CPen m RedPen, m YellowPen, m GreenPen, m BluePen; 
    CPen m_OldPen; 
    int m vpX, m vpY; // размеры области вывода 
    int m orgX, m orgY; // начало области вывода 
    public: 
    CMainWin() ; 
    afx msg void OnPaint(); 
    afx msg void OnLinesO; 
    afx msg void OnRectangles(); 
    afx_msg void OnEllipses () ; 
    afx msg void OnEnlargeO; 
    afx msg void OnOrgO; 
    afx_msg void OnResetO;                                            i
    afx msg void OnExitO;                                             ! 
    afx msg void OnHelp(); 
    DECLARE_MESSAGE_MAP() 
    b-                                                                         
                                             i 
    // Это класс для создания приложения                                  j 
    class CApp : public CWinApp 
    ( 
    public:                                                              | 
    BOOL Initlnstance ();                                                | };  
                                                                   I 
    Это текст самой программы:                                             j 
    // Использование области вывода и режимов отображения 
    ^include <afxwin.h> 
    ^include "viewport.h" 
    #include "ids.h" 
    int maxX, maxY; // размеры экрана 
    CMainWin::CMainWin() 
    ( 
    Create(NULL, "Viewports and Mapping Modes", 
    WSJ3VERLAPPEDWINDOW, rectDefault, 
    NULL, "GraphicsMenu") ; 
    // Загрузка таблицы быстрого доступа 


    230                                     MFC: основы программирования 


    if('LoadAccelTable("GraphicsMenu")) 
    MessageBox("Cannot Load Accelerators", "Error"); 
    maxX = GetSystemMetrics(SM_CXSCREEN) ; 
    maxY = GetSystemMetrics(SM_CYSCREEN) ; 
    CClientDC DC(this) ; 
    // создание образа виртуального окна 
    m_memDC.CreateCompatibleDC(&DC) ; 
    m_bmp.CreateCompatib-LeBitmap (&DC, maxX, maxY); 
    m_memDC.SelectObject(&m_bmp) ; 
    // выбор стандартного фона 
    m_bkbrush.CreateStockObject(WHITE_BRUSH) ; 
    m_memDC.SelectObject(&m_bkbrush); 
    // закраска фона виртуального окна 
    m_memDC.PatBlt(0, 0, maxX, maxY, PATCOPY) ; 
    // создание перьев 
    m_RedPen.CreatePen(PS_SOLID, I, RGB(255, 0, 0) ) ; 
    m_GreenPen.CreatePen(PS_SOLID, 2, RGB(0, 255, 0) ) ; 
    m_BluePen.CreatePen(PS_SOLID, 3, RGB(0, 0, 255)); 
    m_YellowPen.CreatePen(PS_SOLID, 4, RGB(255, 255, 0)); 
    m_01dPen.CreateStockObject(BLACK_PEN) ; 
    // инициализация параметров области вывода 
    т vpX = m vpY = 10; 
    n'l orgX = m orgY = 0; 
    } 
    II Инициализация приложения 
    BOOL CApp::Initlnstance () 
    ( 
    m_pMainWnd = new CMainWin; 
    m_pMainWnd->ShowWindow(m_nCmdShow) ; 
    m pMainWnd->UpdateWindow() ; 
    return TRUE; 
    ) 
    // Это очередь сообщений приложения 
    BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) 
    ON_WM_PAINT () 
    ON_COMMAND(IDM_LINES, OnLines) 
    ON_COMMAND(IDM_RECTANGLES, OnRectangles) 
    ON_COMMAND(IDM_ELLIPSES, OnEllipses) 
    ON_COMMAND(IDM_ENLARGE, OnEnlarge) 
    ON_COMMAND(IDM_ORG, OnOrg) 
    ON_COMMAND(IDM_RESET, OnReset) 
    ON_COMMAND(IDM_EXIT, OnExit) 


    9. Работа с графикой                                              231 


    ON_COMMAND(IDM_HELP, OnHelp) 
    END_MESSAGE_MAP() 
    // Вывод на экран содержимого виртуального окна 
    afx msg void CMainWin::OnPaint() 
    ( 
    CPaintDC DC(this) ; 
    // задание режима отображения, размеров окна и области вывода 
    DC.SetMapMode(MM_ANISOTROPIC) ; 
    DC.SetWindowExt(200, 200); 
    DC.SetViewportExt(m_vpX, m_vpY) ; 
    DC.SetViewportOrg(m_orgX, m_orgY) ; 
    DC.BitBlt(0, 0, maxX, maxY, &m_memDC, О, О, SRCCOPY); 
    } 
    // Обработка сообщения IDM_EXIT 
    afx_msg void CMainWin::OnExit() 
    ( 
    int response; 
    response = MessageBox("Quit the Program?", "Exit", MB_YESNO); 
    if (response == IDYES) 
    SendMessage(WM_CLOSE); // завершение приложения 
    } 
    // Обработка сообщения IDM HELP 
    afx msg void CMainWin :: OnHelp () 
    { 
    MessageBox("Graphics Demo", "Help"); 
    } 
    // Отображение линий и точек 
    afx_msg void CMainWin::OnLines () 
    ( 
    //отображение четырех точек 
    m_memDC.SetPixel(40, 14, RGB(255, 0, 0) ) ; 
    m_memDC.SetPixel(41, 14, RGB(0, 255, 0) ) ; 
    m_memDC.SetPixel(42, 14, RGB(0, 0, 255)); 
    m_memDC.SetPixel(43, 14, RGB(0, 0, 0) ) ; 
    m_memDC.LineTo(100, 50); 
    m_memDC.MoveTo(100, 50); 
    // изменение цвета пера на зеленое 
    m memDC.SelectObject(sm GreenPen); 
    m_memDC.LineTo(200, 100); 
    // изменение цвета пера на желтое 
    m_memDC.SelectObject(&m_YellowPen) ; 
    m_memDC.LineTo(0, 200); 


    232                                    MFC: основы программирования 


    // изменение цвета пера на синее 
    m_rnemDC. SelectObject (&m_BluePen) ; 
    m_memDC.LineTo(200, 200); 
    // изменение цвета пера на красное 
    m_memDC.SelectObject(?m RedPen); 
    m_memDC.LineTo(0, 0);  
    m_memDC.LineTo(100, 50); 
    m_memDC.MoveTo(0, 0) ; 
    m_inemDC. LineTo (100, 250); 
    m_memDC.MoveTo(0, 0) ; 
    m_memDC.LineTo(100, 350); 
    // восстановление первоначального пера 
    m_niemDC.SelectObject(&m_01dPen) ; 
    m_memDC.Arc(О, О, 300, 300, 0, 50, 200, 0) ; 
    //отображение пересекающихся линий, которые определяют дугу 
    m_memDC.MoveTo(150, 150); 
    m_memDC.LineTo(0, 50); 
    m_memDC.MoveTo(150, 150); 
    m_meinDC. LineTo (200, 0) ; 
    InvalidateRect (NULL) ; 
    } 
    // Отображение прямоугольников 
    afx msq void CMainWin: :OnRect.anqles () • 
    ( 
    CBrush HollowBrush; 
    // создание "пустого" заполнения 
    HollowBrush.CreateStockObject(HOLLOW_BRUSH) ; 
    m_memDC.SelectObject(sHollowBrush) ; 
    // отображение нескольких прямоугольников 
    m_niemDC. Rectangle (50, 50, 300, 300); 
    m_memDC.RoundRect(125, 125, 220, 240, 15, 13); 
    // использование красного пера 
    m_[nemDC. SelectObject (&m_RedPen) ; 
    m_memDC.Rectangle(100, 100, 200, 200); 
    m memDC.SelectObject(&m OldPen); // восстановление прежнего пера 
    // восстановление прежней кисти 
    m memDC.SelectObject(&m bkBrush) ; 
    InvalidateRect(NULL) ; 
    ) 
    // Отображение эллипсов 
    afx msg void CMainWin::OnEllipses() 
    ( 


    9. Работа с графикой                                              233 


    CBrush Brush; 
    // создание синей кисти 
    Brush.CreateSolidBiush(RGB(О, О, 255)); 
    m_memDC.SelectObject(SBrush) ; 
    // отображение эллипсов, заполненных синим цветом 
    m_memDC.Ellipse (50, 200, 100, 280); 
    m_mernDC. Ellipse (75, 25, 280, 100); 
    // использование красного пера 
    m_memDC.SelectObject(&m_RedPen) ; 
    // создание зеленой кисти 
    Brush.DeleteObject (); // удаление синей кисти 
    Brush.CreateSolidBrush(RGB(0, 255, 0)) ; 
    m memDC.SelectObject(bBrush); // выбор зеленой кисти 
    m_memDC.Ellipse(100, 100, 200, 200); 
    // отображение сектора 
    m_memDC.Pie(200, 200, 340, 340, 225, 200, 200, 250); 
    ni_memDC.SelectObject(&m_01dPen); // восстановление прежнего пера 
    m_memDC.SelectObject(&m_bkBrush); // восстановление прежней кисти 
    InvalidateRect(NULL) ; 
    ) 
    // Увеличение изображения 
    afx msg void CMainWin::OnEnlarge() 
    { 
    m_vpX +=10; 
    in_vpY +=10; 
    InvalidateRect(NULL) ; 
    ) 
    // Изменение координат начала области вывода 
    afx msg void CMainMin::0n0rg() 
    { 
    m_orgX +=50; 
    m_orqY +=50; 
    InvalidateRect(NULL) ; 
    ) 
    // Сброс параметров области вывода и очистка окна 
    afx_msg void CMainWin::OnReset() 
    ( 
    m vpX = m vpY = 10; 
    m__orgX = m_orgY = 0; 
    m_memDC.PatBlt(0, 0, maxX, maxY, PATCOPY) ; 
    InvalidateRect(NULL) ; 
    ) 
    CApp App; // создание экземпляра приложения 


    234                                     MFC: основы программирования 


    Рис. 9.3. Работа программы при различных размерах и начальных координатах 
области вывода 

    9. Работа с графикой                                                       235 
