Графический интерфейс GDI в Microsoft Windows© Александр Фролов, Григорий ФроловТом 4, М.: Диалог-МИФИ, 1993, 288 стр. 4.5. Приложение BMPINFOПосле всего сказанного выше у вас могло сложиться впечатление, что процедура рисования изображений DIB значительно труднее, чем процедура рисования файлов DDB. В самом деле, для отображения содержимого bmp-файла вы должны считать его в память, проверить формат всех структур, при необходимости создать палитру и следить за ее изменениями со стороны других приложений. В момент рисования вам нужно подготовить значительное количество структур, указателей и параметров. Увы, для рисования изображений DIB в программном интерфейсе GDI операционной системы Windows версии 3.1 нет ничего более удобного, чем описанные нами функции. Большинство приложений, тем не менее, нуждается в рисовании изображений DIB. Для иллюстрации методов работы с такими изображениями мы подготовили приложение BMPINFO. Это приложение умеет загружать bmp-файлы любых "легальных" форматов Windows и Presentation Manager версии 1.x, и выводит соответствующую информацию из заголовков этих файлов, однако оно способно рисовать только некомпрессованные изображения в формате Windows. Как мы уже говорили, в большинстве случаев это как раз именно то, что нужно. В меню "File" есть строки "Open", "Info..." и, конечно, "Exit". С помощью строки "Open" вы можете выбрать bmp-файл. Если это некомпрессованный файл в формате Windows, он рисуется в окне приложения (рис. 4.7).
Рис. 4.7. Главное окно приложения BMPINFO Выбрав из меню "File" строку "Info...", вы можете просмотреть информацию о файле, такую, как размер файла и заголовка, формат файла, размер изображения в пикселах и т. д. (рис. 4.8).
Рис. 4.8. Информация о bmp-файле Если выбранный bmp-файл имеет формат Presentation Manager, на экране появится диалоговая панель, аналогичная изображенной на рис. 4.8. Основной файл исходных текстов приложения BMPINFO приведен в листинге 4.6. Листинг 4.6. Файл bmpinfo/bmpinfo.cpp // ---------------------------------------- // Приложение BMPINFO // Просмотр и анализ bmp-файлов в формате DIB // ---------------------------------------- #define STRICT #include <windows.h> #include <windowsx.h> #include <commdlg.h> #include <mem.h> #pragma hdrstop #include "dib.hpp" #include "bmpinfo.hpp" // Прототипы функций BOOL InitApp(HINSTANCE); LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM); // Имя класса окна char const szClassName[] = "BmpInfoClass"; // Заголовок окна char const szWindowTitle[] = "Bitmap Information"; // Размеры внутренней области окна short cxClient, cyClient; // ===================================== // Функция WinMain // ===================================== #pragma argsused int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; // структура для работы с сообщениями HWND hwnd; // идентификатор главного окна приложения // Инициализируем приложение if(!InitApp(hInstance)) return FALSE; // После успешной инициализации приложения создаем // главное окно приложения hwnd = CreateWindow( szClassName, // имя класса окна szWindowTitle, // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна CW_USEDEFAULT, // задаем размеры и расположение CW_USEDEFAULT, // окна, принятые по умолчанию CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, NULL); // Если создать окно не удалось, завершаем приложение if(!hwnd) return FALSE; // Рисуем главное окно ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); // Запускаем цикл обработки сообщений while(GetMessage(&msg, 0, 0, 0)) { DispatchMessage(&msg); } return msg.wParam; } // ===================================== // Функция InitApp // Выполняет регистрацию класса окна // ===================================== BOOL InitApp(HINSTANCE hInstance) { ATOM aWndClass; // атом для кода возврата WNDCLASS wc; // структура для регистрации // класса окна // Записываем во все поля структуры нулевые значения memset(&wc, 0, sizeof(wc)); // Подключаем меню wc.lpszMenuName = "APP_MENU"; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH); wc.lpszClassName = (LPSTR)szClassName; // Регистрация класса aWndClass = RegisterClass(&wc); return (aWndClass != 0); } // ===================================== // Функция WndProc // ===================================== LRESULT CALLBACK _export WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; static HFILE hfDIBFile; static HDIB hDib; static HPALETTE hPal, hOldPal; // Размер файла static DWORD dwFileSize; switch (msg) { case WM_CREATE: { hfDIBFile = NULL; hDib = NULL; hPal = NULL; return 0; } // При изменении размеров окна сохраняем // новые значения для ширины и высоты case WM_SIZE: { cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; } // Рисование в окне case WM_PAINT: { // Получаем контекст отображения для // рисования во внутренней области окна hdc = BeginPaint(hwnd, &ps); // Если DIB был загружен, и он в формате // некомпрессованного bmp-файла для Windows, // рисуем его if((hDib != NULL) && (DIBType(hDib) == WINRGB_DIB)) { // Если при загрузке была создана палитра, // выбираем ее if(hPal) { hOldPal = SelectPalette(hdc, hPal, FALSE); RealizePalette(hdc); } // Рисуем DIB DIBPaint(hdc, 0, 0, hDib); // Выбираем старую палитру if(hPal) { SelectPalette(hdc, hOldPal, FALSE); } } // Для других форматов bmp-файлов выводим // информацию из заголовка файла else { if(hDib) DIBInfo(hDib, dwFileSize); } // Освобождаем контекст отображения EndPaint(hwnd, &ps); return 0; } // Обработка сообщений от меню case WM_COMMAND: { switch (wParam) { case CM_HELPABOUT: { MessageBox(hwnd, "Bitmap Information, v.1.0\n" "(C) Frolov A.V., 1994", "About BMPINFO", MB_OK | MB_ICONINFORMATION); return 0; } // Загрузка bmp-файла case CM_FILEOPEN: { // Выбираем файл hfDIBFile = DIBSelectFile(); if(hfDIBFile != NULL) { // Читаем файл в память hDib = DIBReadFile(hfDIBFile, &dwFileSize); // Если файл прочитан, создаем палитру на // базе таблицы цветов. Если таблицы цветов нет, // палитра не создается if((hDib != NULL) && (DIBType(hDib) == WINRGB_DIB)) { hPal = DIBCreatePalette(hDib); } // Перерисовываем окно InvalidateRect(hwnd, NULL, TRUE); } return 0; } // Выводим диалоговую панель с информацией из // заголовка bmp-файла case CM_FILEINFO: { if(hDib != NULL) DIBInfo(hDib, dwFileSize); return 0; } // Завершаем работу приложения case CM_FILEEXIT: { DestroyWindow(hwnd); return 0; } default: return 0; } } // Это сообщение приходит при изменении // системной палитры. Наше приложение в ответ // на это сообщение заново реализует свою логическую // палитру и при необходимости перерисовывает окно case WM_PALETTECHANGED: { // Если это не наше окно, передаем управление // обработчику сообщения WM_QUERYNEWPALETTE if (hwnd == (HWND) wParam) break; } // В ответ на это сообщение приложение должно // реализовать свою логическую палитру и // обновить окно case WM_QUERYNEWPALETTE: { HDC hdc; HPALETTE hOldPal; int nChanged; // Выбираем логическую палитру в // контекст отображения hdc = GetDC(hwnd); // При обработке сообщения WM_QUERYNEWPALETTE // палитра выбирается для активного окна, // а при обработке сообщения WM_PALETTECHANGED - // для фонового hOldPal = SelectPalette(hdc, hPal, (msg == WM_QUERYNEWPALETTE) ? FALSE : TRUE); // Реализуем логическую палитру и выбираем // ее в контекст отображения nChanged = RealizePalette(hdc); SelectPalette(hdc, hOldPal, TRUE); // Освобождаем контекст отображения ReleaseDC(hwnd, hdc); // Если были изменения палитры, // перерисовываем окно if(nChanged) InvalidateRect(hwnd, NULL, TRUE); return nChanged; } case WM_DESTROY: { // Удаляем логическую палитру if(hPal) DeletePalette(hPal); PostQuitMessage(0); return 0; } default: break; } return DefWindowProc(hwnd, msg, wParam, lParam); } Обработчик сообщения WM_CREATE сбрасывает содержимое переменных, в которых находятся идентификатор открытого bmp-файла, идентификатор загруженного изображения DIB, а также идентификатор палитры: hfDIBFile = NULL; hDib = NULL; hPal = NULL; Обработчик сообщения WM_PAINT, рисующий изображение DIB, достаточно прост: hdc = BeginPaint(hwnd, &ps); if((hDib != NULL) && (DIBType(hDib) == WINRGB_DIB)) { if(hPal) { hOldPal = SelectPalette(hdc, hPal, FALSE); RealizePalette(hdc); } DIBPaint(hdc, 0, 0, hDib); if(hPal) { SelectPalette(hdc, hOldPal, FALSE); } } else { if(hDib) DIBInfo(hDib, dwFileSize); } EndPaint(hwnd, &ps); return 0; Если загружено изображение DIB, его идентификатор отличен от NULL. В этом случае вызывается функция DIBType, определенная в нашем приложении в файле dib.cpp (листинг 4.8). Эта функция выполняет все необходимые проверки полей структуры заголовка изображения и возвращает тип изображения. Для некомпрессованных изображений DIB в формате Windows возвращается значение WINRGB_DIB. Если при загрузке bmp-файла выяснилось, что он содержит таблицу цветов, создается палитра, идентификатор которой записывается в переменную hPal. Если содержимое этой переменной отлично от NULL, перед рисованием обработчик сообщения WM_PAINT выбирает палитру в контекст отображения и реализует ее, вызывая функции SelectPalette и RealizePalette. Далее вызывается функция DIBPaint, определенная в файле dib.cpp, которая рисует изображение DIB. В качестве параметров этой функции передается идентификатор контекста отображения, координаты (x,y) левого верхнего угла прямоугольной области, в которой нужно нарисовать изображение, и идентификатор загруженного изображения DIB. После этого восстанавливается старая палитра (если были изменения палитры). Если же было загружено изображение DIB в формате Presentation Manager, вызывается функция DIBInfo, которая выводит на экран диалоговую панель с параметрами этого изображения. При выборе в меню "File" строки "Open" получает управление обработчик сообщения WM_COMMAND. Этот обработчик вызывает функцию DIBSelectFile, определенную в файле dib.cpp. Функция DIBSelectFile выводит на экран стандартную диалоговую панель "Open" и позволяет вам выбрать для загрузки любой файл. Идентификатор открытого файла записывается в переменную hfDIBFile. Далее файл читается в память, для чего вызывается функция DIBReadFile, определенная в файле dib.cpp. Функция читает весь файл в заказанный ей глобальный блок памяти, причем перед возвратом управления отмечает этот блок как перемещаемый и возвращает идентификатор блока памяти. Дополнительно она записывает размер файла в байтах в переменную, адрес которой указан ей в качестве второго параметра. После удачного чтения с помощью функции DIBType определяется тип файла. Если это некомпрессованный файл в формате Windows, вызывается функция DIBCreatePalette, которая проверяет наличие таблицы цветов и при необходимости создает логическую палитру, записывая ее идентификатор в переменную hPal. Затем для перерисовки окна и отображения загруженного изображения вызывается функция InvalidateRect. Обработчики сообщений об изменении системной палитры WM_PALETTECHANGED и WM_QUERYNEWPALETTE аналогичны использованным в приложении PALETTE, поэтому мы не будем их описывать еще раз. Перед завершением работы приложения функция окна удаляет логическую палитру, если она была создана, вызывая макрокоманду DeletePalette. Идентификаторы строк меню описаны в файле bmpihfo.hpp (листинг 4.7). Листинг 4.7. Файл bmpinfo/bmpinfo.hpp #define CM_HELPABOUT 301 #define CM_FILEOPEN 302 #define CM_FILEINFO 303 #define CM_FILEEXIT 304 Все функции, предназначенные для работы с bmp-файлами и изображениями DIB, загруженными в оперативную память, мы вынесли в отдельный файл dib.cpp (листинг 4.8). Листинг 4.8. Файл bmpinfo/dib.cpp // ----------------------------------------------------- // Функции для работы с файлами в формате DIB // ----------------------------------------------------- #define STRICT #include <windows.h> #include <windowsx.h> #include <commdlg.h> #include <mem.h> #pragma hdrstop #include "dib.hpp" // ------------------------------- // Функция DIBSelectFile // Выбор DIB-файла // ------------------------------- HFILE DIBSelectFile(void) { // Структура для выбора файла OPENFILENAME ofn; // Буфер для записи пути к выбранному файлу char szFile[256]; // Буфер для записи имени выбранного файла char szFileTitle[256]; // Фильтр расширений имени файлов char szFilter[256] = "Bitmap Files\0*.bmp;*.dib;*.rle\0Any Files\0*.*\0"; // Идентификатор открываемого файла HFILE hf; // Инициализация имени выбираемого файла // не нужна, поэтому создаем пустую строку szFile[0] = '\0'; // Записываем нулевые значения во все поля // структуры, которая будет использована для // выбора файла memset(&ofn, 0, sizeof(OPENFILENAME)); // Инициализируем нужные нам поля // Размер структуры ofn.lStructSize = sizeof(OPENFILENAME); // Идентификатор окна ofn.hwndOwner = NULL; // Адрес строки фильтра ofn.lpstrFilter = szFilter; // Номер позиции выбора ofn.nFilterIndex = 1; // Адрес буфера для записи пути // выбранного файла ofn.lpstrFile = szFile; // Размер буфера для записи пути // выбранного файла ofn.nMaxFile = sizeof(szFile); // Адрес буфера для записи имени // выбранного файла ofn.lpstrFileTitle = szFileTitle; // Размер буфера для записи имени // выбранного файла ofn.nMaxFileTitle = sizeof(szFileTitle); // В качестве начального каталога для // поиска выбираем текущий каталог ofn.lpstrInitialDir = NULL; // Определяем режимы выбора файла ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; // Выбираем входной файл if (GetOpenFileName(&ofn)) { // Открываем выбранный файл hf = _lopen(ofn.lpstrFile, OF_READ); // Возвращаем идентификатор файла return hf; } // При отказе от выбора возвращаем // нулевое значение else return 0; } // ------------------------------- // Функция DIBReadFile // Чтение DIB-файла // ------------------------------- HDIB DIBReadFile(HFILE hfDIBFile, DWORD *dwFileSize) { // Идентификатор глобального блока // памяти, который будет использован для // чтения файла HDIB hDib; HCURSOR hCursor; // идентификатор курсора // Указатель на глобальный блок памяти LPDIB lpBuf; // Курсор в виде песочных часов hCursor = SetCursor (LoadCursor(NULL, IDC_WAIT)); // Определяем размер файла. Для этого // устанавливаем текущую позицию на // конец файла *dwFileSize = _llseek(hfDIBFile, 0l, 2); // Устанавливаем текущую позицию // на начало файла _llseek(hfDIBFile, 0l, 0); // Заказываем глобальный блок памяти, // размер которого равен длине файла hDib = (HDIB)GlobalAlloc(GMEM_FIXED, *dwFileSize); lpBuf = (unsigned char huge *)GlobalLock(hDib); // Если мало свободной памяти, // возвращаем признак ошибки if(lpBuf == NULL) return(NULL); // Читаем файл в полученный блок памяти _hread(hfDIBFile, lpBuf, *dwFileSize); // Восстанавливаем курсор SetCursor (hCursor); // Расфиксируем память GlobalUnlock(hDib); // Закрываем файл _lclose(hfDIBFile); return hDib; } // ------------------------------- // Функция DIBInfo // Вывод диалоговой панели с информацией // о DIB-файле // ------------------------------- BOOL DIBInfo(HDIB hDib, DWORD dwFileSize) { char szBuf[256], szBuf1[256]; DWORD biSize; LPBITMAPFILEHEADER lpDIBFileHeader; LPBITMAPINFOHEADER lpDIBInfoHeader; LPBITMAPCOREHEADER lpDIBCoreHeader; DWORD bfOffset; BOOL bWDIB; LPDIB lpDIBPtr; int nDIBType; WORD wNumColors; // Определяем тип битового изображения nDIBType = DIBType(hDib); if(!nDIBType) // если ошибка, выдаем сообщение { MessageBox(NULL, "Ошибка в формате DIB-файла", "Bitmap Info", MB_OK | MB_ICONHAND); return FALSE; } // Фиксируем область памяти, в которую загружен DIB lpDIBPtr = (unsigned char huge *)GlobalLock(hDib); if(lpDIBPtr == NULL) return(FALSE); lpDIBFileHeader = (LPBITMAPFILEHEADER)lpDIBPtr; // Определяем смещение бит изображения // и размер заголовка bfOffset = lpDIBFileHeader->bfOffBits; biSize = (DWORD)(lpDIBPtr[sizeof(BITMAPFILEHEADER)]); // Готовим текстовую строку для вывода wsprintf(szBuf, "Размер заголовка, байт:\t%ld\n", biSize); wsprintf(szBuf1, "Размер файла, байт: \t%ld\n", dwFileSize); lstrcat(szBuf, szBuf1); wsprintf(szBuf1, "Смещение изображения, байт:\t%ld\n", bfOffset); lstrcat(szBuf, szBuf1); // В зависимости от формата DIB (PM или Windows) // выводим различную информацию из заголовка файла if((nDIBType == WINRGB_DIB) || (nDIBType == WINRLE4_DIB) || (nDIBType == WINRLE8_DIB)) { wsprintf(szBuf1, "\nBMP для Windows\n", biSize); lstrcat(szBuf, szBuf1); lpDIBInfoHeader = (LPBITMAPINFOHEADER)(lpDIBPtr + sizeof(BITMAPFILEHEADER)); wsprintf(szBuf1, "Размер:\t%ldx%ld\n", lpDIBInfoHeader->biWidth, lpDIBInfoHeader->biHeight); lstrcat(szBuf, szBuf1); wsprintf(szBuf1, "Бит на пиксел:\t%d\n", lpDIBInfoHeader->biBitCount); lstrcat(szBuf, szBuf1); wNumColors = DIBNumColors(lpDIBPtr); wsprintf(szBuf1, "Таблица цветов:\t%d\n", wNumColors); lstrcat(szBuf, szBuf1); if(lpDIBInfoHeader->biCompression == BI_RGB) { lstrcat(szBuf, "Без компрессии\n"); } else if(lpDIBInfoHeader->biCompression == BI_RLE4) { lstrcat(szBuf, "Компрессия RLE4\n"); } else if(lpDIBInfoHeader->biCompression == BI_RLE8) { lstrcat(szBuf, "Компрессия RLE8\n"); } } // Для файлов DIB в формате PM else { wsprintf(szBuf1, "\nBMP для Presentation Manager\n", biSize); lstrcat(szBuf, szBuf1); lpDIBCoreHeader = (LPBITMAPCOREHEADER)(lpDIBPtr + sizeof(BITMAPFILEHEADER)); wsprintf(szBuf1, "Размер:\t%dx%d\n", lpDIBCoreHeader->bcWidth, lpDIBCoreHeader->bcHeight); lstrcat(szBuf, szBuf1); wsprintf(szBuf1, "Бит на пиксел:\t%d\n", lpDIBCoreHeader->bcBitCount); lstrcat(szBuf, szBuf1); } MessageBox(NULL, (LPSTR)szBuf, "Bitmap Info", MB_OK | MB_ICONINFORMATION); GlobalUnlock(hDib); return TRUE; } // ------------------------------- // Функция DIBType // Определение и проверка формата DIB // ------------------------------- int DIBType(HDIB hDib) { LPBITMAPFILEHEADER lpDIBFileHeader; LPBITMAPINFOHEADER lpih; LPBITMAPCOREHEADER lpch; DWORD biSize; LPDIB hDIBPtr; int nDIBType; if(hDib == NULL) // Неправильный идентификатор DIB return(-2); // Фиксируем память, в которой находится DIB hDIBPtr = (LPDIB)GlobalLock(hDib); if(hDIBPtr == NULL) return(-1); lpDIBFileHeader = (LPBITMAPFILEHEADER)hDIBPtr; // Проверяем тип файла if(lpDIBFileHeader->bfType != 0x4d42) { GlobalUnlock(hDib); return 0; } // Проверяем размер заголовка biSize = (DWORD)(hDIBPtr[sizeof(BITMAPFILEHEADER)]); if(biSize == sizeof(BITMAPINFOHEADER)) // 40 байт { // Это заголовок DIB в формате Windows lpih = (LPBITMAPINFOHEADER)(hDIBPtr + sizeof(BITMAPFILEHEADER)); // Проверяем основные поля заголовка DIB if((lpih->biPlanes == 1) && ((lpih->biBitCount == 1) || (lpih->biBitCount == 4) || (lpih->biBitCount == 8) || (lpih->biBitCount == 24)) && ((lpih->biCompression == BI_RGB) || (lpih->biCompression == BI_RLE4 && lpih->biBitCount == 4) || (lpih->biCompression == BI_RLE8 && lpih->biBitCount == 8))) { // Определяем метод компрессии файла if(lpih->biCompression == BI_RGB) nDIBType = WINRGB_DIB; else if(lpih->biCompression == BI_RLE4) nDIBType = WINRLE4_DIB; else if(lpih->biCompression == BI_RLE8) nDIBType = WINRLE8_DIB; else nDIBType = 0; } else nDIBType = 0; } else if(biSize == sizeof(BITMAPCOREHEADER)) // 12 байт { // Это заголовок DIB в формате Presentation Manager lpch = (LPBITMAPCOREHEADER)(hDIBPtr + sizeof(BITMAPFILEHEADER)); // Проверяем основные поля заголовка DIB if((lpch->bcPlanes == 1) && (lpch->bcBitCount == 1 || lpch->bcBitCount == 4 || lpch->bcBitCount == 8 || lpch->bcBitCount == 24)) { nDIBType = PM_DIB; } else nDIBType = 0; } else nDIBType = 0; GlobalUnlock(hDib); // Возвращаем тип файла или признак ошибки return nDIBType; } // ------------------------------- // Функция DIBNumColors // Определение размера палитры // ------------------------------- WORD DIBNumColors(LPDIB lpDib) { DWORD dwColorUsed; LPBITMAPINFOHEADER lpih; lpih = (LPBITMAPINFOHEADER)(lpDib + sizeof(BITMAPFILEHEADER)); // Количество цветов dwColorUsed = lpih->biClrUsed; // Если используется палитра уменьшенного размера, // возвращаем нужный размер if(dwColorUsed) return((WORD)dwColorUsed); // Если количество использованных цветов не указано, // вычисляем стандартный размер палитры исходя из // количества бит, определяющих цвет пиксела switch(lpih->biBitCount) { case 1: return 2; case 4: return 16; case 8: return 256; default: return 0; // палитра не используется } } // ------------------------------- // Функция DIBHeight // Определение высоты DIB в пикселах // ------------------------------- WORD DIBHeight(LPDIB lpDib) { LPBITMAPINFOHEADER lpih; lpih = (LPBITMAPINFOHEADER)(lpDib + sizeof(BITMAPFILEHEADER)); return lpih->biHeight; } // ------------------------------- // Функция DIBWidth // Определение ширины DIB в пикселах // ------------------------------- WORD DIBWidth(LPDIB lpDib) { LPBITMAPINFOHEADER lpih; lpih = (LPBITMAPINFOHEADER)(lpDib + sizeof(BITMAPFILEHEADER)); return lpih->biWidth; } // ------------------------------- // Функция DIBFindBits // Определение адреса массива бит изображения // ------------------------------- LPSTR DIBFindBits(LPDIB lpDib) { LPBITMAPFILEHEADER lpfh; LPBITMAPINFOHEADER lpih; lpfh = (LPBITMAPFILEHEADER)lpDib; // Используем значение, указанное в заголовке // файла (если оно не равно нулю) if(lpfh->bfOffBits) return((LPSTR)lpfh + lpfh->bfOffBits); // Вычисляем адрес исходя из размеров заголовков и // таблицы цветов lpih = (LPBITMAPINFOHEADER)(lpDib + sizeof(BITMAPFILEHEADER)); return((LPSTR)lpih + lpih->biSize + (DWORD)(DIBNumColors(lpDib) * sizeof(RGBQUAD))); } // ------------------------------- // Функция DIBPaint // Рисование DIB при помощи функции StretchDIBits // ------------------------------- BOOL DIBPaint(HDC hdc, int x, int y, HDIB hDib) { HBITMAP hbmp; HDC hMemDC; WORD wHeight, wWidth; LPDIB lpDib; LPBITMAPINFOHEADER lpih; lpDib = (LPDIB)GlobalLock(hDib); if(lpDib == NULL) return(-1); lpih = (LPBITMAPINFOHEADER)(lpDib + sizeof(BITMAPFILEHEADER)); // Определяем размеры DIB wHeight = lpih->biHeight; wWidth = lpih->biWidth; // Рисуем DIB без масштабирования StretchDIBits(hdc, x, y, wWidth, wHeight, 0, 0, wWidth, wHeight, DIBFindBits(lpDib), (LPBITMAPINFO)lpih, DIB_RGB_COLORS, SRCCOPY); GlobalUnlock(hDib); return TRUE; } // ------------------------------- // Функция DIBPaintBlt // Рисование DIB при помощи функции BitBlt // ------------------------------- BOOL DIBPaintBlt(HDC hdc, int x, int y, HDIB hDib) { HBITMAP hbmp; HDC hMemDC; WORD wHeight, wWidth; LPDIB lpDib; LPBITMAPINFOHEADER lpih; lpDib = (LPDIB)GlobalLock(hDib); if(lpDib == NULL) return(-1); lpih = (LPBITMAPINFOHEADER)(lpDib + sizeof(BITMAPFILEHEADER)); wHeight = lpih->biHeight; wWidth = lpih->biWidth; // Создаем совместимое битовое изображение hbmp = CreateCompatibleBitmap(hdc, wWidth, wHeight); // Создаем совместимый контекст памяти hMemDC = CreateCompatibleDC(hdc); // Преобразуем DIB в DDB SetDIBits(hdc, hbmp, 0, wHeight, DIBFindBits(lpDib), (LPBITMAPINFO)lpih, DIB_RGB_COLORS); // Выбираем DDB в контекст отображения hbmp = (HBITMAP)SelectObject(hMemDC, hbmp); // Рисуем DDB BitBlt(hdc, x, y, wWidth, wHeight, hMemDC, 0, 0, SRCCOPY); // Удаляем контекст памяти DeleteObject(SelectObject(hMemDC, hbmp)); DeleteDC(hMemDC); GlobalUnlock(hDib); return TRUE; } // ------------------------------- // Функция DIBCreatePalette // Создаем палитру на базе таблицы цветов DIB // ------------------------------- HPALETTE DIBCreatePalette(HDIB hDib) { LPLOGPALETTE lpPal; HPALETTE hPal = NULL; HANDLE hLogPal; int i, wNumColors; LPSTR lpbi; LPBITMAPINFO lpbmi; if (!hDib) return NULL; lpbi = (LPSTR)GlobalLock(hDib); lpbmi = (LPBITMAPINFO)(lpbi + sizeof(BITMAPFILEHEADER)); // Определяем размер таблицы цветов wNumColors = DIBNumColors(lpbi); // Если в DIB есть таблица цветов, создаем палитру if (wNumColors) { // Заказываем память для палитры hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * wNumColors); if (!hLogPal) { GlobalUnlock(hDib); return NULL; } // Получаем указатель на палитру lpPal = (LPLOGPALETTE)GlobalLock(hLogPal); // Заполняем заголовок lpPal->palVersion = 0x300; lpPal->palNumEntries = wNumColors; // Заполняем палитру for (i = 0; i < wNumColors; i++) { lpPal->palPalEntry[i].peRed = lpbmi->bmiColors[i].rgbRed; lpPal->palPalEntry[i].peGreen = lpbmi->bmiColors[i].rgbGreen; lpPal->palPalEntry[i].peBlue = lpbmi->bmiColors[i].rgbBlue; lpPal->palPalEntry[i].peFlags = 0; } // Создаем палитру hPal = CreatePalette(lpPal); if (!hPal) { GlobalUnlock(hLogPal); GlobalFree(hLogPal); return NULL; } GlobalUnlock(hLogPal); GlobalFree(hLogPal); } GlobalUnlock(hDib); // Возвращаем идентификатор созданной палитры return hPal; } Опишем функции, определенные в файле dib.cpp. DIBSelectFileЭта функция выводит на экран стандартную диалоговую панель "Open", с помощью которой можно выбрать bmp-файл. Функция возвращает идентификатор открытого файла или NULL, если пользователь отказался от выбора. DIBReadFileС помощью этой функции открытый файл читается в глобальный блок памяти. Этот блок заказывается непосредственно перед чтением, причем его размер соответствует размеру файла. В случае успеха функция возвращает идентификатор блока памяти, содержащий файл, причем этот блок является перемещаемым. Во время чтения курсор мыши принимает форму песочных часов, так как процедура чтения может быть достаточно длительной. Так как размер bmp-файлов обычно больше 64 Кбайт, для чтения используется функция _hread. DIBInfoФункция выводит информацию о загруженном изображении DIB. Идентификатор блока памяти, содержащего файл изображения, передается этой функции в качестве первого параметра. Через второй параметр передается размер файла, определенный функцией чтения файла DIBReadFile. DIBTypeФункция получает в качестве параметра идентификатор блока памяти, содержащего загруженный файл изображения DIB. Она определяет его формат и выполняет проверку полей структур заголовка. Если в качестве параметра этой функции было передано значение NULL, она возвращает код ошибки -2. Если не удалось зафиксировать блок памяти, возвращается код ошибки -1. Функция проверяет первые два байта bmp-файла. Если они не содержат значения 0x4d42, возвращается код ошибки 0. Далее функция определяет формат заголовка. Для изображения в стандарте Windows проверяются поля biPlanes, biBitCount и biCompression структуры BITMAPINFOHEADER, а для изображения в стандарте Presentation Manager - поля biPlanes и biBitCount структуры BITMAPCOREHEADER. DIBNumColorsЭта функция определяет размер таблицы цветов. Если изображение DIB содержит таблицу цветов уменьшенного размера, возвращается размер таблицы из поля biClrUsed структуры BITMAPINFOHEADER. В противном случае размер таблицы цветов определяется исходя из содержимого поля biBitCount структуры BITMAPINFOHEADER (количество бит памяти, определяющих цвет пиксела). DIBHeightФункция возвращает высоту изображения DIB в пикселах как значение поля biHeight структуры BITMAPINFOHEADER. DIBWidthФункция возвращает ширину изображения DIB в пикселах как значение поля biWidth структуры BITMAPINFOHEADER. DIBFindBitsЭта функция возвращает адрес массива бит изображения. В качестве параметра ей необходимо передать адрес зафиксированного блока памяти, содержащего загруженный файл изображения. DIBPaintЭта функция рисует изображение DIB. Первый параметр определяет контекст отображения. Второй и третий - координаты верхнего левого угла прямоугольной области, в которой будет нарисовано изображение. Через последний параметр передается идентификатор блока памяти, содержащего загруженное изображение DIB. Рисование выполняется при помощи функции StretchDIBits. DIBPaintBltФункция аналогична предыдущей, однако перед рисованием выполняется преобразование DIB в DDB. Далее преобразованное изображение выводится через контекст памяти в контекст отображения при помощи функции BitBlt. DIBCreatePaletteФункция создает логическую палитру на базе таблицы цветов изображения DIB, возвращая идентификатор созданной палитры или NULL при ошибке. Если DIB не содержит таблицу цветов, палитра не создается. Прототипы всех описанных выше функций, а также необходимые константы и типы данных описаны в файле dib.hpp (листинг 4.9). Листинг 4.9. Файл bmpinfo/dib.hpp #define WINRGB_DIB 1 #define WINRLE4_DIB 2 #define WINRLE8_DIB 3 #define PM_DIB 10 typedef HGLOBAL HDIB; typedef unsigned char huge* LPDIB; HFILE DIBSelectFile(void); HDIB DIBReadFile(HFILE hfDIBFile, DWORD *dwFileSize); BOOL DIBInfo(HDIB hDib, DWORD dwFileSize); int DIBType(HDIB hDib); WORD DIBNumColors(LPDIB lpDib); WORD DIBHeight(LPDIB lpDib); WORD DIBWidth(LPDIB lpDib); HPALETTE DIBCreatePalette(HDIB hDIB); BOOL DIBPaint(HDC hDC, int x, int y, HDIB hDIB); Обратите внимание на тип LPDIB, который описан как указатель типа huge: typedef unsigned char huge* LPDIB; Это необходимо для правильной адресации внутри массива данных, размер которого превосходит 64 Кбайт. Файл определения ресурсов приложения BMPINFO приведен в листинге 4.10. Листинг 4.10. Файл bmpinfo/bmpinfo.rc #include "bmpinfo.hpp" APP_MENU MENU BEGIN POPUP "&File" BEGIN MENUITEM "&Open", CM_FILEOPEN MENUITEM "&Info...", CM_FILEINFO MENUITEM SEPARATOR MENUITEM "E&xit", CM_FILEEXIT END POPUP "&Help" BEGIN MENUITEM "&About...", CM_HELPABOUT END END Файл определения модуля приложения BMPINFO вы сможете найти в листинге 4.11. Листинг 4.11. Файл bmpinfo/bmpinfo.def ; ============================= ; Файл определения модуля ; ============================= NAME BMPINFO DESCRIPTION 'Приложение BMPINFO, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 8120 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple |