Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Введение в графические функции. Организация вывода в виртуальное окно - MFC

В Windows имеется большой набор графических функций, предназначенных для векторной графики. В этой главе мы рассмотрим только введение в графическую систему и несколько простых функций. Тем не менее, изучив основы в этой главе, Вы всегда сможете легко изучить в дальнейшем нужные Вам графические функции.

Также мы рассмотрим самый удобный способ решения проблемы перерисовки окна по сообщению WM_PAINT - использование виртуального окна. Мы не использовали этот метод раньше, чтобы не загромождать примеры программ второстепенными деталями.

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

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

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

Перья

Графические объекты рисуются с помощью текущего пера. В MFC перья описываются с помощью класса CPen. По умолчанию установлено черное перо толщиной в 1 пиксель. Всего имеется три стандартных пера: черное, белое и нулевое (прозрачное). Конечно, этого недостаточно. Поэтому обычно создаются собственные перья. Для этого нужно вызвать функцию :

 
 	BOOL CPen::CreatePen(int Style, int Width, COLORREF Color);
 
 
Параметр Style определяет стиль созданного пера. Толщина пера в логических единицах задается параметром Width. Цвет пера задается с помощью параметра Color. Класс CPen также имеет перегруженный конструктор с такими же параметрами. Иногда легче использовать его.

Кисти

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

 	BOOL CBrush::CreateSolidBrush(COLORREF Сolor);
 
Параметр задает цвет кисти. Класс также имеет конструктор с таким же параметром, позволяющий сразу создать кисть.

Следующие функции позволяют создать также кисти со штриховкой и кисти по шаблону битового образа:

 	BOOL Сbrush::CreateHatchBrush(int Index, COLORREF Color);
 	BOOL Сbrush::CreatePatternBrush(CBitmap *pBitmap);
 
Параметр Index указывает стиль кисти. Параметр pBitmap - это указатель на объект битового образа. Предварительно он может быть загружен из ресурсов. Битовый образ должен иметь размер 8х8.

Мы будем использовать все эти функции в примере программы.

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

Отобразить в контексте устройства точку определенного цвета можно с помощью следующей функции:

 
 	СOLORREF CDC::SetPixel(int X, int Y, COLORREF Color);
 
 
Функция отображает точку заданного цвета по указанным координатам. Она возвращает цвет, который был установлен в действительности. Он может отличаться от заданного, из-за округления до ближайшего цвета, поддерживаемого текущим видеорежимом.

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

Для рисования прямоугольников текущим пером используются функции:

 	BOOL CDC::Rectangle(int upX, int upY, int lowX, int lowY);
 	BOOL CDC::RoundRect(int upX, int upY, int lowX, int lowY,
 	int curveX, int curveY);
 
Первая функция рисует обычный прямоугольник, а вторая - прямоугольник со скругленными углами. Параметры curveX и curveY задают ширину и высоту эллипса, определяющего дугу для скругленных углов. Нарисованные фигуры автоматически заполняются с помощью текущей кисти контекста устройства.

Рисование эллипсов

Для рисования эллипсов текущим пером используется функция:

 	BOOL CDC::Ellipse(int upX, int upY, int lowX, int lowY);
 
Координаты (upX, upY) и (lowX, lowY) задают левый верхний и правый нижний углы прямоугольника, в который будет вписан эллипс. Если нужно нарисовать окружность, то задается описанный квадрат. Специальной функции для рисования окружностей нет.

Фигура заполняется с помощью текущей кисти контекста устройства.

Организация виртуального окна

В примере программы, который мы будем рассматривать, в окне рисуется много разных фигур. Возникает проблема восстановления содержимого окна по сообщению WM_PAINT. Запоминать нарисованные фигуры и затем рисовать их заново очень трудно, а в общем случае и невозможно. Поэтому самый удобный метод для решения проблемы перерисовки - выводить графику в память, а при получении сообщения WM_PAINT просто копировать эту область памяти в контекст окна.

Дополнительные функции

Для реализации виртуального окна нужно использовать некоторые еще не рассмотренные нами функции MFC.

Функция с прототипом:

 	BOOL CBitmap::CreateCompatibleBitmap(CDC *pDC, int Width, 
 					               int Height);
 
создает растровое изображение совместимое с указанным контекстом устройства. Впоследствии оно может быть использовано с любым контекстом памяти, совместимым с контекстом данного устройства. Два последних параметра определяют размер изображения.

Следующая нужная нам функция:

 	BOOL CDC::PatBlt(int x, int y, int Width, int Height, 
 					       DWORD RasterOp);
 
заполняет текущей кистью прямоугольную область с указанными начальными координатами и размером. Последний параметр определяет растровую операцию, аналогично функции BitBlt(). Нам будет нужно значение PATCOPY.

Создание виртуального окна

Для этого в примере программы используется следующий код:

 	// Поддержка виртуального окна
 	// Получим размеры экрана
 	maxX = ::GetSystemMetrics(SM_CXSCREEN);
 	maxY = ::GetSystemMetrics(SM_CYSCREEN);
 	CClientDC dc(this); 

 	// Создание совместимого контекста устройства и
 	// битового образа
 	m_memDC.CreateCompatibleDC(&dc);
 	m_bmp.CreateCompatibleBitmap(&dc, maxX, maxY);
 	m_memDC.SelectObject(&m_bmp);
 
 	// Выбор черной кисти для стирания фона окна и
 	// стирание фона
 	m_bkbrush.CreateStockObject(BLACK_BRUSH);
 	m_memDC.SelectObject(&m_bkbrush);
 	m_memDC.PatBlt(0, 0, maxX, maxY, PATCOPY);
 
Сначала определяются размеры экрана. Затем для текущего окна запрашивается контекст устройства, который используется для создания совместимого контекста области памяти. После этого создается совместимый битовый образ. Размеры изображений устанавливаются такими же, как и размеры экрана. Это дает уверенность, что изображение в виртуальном окне будет достаточно большим, независимо от размеров реального окна. Следующим шагом создается стандартная черная кисть, которая затем используется для очистки виртуального окна.

Использование виртуального окна

Использование созданного нами контекста памяти ничем не отличается от использования контекста окна, что мы уже неоднократно делали. При получении сообщения WM_PAINT в обработчике выполняется следующий код:

 	CPaintDC paintDC(this);
 	RECT clientRect;
 	this->GetClientRect(&clientRect);
 	paintDC.BitBlt(0, 0, clientRect.right, clientRect.bottom, 
 	&m_memDC, 0, 0, SRCCOPY);
 
Он копирует содержимое виртуального окна на экран. Обратите внимание, что копируется только часть изображения, в соответствии с размерами клиентской области.

Пример программы, демонстрирующей
некоторые графические функции и использование
виртуального окна

 	MainFrame.hpp
 	#include "stdafx.h"
 	class CMainFrame: public CFrameWnd {
 	public:
 	CMainFrame();
 	~CMainFrame();

 	// Фигуры рисуются по сообщениям от таймера
 
 	afx_msg void OnTimer(UINT id);
 
 	// Обработчик сообщения WM_ERASEBKGND. Обычно по этому
 	// сообщению
 
 	// стирается фон окна. Наш же обработчик ничего
 	// не делает, так как 
 
 	// в окнах с виртуальным окном перерисовывать
 	// фон не нужно.
 
 	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
 	afx_msg void OnPaint();
 	afx_msg void OnDestroy();
 	private: 
 	CWinApp *pApp;
 	CDC m_memDC;
 	CBitmap m_bmp;
 	CBrush m_bkbrush;
 	int maxX, maxY;
 	COLORREF m_textColor;
 	DECLARE_MESSAGE_MAP();
 	};
 
 	MainFrame.cpp
 	
 	#include "stdafx.h"
 	#include "resource.h"
 	#include "MainFrame.hpp"
 	BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
 	ON_WM_ERASEBKGND()
 	ON_WM_PAINT()
 	ON_WM_TIMER()
 	ON_WM_DESTROY()
 	END_MESSAGE_MAP() 
 	CMainFrame::CMainFrame()
 	{
 	pApp = AfxGetApp();

 	this->Create(0, "Графика GDI и использование
 	виртуального окна",WS_OVERLAPPEDWINDOW, rectDefault, 0, 0);

 	// Поддержка виртуального окна
 	// Получим размеры экрана
 	maxX = ::GetSystemMetrics(SM_CXSCREEN);
 	maxY = ::GetSystemMetrics(SM_CYSCREEN);
 	CClientDC dc(this); 
 
 	// Создание совместимого контекста устройства и
 	// битового образа
 
 	m_memDC.CreateCompatibleDC(&dc);
 	m_bmp.CreateCompatibleBitmap(&dc, maxX, maxY);
 	m_memDC.SelectObject(&m_bmp);

 	// Выбор черной кисти для стирания фона окна и
 	// стирание фона
 
 	m_bkbrush.CreateStockObject(BLACK_BRUSH);
 	m_memDC.SelectObject(&m_bkbrush);
 	m_memDC.PatBlt(0, 0, maxX, maxY, PATCOPY);
 	this->ShowWindow(SW_RESTORE);
 	this->UpdateWindow();
 	// Установим таймер с интервалом 0.1 с
 	this->SetTimer(1, 100, 0);
 	}
 
 	CMainFrame::~CMainFrame() {}

 	afx_msg void CMainFrame::OnTimer(UINT id)
 	{

 	// Получим прямоугольник клиентской области
 	// окна

 	RECT clientRect;
 	this->GetClientRect(&clientRect);
 	// Выбрать в контекст перо по случайному
 	// критерию 
 	// Выбор стиля пера
 
 	int penStyle;
 	int random = rand();
 	if(random < RAND_MAX / 3) {
 	penStyle = PS_SOLID;
 	}
 
 	else if(random < RAND_MAX * 2 / 3) {penStyle = PS_DOT;}
 	     else {penStyle = PS_DASH;}

 	// Толщина (от 1 до 3);
 	int penWidth = MulDiv(rand(), 3, RAND_MAX);
 	// Цвет (исключая черный)
 	COLORREF penColor = 0;
 	while(penColor == 0) {
 	penColor = RGB(MulDiv(rand(), 255, RAND_MAX), 
 	MulDiv(rand(), 255, RAND_MAX), 
 	MulDiv(rand(), 255, RAND_MAX));
 	}
 
 	CPen pen(penStyle, penWidth, penColor);
 	m_memDC.SelectObject(pen);

 	// Выбрать в контекст кисть по случайному
	// критерию 
 	CBrush brush;
 	// Цвет 
 
 	COLORREF brushColor = RGB(MulDiv(rand(), 255, RAND_MAX), 
 	MulDiv(rand(), 255, RAND_MAX), 
 	MulDiv(rand(), 255, RAND_MAX));
 	// Выбор стиля кисти
 	CBitmap brushBitmap;
 	brushBitmap.LoadBitmap(IDB_BRUSH);
 	random = rand();
 	if(random < RAND_MAX / 3) {
 	// Сплошная кисть
 	brush.CreateSolidBrush(brushColor);
 	}
 	else if(random < RAND_MAX * 2 / 3) {
 	// Кисть с крестообразной штриховкой 
 	brush.CreateHatchBrush(HS_DIAGCROSS, brushColor);
 	}
 	else {
 	// Кисть по шаблону
 	brush.CreatePatternBrush(&brushBitmap);
 	}
 	m_memDC.SelectObject(brush);
 	// Рисуем фигуру
 	int x = MulDiv(rand(), clientRect.right, RAND_MAX);
 	int y = MulDiv(rand(), clientRect.bottom, RAND_MAX);
 	int r1 = MulDiv(rand(), 90, RAND_MAX) + 10;
 	int r2 = MulDiv(rand(), 90, RAND_MAX) + 10;
 	if(rand() < RAND_MAX / 2) {
 	// Рисуем эллипс
 	m_memDC.Ellipse(x - r1 / 2, y - r2 / 2, x + r1 / 2, y + r2 / 2);
 	}
 	else {
 	// Рисуем прямоугольник со скругленными углами
 	m_memDC.RoundRect(x - r1 / 2, y - r2 / 2, x + r1 / 2, y + r2 / 2,
 	r1 / 5, r2 / 5);
 	}

 	// пошлем сообщение WM_PAINT 
 	this->InvalidateRect(0, FALSE);
 	}
 
 	afx_msg BOOL CMainFrame::OnEraseBkgnd(CDC* pDC)
 	{

 	// Ничего не делаем, так как фон окна
 	// прорисовывать не нужно - 
 
 	// он будет прорисован при копировании
 	// контекста памяти в контекст окна.
 	return TRUE; // Якобы фон прорисован
 	}
 
 	afx_msg void CMainFrame::OnPaint()
 	{
 	CPaintDC paintDC(this);
 	RECT clientRect;
 	this->GetClientRect(&clientRect);
 	paintDC.BitBlt(0, 0, clientRect.right, clientRect.bottom, 
 	&m_memDC, 0, 0, SRCCOPY);
 	}
 
 	afx_msg void CMainFrame::OnDestroy()
                                	{this->KillTimer(1);}
 
 	Graphics_And_VirtWin.hpp
 
 	#include "stdafx.h"
 
 	class CApp: public CWinApp {
 	public:
 	BOOL InitInstance();
 	};
 
 	Graphics_And_VirtWin.cpp
 
 	#include "stdafx.h"
 	#include "Graphics_and_VirtWin.hpp"
 	#include "MainFrame.hpp"
 	CApp App;
 	BOOL CApp::InitInstance()
 	{
 	m_pMainWnd = new CMainFrame();
 	return TRUE;
 	}
 

Рис. 15. Демонстрационная программа, использующая графические функции и вывод через виртуальное окно

В программе рисуются эллипсы и прямоугольники со скругленными краями, и закрашиваются сплошной кистью, кистью со штриховкой или кистью по шаблону. В последнем случае используется битовый образ 8х8 из ресурсов. Также обратите внимание, что в программе обрабатывается сообщение WM_ERASEBKGND. Оно посылается, когда нужно стереть фон окна. Так как мы используем виртуальное окно, то стирать фон не нужно. Это предотвращает мерцание изображения в окне.
назад | содержание | вперед

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 

Комментарии

1.
41K
01 июля 2008 года
erty
0 / / 01.07.2008
Мне нравитсяМне не нравится
7 июля 2008, 22:55:06
А потому что читать надо внимательно. В главе "обработка сообщений" при описании WM_PAINT было сказано:

Большинство реальных окон (за исключением диалогов, которые мы рассмотрим позднее) должны обрабатывать сообщение WM_PAINT. Более того, если Вы хотите написать корректную программу, то весь вывод в окно должен осуществляться только в обработчике WM_PAINT, и никак иначе. Например, крайне нежелательно получать контекст устройства в обработчике сообщения мыши и пытаться там что-то выводить. Это будет работать в большинстве случаев, но далеко не всегда. Дело в том, что Windows может предоставить одновременно всем программам лишь очень небольшое число контекстов устройств (Windows 95 - всего пять). Поэтому при запросе контекста не из обработчика WM_PAINT создание класса контекста может потерпеть провал, если другие приложения уже заняли все контексты.

И динамическая память тут вряд ли поможет. А так все работает уже минут десять.
2.
Аноним
Мне нравитсяМне не нравится
9 июня 2004, 13:48:00
В приведённое проге перо будет работать лишь некоторое время, после чего вывалится "Запрашиваемый ресурс недоступен". Это можно вылечить, разместив перо в динамической памяти, но как тогда управлять его параметрами?
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог