лекции / Shchupak_Yu._Win32_API_Razrabotka_prilozheniy_dlya_Windows
.pdfРастры |
191 |
|
|
|
|
|
if |
(!isFileLoaded) { |
MessageBox(hWnd, "Файл " FILE_NAME " не загружен.", "Error", MB_OK);
MessageBox(hWnd, bmp.GetError(), "Error", MB_OK); break;
}
// Подогнать размеры окна программы под размер растра bmp GetClientRect(hWnd, &rect);
dX = bmp.GetWidth() - rect.right; dY = bmp.GetHeight() - rect.bottom; GetWindowRect(hWnd, &rect); InflateRect(&rect, dX/2, dY/2);
MoveWindow(hWnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
ReleaseDC(hWnd, hDC); break;
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps);
wd = ws = bmp.GetWidth(); hd = hs = bmp.GetHeight();
bmp.Draw(hDC, 0, 0, wd, hd, 0, 0, ws, hs, SRCCOPY);
EndPaint(hWnd, &ps); break;
case WM_DESTROY: PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
//////////////////////////////////////////////////////////////////////
Возможно, первое, на что вы обратили внимание, — это макрос, задающий имя BMP файла, предназначенного для загрузки и отображения в окне программы. В данном случае файл называется YoungHacker.bmp. Но где же его взять? Можно заглянуть на сайт издательства «Питер» www.piter.com и найти там листинги приме ров к данной книге. В соответствующем каталоге нетрудно отыскать и этот графи ческий файл. Кроме того, можно выбрать любой другой BMP файл, поместить его
вкаталог проекта и записать его имя в указанном макросе.
Востальном код приложения достаточно прозрачен. Работающая программа выводит на экран изображение, показанное на рис. 3.7.
Следует обратить внимание на аргументы метода bmp.Draw, который реализован при помощи функции StretchDIBits. Значения аргументов определяют, что изобра жение выводится в своем исходном размере (без масштабирования) и в своей ис ходной ориентации.
Давайте проведем следующий эксперимент для иллюстрации возможностей функции StretchDIBits. Нужно внести небольшие изменения в оконную процедуру. Добавьте в нее определения следующих переменных:
int xd0, yd0, xd1, yd1, xd2, yd2, xd3, yd3;
192 |
Глава 3. GDI. Палитры, растры, метафайлы |
|
|
Рис. 3.7. Вывод растра из файла YoungHacker.bmp при помощи программы BmpFileViewer
Рис. 3.8. Результат работы модифицированной программы BmpFileViewer
Растры |
193 |
|
|
|
|
А блок обработки сообщения WM_PAINT замените новым фрагментом кода:
case WM_PAINT: |
|
|
hDC = BeginPaint(hWnd, |
&ps); |
|
ws = bmp.GetWidth(); |
|
|
hs = bmp.GetHeight(); |
|
|
wd = ws/2 - 4; |
|
|
hd = hs/2 - 4; |
|
|
xd0 = 2; |
yd0 = 2; |
|
xd1 = ws/2 + 2; |
yd1 = 2; |
|
xd2 = 2; |
yd2 = hs/2 + 2; |
|
xd3 = ws/2 + 2; |
yd3 = hs/2 + 2; |
|
SetStretchBltMode(hDC, STRETCH_HALFTONE);
bmp.Draw(hDC, xd0, yd0, wd, hd, ws, 0, -ws, hs, SRCCOPY); bmp.Draw(hDC, xd1, yd1, wd, hd, 0, 0, ws, hs, SRCCOPY); bmp.Draw(hDC, xd2, yd2, wd, hd, ws, hs, -ws, -hs, SRCCOPY); bmp.Draw(hDC, xd3, yd3, wd, hd, 0, hs, ws, -hs, SRCCOPY); EndPaint(hWnd, &ps);
return 0;
Теперь программа выведет картинку, показанную на рис. 3.8.
Аппаратно-зависимые растры
Аппаратно зависимый растр (DDB) представляет собой объект GDI, который фун кционирует под управлением GDI и драйверов устройств. Он обладает тем же ста тусом, что и объекты логического пера или логической кисти. При создании DDB растра Windows определяет его внутренний формат и выделяет память из области памяти GDI. После этого все операции с растром выполняются через дескриптор объекта GDI, имеющий тип HBITMAP. DDB растры также часто называют «растро выми объектами GDI». Напомним, что особенностью формата DDB является то, что система всегда создает битовый образ в памяти с учетом характеристик конк ретного графического оборудования.
Создание DDB-растра в программе
Win32 GDI предлагает следующие функции для создания растровых объектов GDI:
HBITMAP |
CreateBitmap(int nWidth, |
int |
nHeight, |
UINT cPlanes, |
|
UINT cBitsPerPel, CONST VOID* lpvBits); |
|
|
|||
HBITMAP |
CreateBitmapIndirect(CONST |
BITMAP* |
lpbm); |
|
|
HBITMAP |
CreateCompatibleBitmap(HDC |
hdc, |
int |
nWidth, |
int nHeight); |
Функция CreateBitmap создает растр DDB с заданными шириной nWidth, высотой nHeight, количеством плоскостей cPlanes, количеством битов на пиксел cBitsPerPel, после чего инициализирует массив пикселов данными из массива lpvBits. Параметры cPlanesи cBitsPerPelопределяют лишь минимальные требования к растру. В свою оче редь, GDI поручает драйверу устройства выбор ближайшего доступного формата с кодировкой по крайней мере cPlanes × cBitsPerPel бит/пиксел.
Функция CreateBitmapIndirect делает практически то же самое, но параметры ра стра передаются ей через структуру типа BITMAP:
typedef |
struct tagBITMAP { |
|
LONG |
bmType; |
// тип растра (поле должно быть равно 0) |
LONG |
bmWidth; |
// ширина растра в пикселах |
LONG |
bmHeight; |
// высота растра в пикселах |
194 |
|
|
|
|
Глава 3. GDI. Палитры, растры, метафайлы |
|
|
|
|
|
|
|
|
LONG |
bmWidthBytes; |
// |
число |
байтов на |
строку развертки |
|
|
|
|
//(значение должно |
быть четным) |
||
WORD |
bmPlanes; |
|
// |
число |
цветовых |
плоскостей |
WORD |
bmBitsPixel; |
// |
число |
битов на пиксел |
||
LPVOID bmBits; |
// указатель на массив битов с данными инициализации |
|||||
} BITMAP; |
|
|
|
|
|
|
К сожалению, обе функции имеют существенные ограничения, влияющие на их практическое применение. Они позволяют создавать либо монохромный растр, инициализированный данными из массива, либо цветной растр, но без инициализации. При создании растра без инициализации параметр lpvBits или поле lpbm.bmBits должны иметь значение NULL. Попытка создать цветной растр, инициализированный данными из конкретного массива, как правило, заканчи вается неудачей, так как созданный битовый образ оказывается несовместимым с конкретным графическим устройством.
Функция CreateCompatibleBitmap предназначена для создания DDB растра с заданными шириной и высотой, который заведомо совместим с контекстом уст ройства hdc. Но битовый образ, создаваемый этой функцией, также является не инициализированным.
Создание DDB-растра из упакованного DIB
Итак, рассмотренные выше функции мало пригодны для создания инициализиро ванных цветных DDB растров. В то же время аппаратно независимый растр обла дает хорошими средствами для описания стандартных цветовых форматов. Поэто му GDI содержит функцию CreateDIBitmap, которая создает инициализированный DDB растр на базе DIB, то есть, по существу, преобразует упакованный DIB в DDB. Функция имеет следующий прототип:
HBITMAP CreateDIBitmap(HDC hdc, CONST BITMAPINFOHEADER* lpbmih, DWORD fdwInit, CONST VOID* lpbInit, CONST BITMAPINFO* lpbmi,
UINT fuUsage);
В своей реализации она сначала создает DDB растр, совместимый с контек стом устройства hdc, а затем преобразует DIB в DDB. Параметр lpbmih содержит адрес заголовка информационного блока DIB. Параметр fdwInit определяет необхо димость инициализации DDB растра. Если он равен CBM_INIT, то следующие три параметра ссылаются на упакованный DIB растр, используемый для инициализа ции. Параметр lpbInit указывает на массив пикселов, а параметр lpbmi — на заголо вок информационного блока DIB, преобразованный к типу BITMAPINFO. Последний параметр, fuUsage, сообщает, содержит цветовая таблица индексы палитры (DIB_PAL_COLORS) или цвета RGB (DIB_RGB_COLORS).
Честно говоря, не совсем понятно, для чего нужна эта функция, поскольку если DIB растр загружен, как, например, в коде листинга 3.6, то никаких проблем с его выводом не будет. Для этого вызывается функция StretchDIBits (в классе KDib она инкапсулирована в метод Draw). Функция CreateDIBitmap позволяет преобразовать этот же DIB растр, предварительно загруженный в память, в объект DDB растра, который затем может быть отображен при помощи функции BitBlt (подробности вывода DDB растров рассматриваются ниже). Но остается вопрос: а что дает эта более сложная схема?
Все же основные способы создания DDB растров сводятся к рассматриваемым ниже двум функциям.
Растры |
195 |
|
|
|
|
Загрузка DDB-растра из ресурсов приложения
В Windows программировании DIB растры (BMP файлы) часто присоединяются к модулю в виде ресурса1, а затем загружаются в программу функцией LoadBitmap, которая преобразует растр к типу HBITMAP:
HBITMAP LoadBitmap(
HINSTANCE hInstance, // дескриптор экземпляра приложения LPCTSTR lpBitmapName // имя растрового ресурса
);
Как показано в главе 5, имя ресурса может быть либо строкой с завершающим нуль символом, например szBmpName1, либо целочисленным идентификатором, например IDB_1. В первом случае параметру lpBitmapName передается адрес строки szBmpName1, а во втором — выражение, преобразующее целочисленный идентифи катор к типу LPCTSTR при помощи макроса MAKEINTRESOURCE, то есть параметр полу чает значение MAKEINTRESOURCE(IDB_1).
Растровые ресурсы хранятся в модулях Win32 в формате упакованного DIB растра. Функция LoadBitmap находит растровый ресурс, загружает его в память, получает дескриптор упакованного DIB растра и затем на его базе создает DDB растр, совместимый с текущим экранным режимом.
Пример использования функции LoadBitmap можно найти в листинге 5.4 и в лис тинге 12.1.
Приложение может также вызвать функцию LoadBitmap для загрузки предопре деленных в системе растров с изображениями различных кнопок, которые исполь зует Win32 API. Для этого необходимо передать параметру hInstance значение NULL, а второму параметру — одно из значений OBM_BTNCORNERS, OBM_BTSIZE и других аналогичных констант.
Загрузка DDB-растра из BMP-файла
Если по какой то причине вам удобно загружать битовый образ не из ресурса, а не посредственно из BMP файла, то можно воспользоваться многопрофильной функ цией LoadImage, предназначенной для загрузки пиктограмм, курсоров и растров. Функция имеет следующий прототип:
HANDLE LoadImage( |
|
HINSTANCE hinst, |
// дескриптор экземпляра приложения |
LPCTSTR lpszName, |
// имя или идентификатор изображения |
UINT uType, |
// тип изображения |
int cxDesired, |
// ширина изображения |
int cyDesired, |
// высота изображения |
UINT fuLoad |
// флаги загрузки |
); |
|
Имя загружаемого растра передается параметру lpszName, при этом параметр fuLoad должен иметь значение LR_LOADFROMFILE, а параметр uType — значение IMAGE_BITMAP. Установка нулевых значений для параметров cxDesired и cyDesired приводит к использованию текущих размеров загружаемого растра.
В случае успешного завершения функция возвращает дескриптор полученно го DDB растра. Если загрузить изображение не удалось, то возвращается значе ние NULL.
Пример использования функции LoadImage приведен в листинге 3.7.
1 Более подробно ресурсы Windows приложения рассматриваются в главе 5.
196 |
Глава 3. GDI. Палитры, растры, метафайлы |
|
|
Получение информации о графическом объекте
После создания или загрузки изображения в формате DDB приложению необхо димо узнать его размеры, чтобы использовать их для отображения растра. Для решения этой проблемы можно вызвать функцию GetObject, имеющую следующий прототип:
int GetObject( |
|
HGDIOBJ hgdiobj, |
// дескриптор графического объекта |
int cbBuffer, |
// размер буфера для информации об объекте |
LPVOID lpvObject |
// адрес буфера для информации об объекте |
); |
|
Функция является универсальной, поэтому в параметре hgdiobj может быть ука зан дескриптор одного из таких объектов, как перо, кисть, шрифт, палитра или растр типа HBITMAP. Информацию об объекте функция помещает в буфер с адресом lpvObject. В табл. 3.7 показано, каким должен быть тип буфера в зависи мости от типа объекта.
Таблица 3.7. Тип буфера, на который указывает параметр lpvObject
Тип объекта |
Тип буфера |
|
|
HBITMAP |
BITMAP |
HBITMAP, возвращенный |
DIBSECTION, если поле cbBuffer установлено в значение |
функцией CreateDIBSection |
sizeof(DIBSECTION), или BITMAP, если поле cbBuffer установлено |
|
в значение sizeof(BITMAP) |
HPALETTE |
WORD — количество цветов в логической палитре |
HPEN |
LOGPEN |
HPEN, возвращенный |
EXTLOGPEN |
функцией ExtCreatePen |
|
HBRUSH |
LOGBRUSH |
HFONT |
LOGFONT |
|
|
Если при вызове функции параметр lpvObject равен NULL, то функция возвраща ет количество байтов, которое необходимо зарезервировать в буфере lpvObject для размещения информации об объекте.
Если параметр lpvObject не равен NULL, то при успешном завершении функция возвращает количество сохраненных в буфере байтов. При возникновении ошиб ки функция возвращает нулевое значение.
Когда функция GetObject применяется к объекту типа HBITMAP, то возвращае мая информация содержит данные только о ширине и высоте растра, а также о цветовом формате изображения. Поле bmBits структуры BITMAP при этом равно нулю.
Отображение DDB-растра
Несмотря на то что DDB растры играют важную роль в Windows программирова нии, в Win32 API нет функции для непосредственного отображения DDB. В GDI задача отображения DDB решается обобщенно, как задача пересылки пря моугольного массива пикселов с одного графического устройства (устройства источника) на другое графическое устройство (устройство приемник).
При отображении растра устройством приемником обычно является экран дисплея, и его представителем выступает контекст дисплея.
Растры |
197 |
|
|
|
|
А что в этом случае является устройством источником? В роли этого устрой ства выступает виртуальное графическое устройство, имитируемое в памяти в виде растра. Для его представления в GDI предусмотрен контекст устройства в памя ти (или совместимый контекст). В главе 2 была дана краткая характеристика это го вида контекста устройства. Напомним, что совместимый контекст создается следующей функцией:
HDC CreateCompatibleDC (HDC hdc);
где hdc — дескриптор существующего контекста устройства, с которым должен быть совместим создаваемый контекст. Функция CreateCompatibleDCможет использовать ся только с устройствами, поддерживающими растровые операции. Чаще всего hdc является дескриптором контекста дисплея, который относится к клиентской облас ти окна приложения. Если этот параметр имеет значение NULL, то функция создает контекст в памяти, совместимый со всем экраном дисплея. Если функция заверша ется успешно, то она возвращает дескриптор совместимого контекста устройства.
Когда совместимый контекст устройства создан, с ним по умолчанию связан монохромный растр из одного пиксела. Поэтому, прежде чем использовать этот контекст, необходимо выбрать в него DDB растр при помощи функции SelectObject.
Типичная схема отображения DDB растра с дескриптором hBitmap на графичес кое устройство с дескриптором контекста устройства hDC выглядит следующим образом:
//Определить объект совместимого контекста HDC hMemDC;
//Создать совместимый контекст
hMemDC = CreateCompatibleDC(hDC);
//Выбрать DDB-растр в совместимый контекст SelectObject(hMemDC, hBitmap);
//Скопировать изображение из hMemDC в hDC
BitBlt(hDC, 0, 0, bmpWidth, bmpHeight, hMemDC, 0, 0, SRCCOPY); // Удалить совместимый контекст
DeleteDC(hMemDC);
Очевидно, что устройство источник и устройство приемник в рассмотренной схеме можно поменять местами. В этом случае будет осуществляться копирова ние изображения с устройства hDC на устройство hMemDC, то есть в DDB растр.
Функции блиттинга
Для поддержки блиттинга1 GDI использует функции BitBlt и StretchBlt. Рассмот рим подробнее первую из них.
Название функции BitBlt происходит от слов «bit block transfer», означающих «перенос битового блока». На самом деле функция BitBlt позволяет делать намного больше, чем просто перенос блока пикселов. Эта функция может выполнить одну из 256 логических растровых операций над тремя наборами пикселов. Прототип функции имеет следующий вид:
BOOL BitBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, HDC hdcSrc, int nXSrc, int nYSrc, DWORD dwRop);
Функция BitBlt передает прямоугольный блок пикселов с поверхности устрой ства источника hdcSrc на поверхность устройства приемника hdcDest. Исходный прямоугольник определяется параметрами nXSrc, nYSrc, nWidth, nHeight в логической
1 В компьютерной графике блиттингом называют копирование изображений в память видеоустройств.
198 |
Глава 3. GDI. Палитры, растры, метафайлы |
|
|
системе координат контекста устройства источника. Приемный прямоугольник определяется параметрами nXDest, nYDest, nWidth, nHeight в логической системе ко ординат контекста устройства приемника. Оба контекста устройства должны под держивать растровые операции RC_BITBLT.
Из списка параметров функции видно, что ширина и высота исходного и прием ного прямоугольников задаются одними и теми же параметрами — nWidth и nHeight. Поэтому если в устройстве приемнике и в устройстве источнике уста новлен режим отображения по умолчанию MM_TEXT, в котором логические единицы совпадают с физическими единицами, то перенос блока пикселов происходит без масштабирования.
В других режимах отображения исходный и приемный прямоугольники могут иметь разные размеры в системах координат устройства. В таком случае исход ное изображение масштабируется по размерам приемного прямоугольника.
Контексты устройства источника и устройства приемника не обязательно должны быть разными. Функцию BitBltможно применять и для переноса блока пикселов в пре делах той же поверхности, на которой находится исходный прямоугольник.
Последний параметр dwRop задает тернарную растровую операцию, то есть спо соб объединения исходного пиксела, приемного пиксела и кисти для формирова ния нового значения приемного пиксела. Эти операции будут рассмотрены позже, а пока что будет использоваться простейшая растровая операция SRCCOPY, которая просто заменяет пикселы приемника значениями пикселов источника.
Вторая функция блиттинга StretchBlt имеет следующий прототип:
BOOL StretchBlt(HDC hdcDest, int nXDest, int nYDest, int nWidthDest, int nHeightDest, HDC hdcSrc, int nXSrc, int nYSrc, int nWidthSrc, int nHeightSrc, DWORD dwRop);
Она делает практически то же самое, что и функция BitBlt, но в списке парамет ров размеры исходного и приемного прямоугольников определяются независимо друг от друга. Это позволяет задавать масштабирование при переносе блока пиксе лов, когда исходный и приемный прямоугольники имеют разные размеры в логических координатах. Еще одно различие — функция может осуществлять зеркальные отражения изображения так же, как это делает функция StretchDIBits, если параметры исходного (или приемного) прямоугольника задавать по прави лам, приведенным в табл. 3.6.
Пример работы с DDB
Программа, приведенная в листинге 3.7, осуществляет загрузку DDB растра из BMP файла, после чего отображает рисунок в своем окне. По существу, здесь решается та же задача, что и в программе BmpFileViewer (см. листинг 3.6), но другими средст вами.
Листинг 3.7. Проект BmpFileViewer2
//////////////////////////////////////////////////////////////////////
// BmpFileViewer2.cpp #include <windows.h> #include "KWnd.h"
#define FILE_NAME "YoungHacker.bmp"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
Растры |
199 |
|
|
|
|
//====================================================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
KWnd mainWnd("BmpFileViewer2", hInstance, nCmdShow, WndProc); while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
//==================================================================== LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hDC; PAINTSTRUCT ps; RECT rect;
static int bmpWidth, bmpHeight;
static HBITMAP hBitmap; |
// дескриптор битового образа |
|
HINSTANCE hInst; |
|
|
HDC |
hMemDC; |
// дескриптор совместимого контекста |
BITMAP bm; |
|
|
int |
dX, dY; |
|
switch (uMsg)
{
case WM_CREATE:
hDC = GetDC(hWnd);
hInst = (HINSTANCE)GetClassLong(hWnd, GCL_HMODULE);
hBitmap = (HBITMAP)LoadImage(hInst, FILE_NAME, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (hBitmap == NULL) {
MessageBox(hWnd, "Файл " FILE_NAME " не загружен", "Error", MB_OK); break;
}
GetObject(hBitmap, sizeof(bm), &bm); bmpWidth = bm.bmWidth;
bmpHeight = bm.bmHeight;
// Подогнать размеры окна программы под размер растра hBitmap GetClientRect(hWnd, &rect);
dX = bmpWidth - rect.right; dY = bmpHeight - rect.bottom; GetWindowRect(hWnd, &rect);
InflateRect(&rect, dX/2, dY/2); MoveWindow(hWnd, rect.left, rect.top,
rect.right-rect.left, rect.bottom-rect.top, TRUE); ReleaseDC(hWnd, hDC);
break;
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps);
// Создать совместимый контекст
hMemDC = CreateCompatibleDC(hDC);
продолжение
200 Глава 3. GDI. Палитры, растры, метафайлы
Листинг 3.7. (продолжение)
// Выбрать hBitmap в совместимый контекст SelectObject(hMemDC, hBitmap);
// Скопировать изображение из hMemDC в hDC
BitBlt(hDC, 0, 0, bmpWidth, bmpHeight, hMemDC, 0, 0, SRCCOPY);
DeleteDC(hMemDC); EndPaint(hWnd, &ps); break;
case WM_DESTROY: PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
//////////////////////////////////////////////////////////////////////
Следует обратить внимание на несколько интересных моментов. Так, DDB растр hBitmap загружается в блоке обработки сообщения WM_CREATE при помощи функции LoadImage. Дескриптор экземпляра приложения hInst, который нужно передать в функцию LoadImage, определяется предварительным вызовом функции GetClassLong(hWnd, GCL_HMODULE). После загрузки растра его ширина и высота нахо дятся при помощи функции GetObject.
Отображение растра в клиентской области окна реализовано в блоке обработ ки сообщения WM_PAINT по традиционной схеме. Сначала создается совместимый контекст (контекст устройства в памяти) hMemDC, после чего в него выбирается битовый образ hBitmap. Затем блок пикселов копируется при помощи функции BitBlt. В завершение совместимый контекст удаляется.
Результат выполнения этой программы выглядит так же, как и результат вы полнения программы BmpFileViewer (см. рис. 3.7).
DIB-секции
Каждый из рассмотренных выше типов растров обладает своими недостатками и достоинствами. Сравнительная характеристика DDB и DIB приведена в табл. 3.8.
Таблица 3.8. Сравнение растров DDB и DIB
Характеристика |
DDB |
DIB |
|
|
|
Независимость от аппаратуры |
Íåò |
Äà |
Рисование на растре средствами GDI |
Поддерживается |
Не поддерживается |
Прямой доступ к массиву пикселов |
Отсутствует |
Имеется |
со стороны приложения |
|
|
Максимальный размер растра |
48 Mбайт (используется |
Ограничен объемом виртуаль- |
|
системная память) |
ного адресного пространства |
|
|
(используется память уровня |
|
|
приложения) |
|
|
|
ВWin32 API появился новый тип растра, объединяющий достоинства DDB
иDIB.
