В предыдущих пп. рассматривалось программирование графики в Windows. Подведем итоги. Отображение графических данных в системе Windows состоит из следующих шагов:
· получение контекста устройства;
· установка атрибутов вывода фона, точек и линий;
· создание и выбор перьев, кистей и других графических объектов;
· ввод рисунков с помощью графических функций;
· освобождение и уничтожение созданных перьев, кистей и других графических объектов;
· освобождение контекста устройства.
Первый и шестой шаги были описаны в пп. 2.1.6. Они выполняются с помощью функций GetDC() и ReleaseDC(). При обработке сообщения WM_PAINT о требовании перерисовки окна эти шаги осуществляются с помощью BeginPaint() и EndPaint().
Если в оконной функции рисунки выводятся часто, то можно получить и освободить контекст устройства один раз. В этом случае стиль окна следует установить при регистрации равным CS_OWNDC, например
WNDCLASS wcl;
Wcl.Style = CS_OWNDC;
…
RegisterClass(&wcl);
Контекст устройства также может быть создан в памяти, с помощью функции CreateCompatibleDC(). Мы рассматривали эту возможность в пп. 2.1.10 (программа, выводящая вращающиеся квадраты). В этом случае устройством является битовая матрица Windows, расположенная в памяти, в которой для каждого пикселя хранится его цвет.
На втором шаге устанавливается атрибуты рисования. Под атрибутами рисования понимается способ вывода точек, линий и областей. При получении нового контекста устройства этот контекст имеет атрибуты, установленные по умолчанию.
Цветом фона называется цвет, используемый при заполнении промежутков в не сплошных линиях при закраске области между линиями штриховки. По умолчанию является белым.
Для изменения цвета фона вызывается функция
COLORREF SetBkColor (HDC hdc, COLORREF color);
Например, вызов
SetBkColor (hdc,RGB(255,0,0));
устанавливает красный цвет для фона.
Заметим, что функция вывода текста TextOut(), использует цвет фона в качестве цвета фона для букв. Цвет текста устанавливается с помощью функции:
COLORREF SetTextColor (HDC hdc, COLORREF color);
Режим вывода фона. Режим фона определяет способ заполнения точек области, находящихся между точками и штрихами. Режим фона устанавливается с помощью функции int SetBkMode (HDC hdc, int BkMode);
где hdc – контекст устройства, а BkMode принимает одно из двух значений:
· TRANSPARENT — прозрачный фон, промежутки между штрихами никак не закрашиваются;
· OPAQUE — промежутки между штрихами закрашиваются текущим цветом фона (это значение установлено по умолчанию). Точки и штрихи закрашиваются цветом кисти.
Режим смешения определяет, каким образом Windows комбинирует цвет пера с цветом точек, выведенных на экран компьютера. Он устанавливается с помощью вызова функции
int SetROP2 (HDC hdc, int fnDrawMode);
возвращающей текущий режим смешения. Аргументу fnDrawMode соответствуют режимы, возможные значения которых перечислены в таблице 2.1.
Таблица 2.1
Режим |
Назначение(цвет пиксела) |
R2_BLACK |
черный |
R2_COPYPEN |
цвет пера |
R2_MASKNOTPEN |
(цвет экрана)&(цвет пера) |
R2_MASKPEN |
(цвет пера)&(цвет экрана) |
R2_MASKPENNOT |
(цвет экрана)| (цвет пера) |
R2_MERGEPEN |
(цвет пера)|(цвет экрана) |
R2_MERGEPENNOT |
(цвет пера)|(цвет экрана) |
R2_NOP |
цвет экрана |
R2_NOT |
(цвет экрана) |
R2_NOTCOPYPEN |
(цвет пера) |
R2_NOTMASKPEN |
(цвет пера)&(цвет экрана) |
R2_NOTMERGEPEN |
(цвет пера)|(цвет экрана) |
R2_NOTXORPEN |
(цвет пера)(+)(цвет экрана) |
R2_WHITE |
Белый |
R2_XORPEN |
(цвет пера)(+)(цвет экрана) |
В таблице 2.1 черта сверху обозначает поразрядное отрицание; &,|,(+) – поразрядные логические операции умножения, сложения и сложения по модулю два соответственно.
Например, если вызвать функцию
SetRop2(hdc, R2_XORPEN);
то рисование линий будет производиться аналогично выводу отрезка в режиме XOR_PUT в MS DOS. В этом случае повторное рисование линии приведёт к ее исчезновению.
Создание и выбор графических объектов
Перо по умолчанию рисует черные линии толщиной в один пиксел. Перо можно создать из стандартных перьев с помощью вызова функции
HGDIOBJ GetStockObject (int fnOBJECT);
где fnObject принимает одно из следующих значений: BLACK_PEN (черное перо), WHITE_PEN (белое перо), NULL_PEN (перо, которое ничего не рисует). В случае NULL_PEN не будут рисоваться линии и границы областей.
Перо можно создать с помощью функции
HPEN CreatePen (int style, int width, COLORREF color);
где style определяет один из типов линий
PS_DASH — пунктирная линия;
PS_DASHDOT — штрих-пунктирная линия (тире-точка-тире);
PS_DASHDOTDOT — штрих-пунктирная линия (тире-точка-точка-тире);
PS_DOT — точечная линия;
PS_INSIDEFRAME — сплошная линия внутри ограничивающей области;
PS_NULL — прозрачное перо;
PS_SOLID — сплошная линия.
После создания пера для данного контекста устройства его можно выбрать с помощью функции SelectObject().
Например, в следующем фрагменте программы устанавливается красное перо толщиной три пиксела:
HPEN hredpen;
Hredpen = CreatePen (PS_SOLID,3,RGB(255,0,0));
SelectObject (hdc,hredpen);
Перед завершением работы программы необходимо удалить перо с помощью вызова
DeleteObject (hredpen);
Рассмотрим пример программы, в которой создается черное перо толщиной три пиксела для вывода графика функции y = xsin(x).
// файл ресурсов graphic.rc
#include <windows.h>
MYDB DIALOG 300,20,65,75
CAPTION "Parameters"
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
{
DEFPUSHBUTTON "OK", IDOK, 0,0, 20,8,
WS_CHILD | WS_VISIBLE | WS_TABSTOP
EDITTEXT 300, 25, 8, 60, 10, ES_LEFT |
ES_AUTOHSCROLL|WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP
EDITTEXT 301, 25, 22, 40, 10, ES_LEFT |
ES_AUTOHSCROLL|WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP
EDITTEXT 302, 25, 36, 40, 10, ES_LEFT |
ES_AUTOHSCROLL|WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP
EDITTEXT 303, 25, 50, 40, 10, ES_LEFT |
ES_AUTOHSCROLL|WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP
EDITTEXT 304, 25, 64, 40, 10, ES_LEFT |
ES_AUTOHSCROLL|WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP
LTEXT "xmin=",310,0,8,25,10
LTEXT "xmax=",311,0,22,25,10
LTEXT "ymin=",312,0,36,25,10
LTEXT "ymax=",313,0,50,25,10
LTEXT " n=",314,0,64,25,10
}
//Текст программы, файл graphic.cpp
#include <windows.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <math.h>
LRESULT CALLBACK WindowFunc(HWND,UINT,WPARAM,LPARAM);
BOOL CALLBACK DialogFunc (HWND,UINT,WPARAM,LPARAM);
char szwinname[] = "MyWindow";
int maxX,maxY;
float xmin=-5, xmax=15, ymin=-15, ymax=15;
float hx, xdens, ydens;
int n=100;
char str0[80], str1[80],str2[80],str3[80],str4[80];
HINSTANCE hInst;
HPEN holdpen;
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 = NULL;
wcl.cbClsExtra = 0;
wcl.cbWndExtra = 0;
wcl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
if(!RegisterClass(&wcl))
return 0;
hwnd = CreateWindow(szwinname,
"Function",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
HWND_DESKTOP,
NULL,hthisinst,
NULL);
ShowWindow(hwnd,nwinmode);
UpdateWindow(hwnd);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
// вычисление x
int ex(float x, float y)
{
return (int)((x-xmin)/xdens);
}
// вычисление y
int ey(float x, float y)
{
return (int)((ymax-y)/ydens);
}
// функция f
float f(float x)
{
return (float)(x*sin(x));
}
// вывод отрезка
void line(HDC dc, int x0, int y0, int x1, int y1)
{
MoveToEx(dc,x0,y0,NULL); LineTo(dc,x1,y1);
}
LRESULT CALLBACK WindowFunc(HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
HDC hdc;
static HWND hwed;
float x,y;
PAINTSTRUCT paintstruct;
HANDLE saveObject;
switch(message)
{
case WM_CREATE:
maxX= GetSystemMetrics(SM_CXSCREEN);
maxY= GetSystemMetrics(SM_CYSCREEN);
break;
case WM_CHAR:
DialogBox(hInst,"MYDB",hwnd,DialogFunc);
InvalidateRect(hwnd,NULL,1);
break;
case WM_PAINT:
hdc = BeginPaint(hwnd,&paintstruct);
xdens = (xmax-xmin)/maxX; ydens = (ymax-ymin)/maxY;
hx=(xmax-xmin)/n;
saveObject = SelectObject(hdc,
CreatePen(PS_SOLID,3,RGB(0,0,0)));
line(hdc, ex(xmin,0),ey(xmin,0), ex(xmax,0), ey(xmax,0));
line(hdc, ex(0,ymin),ey(0, ymin), ex(0, ymax), ey(0, ymax));
// вывод графика
for (x=xmin; x<xmax; x+=hx)
line(hdc, ex(x,f(x)),ey(x,f(x)), ex(x+hx,f(x+hx)),
ey(x+hx,f(x+hx)));
DeleteObject(SelectObject(hdc,saveObject));
TextOut(hdc, 50,20,
"Нажмите на любую клавишу для изменения параметров", 49);
EndPaint(hwnd,&paintstruct);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd,message,wParam,lParam);
}
return 0;
}
BOOL CALLBACK DialogFunc(
HWND hdwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
HDC hdc;
int i;
static HWND hwed;
switch(message)
{
case WM_INITDIALOG:
sprintf(str0,"%f",xmin);
SetDlgItemText(hdwnd,300,(LPSTR)str0);
sprintf(str1,"%f",xmax);
SetDlgItemText(hdwnd,301,(LPSTR)str1);
sprintf(str2,"%f",ymin);
SetDlgItemText(hdwnd,302,(LPSTR)str2);
sprintf(str3,"%f",ymax);
SetDlgItemText(hdwnd,303,(LPSTR)str3);
sprintf(str4,"%d",n);
SetDlgItemText(hdwnd,304,(LPSTR)str4);
return 1;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDCANCEL:
EndDialog(hdwnd,1);
return 1;
case IDOK:
GetDlgItemText(hdwnd, 300, str0,80);
GetDlgItemText(hdwnd, 301, str1,80);
GetDlgItemText(hdwnd, 302, str2,80);
GetDlgItemText(hdwnd, 303, str3,80);
GetDlgItemText(hdwnd, 304, str4,80);
sscanf(str0,"%f",&xmin);
sscanf(str1,"%f",&xmax);
sscanf(str2,"%f",&ymin);
sscanf(str3,"%f",&ymax);
sscanf(str4,"%d",&n);
EndDialog(hdwnd,1);
return 1;
}
}
return 0;
}
Результаты работы программы приведены на рис. 2.13.
Рис. 2.13. График функции y = xsin(x)
В данной программе использует модальный диалог. В результате работы программы на экран выводится график функции. Параметры, установленные по умолчанию, отображаются в диалоговом окне. Если изменить какие-нибудь из этих параметров и нажать клавишу [Enter], или на кнопку ОК, то на экран будет выведен график, соответствующий новым параметрам.
Создание и выбор кисти. Для закрашивания внутренности замкнутых фигур Windows использует текущую кисть. Установленная по умолчанию кисть закрашивает внутренние точки области сплошным белым цветом. Цвет и шаблон заполнения можно изменить, получив и выбрав другую кисть.
Любую из стандартных кистей можно получить с помощью функции
HGDIOBJ GetStockObject (int fnObject);
присвоив параметру fnObject одно из следующих значений:
WHITE_BRUSH — сплошная белая ;
BLACK_BRUSH — сплошная черная;
DKGRAY_BRUSH — сплошная темно-серая;
GRAY_BRUSH — сплошная серая;
LTGRAY_BRUSH — сплошная светло-серая;
NULL_BRUSH — кисть , которая ничего не рисует.
Все эти кисти, кроме NULL_BRUSH, заполняют внутренние точки замкнутой фигуры цветом кисти. Функция GetStockObject() возвращает дескриптор кисти, который можно сохранить в переменной типа HBRUSH.
Например,
HBRUSH hgraybrush = GetStockObject(GRAY_BRUSH);
создает стандартную кисть hgraybrush. Для того, чтобы эту кисть можно было использовать при выводе, ее нужно выбрать:
HBRUSH hbrush = (HBRUSH) SelectObject (hdc,hgraybrush);
После того, как эта кисть уже будет не нужна, можно восстановить старую кисть с помощью
SelectObject (hdc,hbrush);
Наиболее общим способом создания кисти является использование функции
HBRUSH CreateBrushIndirect (CONST LOGBRUSH * lplb);
где параметр lplb является указателем на приведенную ниже структуру:
struct LOGBRUSH
{
UINT lbStyle;
COLORREF lbColor;
LONG lbHatch;
};
в которой lbStyle – стиль кисти, lbColor – цвет, lbHatch - дескриптор битовой матрицы, содержащей шаблон заполнения, заданный пользователем. Поле lbHatch используется в тех случаях, когда lbStyle = BS_PATTERN. Дескриптор битовой матрицы можно получить с помощью вызова функции LoadBitmap() для загрузки битовой матрицы, включенной в файл ресурсов. Пример создания кисти заданной пользователем, будет рассмотрен при изучении программирования в Visual C++.
Параметр lpStyle может принимать следующие значения:
BS_SOLID — сплошное заполнение;
BS_HATCHED — штриховая кисть, заполняет область линиями штриховки;
BS_PATTERN — заполняет область копированием битовой матрицы размером 8х8;
BS_NULL — нулевая кисть, не заполняет область ничем, независимо от режима смешения.