Растровые изображенияОсновные понятияЗагрузить и вывести на экран изображение, используя функции WinAPI, довольно сложная задача (например, функций по загрузке изображения из файлов вообще нет, а функции манипулирования изображениями в памяти имеют ограниченные возможности). Средства GDI немного упрощают работу с изображениями за счет того, что с каждым DC, предназначенным для графического вывода, связан графический объект bitmap (растровое изображение), который хранит массив пикселей, выводимых на устройство. Однако, для того, чтобы рисовать средствами GDI, придется создавать и хранить изображение в виде графического объекта bitmap. В общем случае, Windows поддерживает два формата битовых изображений - аппаратно-зависимый DDB (device-dependent bitmap ) и аппаратно-независимый DIB (device-independent bitmap). DDB формат - набор бит в оперативной памяти, структура которого жестко привязана к аппаратным особенностям устройства вывода и, который может быть отображен на этом устройстве (например, при прямом копировании бит в видео память монитора, в память принтера, плотера и. т.д.). Естественно - DDB обеспечивают более быстрый вывод изображений, но универсальность DIB позволила ему стать наиболее используемым (за DDB остались, практически, лишь прошивки тестов для устройств вывода и форматы промежуточных преобразований). Отметим, что кроме битовых изображений, Windows поддерживает и векторные изображения, являющиеся ни чем иным как совокупностью отдельных графических объектов, из которых состоит изображение (формат метафайл Windows с расширением *.emf (*.wmf) хранит картинку в виде набора описаний и определений всех компонент графики и их характеристик - отрезков линий, шаблонов заполнения, текста и его атрибутов и т.п. - или, немного упрощая - набор функций GDI). Векторные изображения масштабируются без потери качества, требуют при хранеии меньше памяти, но проигрывают битовым изображениям в скорости. И, все же, хотя ряд устройств, например плоттер или графопостроитель, способны работать только с векторными изображениями (рисование выполняется перьями), все же основные форматы Windows это DIP и DDP. Работа с изображениями формата DDBИзображения DDB, в силу спицифики их применения, либо загружаются из ресурсов приложения, либо создаются непосредственно в оперативной памяти. Работе с ресурсами посвящена отдельная глава "Работа с ресурсами в Borland C++ Builder" и, поэтому, здесь эти вопросы подробно рассматриваться не будут. Отметим лишь, что для загрузке растрового изображения из ресурсов используется функция LoadBitmap(): HBITMAP LoadBitmap ( HINSTANCE hInstance, //дескриптор приложения LPCTSTR lpBitmapName //имя ресурса ); Функция возвращает идентификатор загруженного изображения или NULL. Имя ресурса - параметр, который содержит идентификатор ресурса, либо идентификатор ресурса в младшем слове и 0 в старшем слове. Сам ресурс, как это показано далее, должен быть включен в приложение через файл определения ресурсов .rc. Использование предопределенных растровых изображенийLoadBitmap(), может использовать предопределенные точечные рисунки Win32 API. Их перечень не трудно получить по F1 для функции LoadBitmap(), их много, и здесь перечислены лишь некоторые: OBM_CHECK - галочка; OBM_BTSIZE - угловая штриховка; OBM_BTNCORNERS - кружочек; OBM_CHECKBOXES - совокупность нескольких элементов управления; OBM_CLOSE - пиктограмма Windows Close; OBM_COMBO - заштрихованная стрелка вниз; OBM_DNARROW - заштрихованная стрелка вниз на кнопочке (не нажата); OBM_DNARROWD - заштрихованная стрелка вниз на кнопочке (нажата); OBM_DNARROWI - стрелка вниз на кнопочке в режиме Enabled (нажата); OBM_LFARROW - заштрихованная стрелка влево на кнопочке; ........... и т.д. Следующий пример показывает использование предопределенных растровых изображений Windows: Чтобы приложение использовало любую из OBM_ констант, константа OEMRESOURCE должна быть установлена прежде, чем включится заголовочный файл, поэтому в начеле файла unit1.cpp необходимо поместить следующую директиву: #define OEMRESOURCE Далее в любом обработчике (Например OnClick для кнопки) пишем код: void __fastcall TForm1::Button1Click(TObject *Sender) { HBITMAP hBmp=LoadBitmap(NULL,MAKEINTRESOURCE(OBM_CLOSE)); if(hBmp) { HDC hDc=GetDC(Handle); //Устанавливаем прозрачный фон SetBkMode(hDc,TRANSPARENT); //Создаем контекст в памяти, совместимый с контекстом //устройства - в данном случае окна приложение HDC hMemDc = CreateCompatibleDC(hDc); //Выбор битового изображения в контекст памяти SelectObject(hMemDc,hBmp); //Отрисовка изображения BitBlt(hDc,0,0,24,24,hMemDc,0,0,SRCCOPY); StretchBlt(hDc,0,30,48,48,hMemDc,0,0,24,24,SRCCOPY); //Возврат к исходному состоянию DeleteDC(hMemDc); DeleteDC(hDc); DeleteObject(hBmp); } }
Рис 1. Пример использования DDB. Здесь использованы функции: Функция SelectObject выбирает растровое изображение в указанный контекст устройства. HGDIOBJ SelectObject ( HDC hdc, // дескриптор контекста устройства HGDIOBJ hgdiobj // дескриптор растрового изображения ); Функции отрисовки изображения BitBlt() и StretchBlt() позволяют отрисовать растровое изображение без методом прямого копирования и с масштабированием. BOOL WINAPI BitBlt ( HDC hdcDest, // контекст для рисования int nXDest, // x-координата верхнего левого угла // области рисования int nYDest, // y-координата верхнего левого угла // области рисования int nWidth, // ширина изображения int nHeight, // высота изображения HDC hdcSrc, // идентификатор исходного контекста int nXSrc, // x-координата верхнего левого угла // исходной области int nYSrc, // y-координата верхнего левого угла // исходной области DWORD dwRop // код растровой операции ); BOOL WINAPI StretchBlt ( HDC hdcDest, // контекст для рисования int nXDest, // x-координата верхнего левого угла // области рисования int nYDest, // y-координата верхнего левого угла // области рисования int nWidthDest, // новая ширина изображения int nHeightDest, // новая высота изображения HDC hdcSrc, // идентификатор исходного контекста int nXSrc, // x-координата верхнего левого угла // исходной области int nYSrc, // y-координата верхнего левого угла // исходной области int nWidthSrc, // ширина исходного изображения int nHeightSrc, // высота исходного изображения DWORD dwRop // код растровой операции ); Параметры функций прозрачны, код растровой операции определяет цвет результирующего изображения создаваемый при использовании логических операций И и ИЛИ над цветом изображения и цветом фона (см. Help). Таким образом, для того, чтобы выводить изображение на экран используется следующая кодовая последовательность. После получения дескриптора изображения выбирается контекст устройства для отображения (DC, функция SelectObject), затем он сопоставляется некоторому контексту созданному в памяти (memory DC), совместимомому с данным контекстом. Далее в этом контексте в объекте default bitmap - растровом изображении, созданным вместе с контекстом memory DC, выполняются необходимые действия по отрисовке изображения и далее функциями копирования битов растровое изображение выводится в DC. В принципе отрисовку можно вести и прямо в DC, но результатом будет мерцание экрана, от которого как раз и избавляют быстрые функции копирования, такие как BitBlt() и StretchBlt(). После использования приложение должно удалить из памяти битовое изображение и освободить задействованные контексты устройств. Приложение может определить параметры загруженного изображения, вызвав функцию GetObject : int WINAPI GetObject ( HGDIOBJ hgdiobj, // идентификатор объекта int cbBuffer, // размер буфера void FAR* lpvObject // адрес буфера ); С помощью этой функции можно получить разнообразную информацию об объектах GDI, таких, как логические перья, кисти, шрифты или битовые изображения. Идентификатор изображения должен передаваться через параметр hgdiobj. Параметр lpvObject должен указывать на структуру типа BITMAP, в которую будут записаны сведения об изображении. Через параметр cbBuffer передается размер структуры BITMAP: BITMAP bmp; HBITMAP hBmp; GetObject(hBmp,sizeof(BITMAP),(LPSTR) &bmp); Структура BITMAP определена как: typedef struct tagBITMAP { int bmType; int bmWidth; int bmHeight; int bmWidthBytes; BYTE bmPlanes; BYTE bmBitsPixel; void FAR* bmBits; }BITMAP; Где:
Используя функцию GetObject() можно осознанно использовать размеры изображения, например в предыдущем примере: HBITMAP hBmp=LoadBitmap(NULL,MAKEINTRESOURCE(OBM_CLOSE)); BITMAP bmp; GetObject(hBmp,sizeof(BITMAP),(LPSTR) &bmp); ......... BitBlt(hDc,0,0,bmp.bmWidth,bmp.bmHeight,hMemDc,0,0,SRCCOPY); StretchBlt(hDc,0,30,48,48,hMemDc,0,0,bmp.bmWidth, bmp.bmHeight,SRCCOPY); Создание и использование растровых изображенийВ предыдущем параграфе мы уже рассматривали пример создания растрового изображения для кисти, повторим пример для отрисовки изображения, немного его продолжив. HDC hDc,hMemDc; HBRUSH hBrush,hBrushOld; // Массив для пикеселей изображения 10*10 пикселей. При 24 //цветах на каждый пиксель требуется 3 байта или 1.5 unsigned //short числа, для 10*10 пикселей требуется 15*15=225 чисел. // Создание кистей из битовых образов размером более 8*8 //пикселей в Win 9x не поддерживается. Если указан битовый //образ большего размера, используется его часть. unsigned short usrgMassPix[225]; //Зная как в памяти располагаются биты, например для 24 цветовой //палитры синий цвет ff0000, можем задать его как цвет кисти if(GetDeviceCaps(CreateDC("DISPLAY",NULL,NULL,NULL), BITSPIXEL) == 24) { for(int i=0; i < 75; i++) { usrgMassPix[i*3]=255; usrgMassPix[i*3+1]=0; usrgMassPix[i*3+2]=0; } } else //Здесь для других значений или если не знаем как for(int i=0; i < 225; i++) usrgMassPix[i] =random(255); //Создаем изображение кисти в памяти //10*10 пикселов и получаем его хэндл HBITMAP hBmp = CreateBitmap(10,10,GetDeviceCaps(CreateDC("DISPLAY", NULL,NULL,NULL),PLANES), GetDeviceCaps(CreateDC("DISPLAY",NULL,NULL,NULL),BITSPIXEL), &usrgMassPix[0]); //Получаем хэндл устройства hDc=GetDC(Handle); //Создаем кисть hBrush = CreatePatternBrush(hBmp); //Выбираем кисть в контекст устройства hBrushOld=SelectObject(hDc,hBrush); //Здесь можно было выбрать перо для обводки фигуры как и //в примере выше, но решили воспользоваться пером по умолчанию //Создает контекст совместимый с контекстом устройства hDc=CreateDC("DISPLAY",NULL,NULL,NULL); hMemDc = CreateCompatibleDC(hDc); //Cоздает пустое растровое изображение в памяти размером 50*50 //совместимое с контекстом устройства и получаем его хэндл HBITMAP hBmpMem = CreateCompatibleBitmap(hDc,50,50); //Выбтраем изображение в совместимый контекст в памяти SelectObject(hMemDc,hBmpMem); RECT rect; rect.left = 0; rect.top = 0; rect.right = 50; rect.bottom = 50; //Закрашиваем изображение кистью FillRect(hMemDc,&rect,hBrush); //Выводим изображение или с помощью функции BitBlt BitBlt(hDc,0,0,50,50,hMemDc,0,0,SRCCOPY); SetBkMode(hDc,TRANSPARENT); //Или выводим с помощью функции StretchBlt StretchBlt(hDc,0,0,200,200,hMemDc,0,0,50,50,SRCCOPY); SelectObject(hDc,hBrushOld); DeleteDC(hMemDc); DeleteObject(hBmp); DeleteObject(hBmpMem); DeleteObject(hBrush); DeleteObject(hBrushOld); ReleaseDC(Handle,hDc); DeleteDC(hDc); } Функция CreateBitmap() создает точечный рисунок с указанной шириной, высотой, и форматом (цветные панели и число бит на пиксел): HBITMAP CreateBitmap ( Int nWidth, // растровая ширина, в пикселах Int nHeight, // растровая высота, в пикселах UINT cPlanes, // число цветных плоскостей устройства UINT cBitsPerPel, // число бит на пиксель CONST VOID *lpvBits // указатель на массив пикселей ); Функция GetDeviceCaps использована для получения информации об устройстве: Int GetDeviceCaps ( HDC hdc, // контекст устройства Int nIndex // индекс запроса ); Для создания изобравжения может быть использована также функция: HBITMAP CreateBitmapIndirect(BITMAP FAR* lpbm); Эта функция создает растровое изображение по массиву байт и возвращает идентификатор битового изображения, который далее можете быть использован обычным способом. Другие полезные функции для работы с изображениями DDB:
Работа с изображениями формата DIBИзображения DIB, в отличие от изображений DDB, являются аппаратно-независимыми, поэтому, без дополнительного преобразования, их нельзя отображать на экране с помощью функций BitBlt и StretchBlt. В Windows основным форматом для хранения битовых изображений является формат .bmp и, в тоже время, в программном интерфейсе Windows нет функций, специально предназначенных для рисования битовых изображений этого формата. Сложности работы с изображениями в формате DIB непосредственно используя функции GDI, о чем речь ниже, привело к тому, что редко кто использует эту возможность, предпочитая работу с использованием свойств комронент Borland C++ Builder. Однако знание формата DIB порой необходимо и полезно, поэтому, кратко остановимся на основном формате DIB - формате bitmap. Формат bmp-файлов содержит:
Структура BITMAPFILEHEADER: typedef struct tagBITMAPFILEHEADER { UINT bfType; // Тип изображения, для .bmp файлов BM. DWORD bfSize; // Размер файла в байтах UINT bfReserved1; UINT bfReserved2; DWORD bfOffBits; // Смещение начала непосредственно бит // изображения относительно начала файла }BITMAPFILEHEADER; typedef BITMAPFILEHEADER* PBITMAPFILEHEADER; typedef BITMAPFILEHEADER FAR* LPBITMAPFILEHEADER; Структура BITMAPINFO: typedef struct tagBITMAPINFO { //Структура BITMAPINFOHEADER BITMAPINFOHEADER bmiHeader; //Массив определяющий цвета каждой //точки изображения RGBQUAD bmiColors[1]; } BITMAPINFO; typedef BITMAPINFO* PBITMAPINFO; typedef BITMAPINFO FAR* LPBITMAPINFO; Биты в массиве bmiColors расположены друг за другом построчно (как ни странно - с конца файла), каждая строка изображения дополняется нулями до границы двойного слова. Структура BITMAPINFOHEADER содержит информацию относительно измерений и цветного формата растрового изображения: typedef struct tagBITMAPINFOHEADER { DWORD biSize; // Размер структуры в байтах LONG biWidth; // Ширина изображения в пикселах LONG biHeight; // Высота изображения в пикселах WORD biPlanes; // Число цветовых плоскостей WORD biBitCount // Число бит на один пиксел DWORD biCompression; // Метод сжатия (компрессии) DWORD biSizeImage; // Размер изображения в байтах // не компессированного изображения LONG biXPelsPerMeter; // Разрешение устройства вывода по // горизонтали в пикселах на метр LONG biYPelsPerMeter; // Разрешение устройства вывода по // вертикали в пикселах на метр DWORD biClrUsed; // Размер таблицы цветов DWORD biClrImportant; // Количество цветов, необходимое для // отображения файла без искажений }BITMAPINFOHEADER; Размер таблицы цветов (biBitCount) может быть следующий: Значение Размер 1 2 4 16 8 256 24 не используется Если содержимое поля biClrUsed отлично от нуля, используется таблица цветов уменьшенного размера. В ней описаны только те цвета, которые содержатся в изображении. Процесс вывода DIB изображений включает в себя несколько этапов:
Отрисовка изображения может быть выполнена с использованием функции StretchDIBits() (функция StretchDIBits копирует данные для прямоугольной области пикселов формата DIB в область адресата отображения формата DDB, полученное таким образом изображение DDB может быть выбрано в контекст памяти и нарисовано обычным способом при помощи функции BitBlt() или StretchBlt()) или с использованием функции StretchDIBits() (эта функция сама выполняет необходимые преобразования). Сложность структуры файлов DIB, естественно, влечет за собой и сложность работы по выводу изображения и, в настоящее время, как уже отмечено выше, методы чтения файлов DIB и их преобразования в DDB с помощью функций GDI редко кто использует. Работе с графикой, в том числе и с растровыми изображениями, с
использованием свойств компонент Borland C++ Builder посвящена следующая глава
"Работа с графикой с использованием свойст компонент Borland C++ Builder".
В следующем параграфе главы рассмотрим
работу со шрифтами.
|