4.1.11 ОСНОВНЫЕ ШАГИ ПРИ ПРОГРАММИРОВАНИИ ГРАФИКИ

В предыдущих пп. рассматривалось программирование графики в 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                  — нулевая кисть, не заполняет область ничем, независимо от режима смешения.