Дата публикации статьи: 10.06.2006 11:05

ГЛАВА 3. Примеры работы с WIN GDI32 графикой в среде Visual Basic

    Значительное место в программировании графики занимает растровая графика, представляющая собой массив состоящий из пикселов. Для описания каждого пикселя из этого массива применяется разное число бит (растровую графику еще называют битовой). В зависимости от числа бит, затраченных на описание одного пикселя зависит качество изображения. Основным форматом для программирования графики является формат файла типа BMP (так как он хранится без сжатия). Для определения сколько бит затрачено на пиксель перед массивом данных располагаются структуры, описывающие параметры файла. Суть программирования растровой графики сводится к манипуляции пикселями. Как правило манипуляции заключаются в копировании некоторого числа пикселов из одного изображения (загруженного в память) в другое, после чего новое изображение (созданное в памяти) выводится на экран или записывается в файл. При данных манипуляциях возможно для создания эффектов (например прозрачности) часть пикселов, может быть исключено, перенесено в другую часть изображения или замаскировано. Такого рода операции затратны с точки зрения памяти, поэтому наибольшего развития этот метод получил в технологии DirectDraw (см. следующую главу), оптимизирующей подобные операции.
    Шкала измерительного прибора из главы 2 была построена с помощью векторной графики. В этой главе шкала прибора строится с помощью битовой графики. Применение битовой графики оживляет восприятие действительности особенно если использовать фотографии, но ухудшает возможности по трансформации графики (масштабирование, поворот итп).
    Для демонстрации работы с битовой графикой создайте проект типа Standard EXE (см. проект папка N5/). Поместите на форму два элемента PictureBox и кнопку. Введите в обработчики формы следующий код:

Option Explicit
Private Declare Function BitBlt _
Lib "gdi32" (ByVal hDC As Long, _
    ByVal x As Long, ByVal y As Long, _
    ByVal nWidth As Long, ByVal nHeight As Long, _
    ByVal hSrcDC As Long, _
    ByVal xSrc As Long, ByVal ySrc As Long, _
    ByVal dwRop As Long _
) As Long
Dim Xleft As Long


Private Sub Form_Load()
Xleft = 8
End Sub


Private Sub BitBl_Click()
 Dim vozvrat As Long
Picture2.Refresh
 Xleft = Xleft + 5
'Маска
vozvrat = BitBlt(Picture2.hDC, Xleft, 37, 25, 16, _
            Picture1.hDC, 25, 0, vbSrcAnd)
'Накладка
vozvrat = BitBlt(Picture2.hDC, Xleft, 37, 25, 16, _
           Picture1.hDC, 0, 0, vbSrcPaint)
End Sub

Запустив на выполнение приложение, нажмите кнопку. Произойдет копирование из элемента Picture1 в элемент Picture2 двух блоков пикселов. Первой будет скопирована маска, а затем изображение (Рис. 1).


Рис. 1

    Маска нужна для ограничения области которая выводится и исключается при операции копирования. Работа по копированию из одной области (контекста) в другую возлагается на API функцию BitBlt(). Флаги vbSrcAnd и vbSrcPaint описывают характер битовых операций (всего их шестнадцать), так же в функции указаны координаты источника и приемника данных.
    У файла BMP есть более правильное название это DIB (Devaice – Independent Bitmap) аппаратно – независимая битовая карта. В файл BMP формат DIB превращает структура BITMAPFILEHEADER (начинается с символов BM-4D42). При желании вы можете выкинуть эту структуру. Файл не перестанет быть файлом DIB, но потеряет признаки формата BMP. Для демонстрации приведу простой пример, написанный на Visual C++.NET (логотип .NET пусть не вводит в заблуждение – это обычный С++ стандарта ANSI). Приложение создано с использованием MFC, но без применения архитектуры документ/вид (см. проект папка N6/CrakBMP).
Основную работу совершает класс Cdib (см. dib.h/dib.cpp):

class Cdib : public CObject
{
UINT razmer;
BYTE* BuferRis;
public:
BITMAPINFO* m_pBitmapInfo;
BITMAPINFOHEADER* gl;
BYTE* m_pData;
UINT GetRazmer();
void* GetBuferRis();
int GetDibWidth(); 
int GetDibHeight(); 
Cdib(const char* dibFaileName);
~Cdib(void);
};

Cdib::Cdib(const char* dibFaileName)
{
///////ЗАГРУЗКА "ВМ", ПАМЯТЬ ПОД "ВМ"-*.BM, имеем pDib
CFile dibFile(dibFaileName, CFile::modeRead);
BITMAPFILEHEADER start;
dibFile.Read((void*)&start, sizeof(BITMAPFILEHEADER));
razmer =(UINT) (dibFile.GetLength() - sizeof(BITMAPFILEHEADER));
BuferRis = (BYTE*)GlobalAlloc(GMEM_FIXED, razmer);
dibFile.Read((void*)BuferRis, razmer);
dibFile.Close();
/////////////////данные для StretchDIBits()
m_pBitmapInfo = (BITMAPINFO*)BuferRis;
gl = (BITMAPINFOHEADER*)BuferRis;
m_pData = BuferRis + gl->biSize + (256 * sizeof(RGBQUAD));
}

void* Cdib::GetBuferRis()
{
return (void*)BuferRis;
}
UINT Cdib::GetRazmer()
{
return razmer;
}
int Cdib::GetDibWidth() 
{ 
return (int) gl->biWidth; 
} 
int Cdib::GetDibHeight() 
{ 
return (int) gl->biHeight; 
} 

Cdib::~Cdib(void)
{
GlobalFree(m_pBitmapInfo);
}

    Вывод в клиентскую область окна происходит с помощью функции StretchDIBits(), которая расположена в файле CchildView.cpp. Эта функция является близкой родственницей уже, рассмотренной выше функции BitBlt(). Запустите программу на выполнение. Загрузите в окно программы файл Futura.bmp (или другой). Сохранение произведите в файл с именем dibnoheader.ris (изначально установлено в диалоговом окне сохранения файла) в папку с исполняемым файлом приложения (Рис. 2).


Рис. 2

Если попытаться загрузить файл dibnoheader.ris в графический редактор Paint (входит в состав Windows XP), программа выдаст предупреждение, что этот файл не поддерживается (Рис. 3).


Рис. 3

Для загрузки единственного файла dibnoheader.ris создадим простое приложение на Visual C++.NET(см. проект папка N6/RIS). Данное приложение умет читать DIB файл без заголовка BITMAPFILEHEADER. Вывод файла в клиентскую область окна происходит в функции StretchDIBits() в событии Paint:

void CChildView::OnPaint() 
{
CPaintDC dc(this); // device context for painting
m_pRis = new CRisDib("dibnoheader.ris");//4
int bmWidth = m_pRis->GetRisWidth();
int bmHeight = m_pRis->GetRisHeight();
StretchDIBits(dc.m_hDC,0,0,bmWidth,bmHeight,0,0,bmWidth,bmHeight,
m_pRis->GetRisData(),m_pRis->GetRisFailInfo(),DIB_RGB_COLORS,SRCCOPY);//5
}

    Скопируйте фал dibnoheader.ris в папку с исполняемым файлом и запустите приложение на выполнение. В клиентской области должен отобразится бывший файл Futura.bmp.
    Приведенные выше примеры на VC не охватывают и малой части манипуляций с файлами формата DIB. Пример скорее демонстрирует разницу в подходах к программированию на VB и VC. Если вы технолог, которому надо только представить технологический процесс в более выгодном свете с помощью битовой карты, вам достаточно VB. Если вы эксперт в область битовой график, потребуется доступ к формату хранения и данным, представленным в нем, а это задача для VC.
    В завершении темы GDI32 и Visual Basic приводится пример, показывающий, как легко отобразить на экране монитора картографическую информацию (см. проект папка N7/). Отображение простых карт может понадобится многим технологам, работающим например в области ЖКХ, муниципального хозяйства, для учета объектов на местности итп. В примере используется простая база данных, созданная в Microsoft Access 97 (используется DAO). Данные из БД согласуются с картой небольшого города (например Соснового Бора Ленинградской области с населением около 70тыс. человек).
    Примечание. Если захотите открыть БД в Access Вам будет необходим пароль, который присваивается в событии Private Sub Form_Load() через объект Dim MyBaza As Database методом OpenDatabase.
    В папку WINDOWS/FONTS необходимо поместить файл с шрифтом TC05002T, так как при создании рисунка в формате WMF(из которого состоит карта) в некоторых надписях был применен этот шрифт. Для этого примера в папке (N7/ Release7) находится инсталляционная программа.
В связи с тем, что код примера достаточно большой просмотрите его сами в среде VB6. Вид интерфейса БД/Карта в работе представлен на рисунке 4 .


Рис. 4

Приведу только некоторые пояснения:
1.С помощью увеличительного стекла можно уменьшать и увеличивать изображение, а с помощью стрелок можно перемещаться по карте.
2. На рисунке 4 показано как на увеличенной части карты нанесен треугольник. Координаты треугольника будут занесены в БД. В реальном примере лучше помещать координаты в файл, а не БД(с другой стороны одна другая тысяча записей в БД для современного компьютера не проблема). С координатами треугольника будет связана фамилия, телефон, адрес и все что угодно по Вашему усмотрению. Когда в БД появится много записей Вы можете воспользоваться поиском. Поиск производится по средством SQL запроса вида "SELECT * FROM Таблица1 WHERE ФИО LIKE '" & Isk & "*' " '".
3. С API связанны следующие функции:

Public Declare Function SetMapMode& Lib "gdi32" (ByVal hDC As Long, _
						ByVal nMapMode As Long)
Public Declare Function SetWindowExtEx& Lib "gdi32" (ByVal hDC _
		As Long, ByVal nX As Long, ByVal nY As Long, lpSize As SIZE)
Public Declare Function SetWindowOrgEx& Lib "gdi32" (ByVal hDC _
	As Long, ByVal nX As Long, ByVal nY As Long, lpPoint As POINTAPI)
Public Declare Function SetViewportExtEx& Lib "gdi32" (ByVal hDC _
		As Long, ByVal nX As Long, ByVal nY As Long, lpSize As SIZE)
Public Declare Function SetViewportOrgEx& Lib "gdi32" (ByVal hDC _
	As Long, ByVal nX As Long, ByVal nY As Long, lpPoint As POINTAPI)
Public Declare Function GetDeviceCaps Lib "gdi32" (ByVal hDC As _
					Long, ByVal nIndex As Long) As Long

    Единственная задача для них вступать в работу когда Вы пользуетесь увеличительным стеклом, приводя в соответствие с коэффициентом масштабирования треугольник, показывающий местожительство выбранного из БД человека. Последняя функция предназначена для предотвращения запуска приложения при режиме разрешения экрана отличном от 96dpi. В утилите свойства экрана нажмите кнопку “Дополнительно” и измените размер шрифта на значение “Крупный шрифт”(120dpi). После перегрузки при попытке запустить приложение должно появиться предупреждение. В данном примере объединены такие возможности VB, как быстрое построение визуального интерфейса, подключение баз данных и доступ к графическому API GDI.