Отдельно надо рассмотреть случай, когда один из контекстов является цветным, а другой черно–белым — при этом особым образом осуществляется преобразование цветов:
при переходе от монохромного к цветному цвет, закодированный 1, соответствует цвету фона (задаваемому функцией SetBkColor), а цвет 0 — цвету текста (функция SetTextColor).
при переходе от цветного к монохромному считается, что если цвет точки совпадает с цветом фона, то эта точка кодируется цветом 1, иначе 0.
Самая мощная функция, выполняющая растровые операции:
BOOLStretchBlt (
hDestDC, nDestX, nDestY, nDestWidth, nDestHeight,
hSrcDC, nSrcX, nSrcY, nSrcWidth, nSrcHeight, dwROP);
позволяет не только передать изображение между разными контекстами, но и осуществить масштабирование изображения. При масштабировании возможно два случая:
изображение увеличивается, то некоторые строки (столбцы) будут дублироваться;
изображение уменьшается, то некоторые строки (столбцы) будут комбинироваться в одну строку (столбец).
Объединение строк (столбцов) при сжатии может осуществляться различными способами, которые выбираются с помощью функции
UINT SetStretchBltMode (hDC, nMode);
параметр nMode задает режим объединения строк:
BLACKONWHITE | выполняется операция И (AND). В результате получается, что черный цвет имеет "приоритет" над белым — сочетание черного с белым рассматривается как черный |
WHITEONBLACK | выполняется операция ИЛИ (OR). При этом "приоритет" принадлежит белому над черным — сочетание черного с белым дает белый |
COLORONCOLOR | при этом происходит простое исключение строк (столбцов). |
HALFTONE1 | только в Win32 API; происходит усреднение цвета объединяемых точек. |
Независимые от устройства битмапы
У уже рассмотренных зависимых от устройства битмапов имеется один очень серьезный недостаток — их организация отражает организацию видеопамяти того графического устройства, для которого они были спроектированы. При этом возникают большие сложности с переносом битмапов с одного устройства на другое, особенно при создании битмапов, которые будут сохраняться в виде файлов и позже переносится на другие компьютеры. Достаточно универсальным является только лишь монохромный битмап, который легко может быть отображен на любом цветном устройстве, однако такое ограничение является крайне неудобным для конечного пользователя.
Для цветных битмапов сложности возникают даже при их отображении на однотипных устройствах. Например, SVGA адаптеры часто используют логическую палитру, задающую набор воспроизводимых цветов. При отображении одного и того же цветного битмапа на устройствах, использующих разные палитры, результат будет различным.
Наличие этих сложностей привело к появлению новых видов битмапов, так называемых независимых от устройства битмапов (Device Independed Bitmap, DIB). Такой битмап отличается от обычного тем, что дополнительно содержит данные, определяющие соответствие цветов, используемых битмапом, реальным цветам. Благодаря этому независимый от устройства битмап может быть отображен практически на любом графическом устройстве, поддерживающем операции по обмену битовыми образами, с минимальными искажениями цвета.
На практике, начиная с версий Windows 3.x для хранения изображений (в виде .bmp файлов или ресурсов приложения) используются только независимые от устройства битмапы.
Формат независимого от устройства битмапа
Обычно приходится иметь дело с DIB, когда они представлены либо в виде файлов или ресурсов приложения. Поэтому знакомство с DIB мы начнем с формата файла, содержащего DIB.
Некоторые сложности связаны с наличием нескольких различных видов DIB–файлов. Первоначально (в самых ранних версиях Windows и OS/2 использовался битмап в его простейшем виде, называемом в документации форматом OS/2[5]. В дальнейшем, по мере развития GDI появился формат Windows, который для приложений Windows долгое время являлся фактически стандартом. Этот вид битмапов дожил до платформы Win32, когда к нему было добавлено несколько новых возможностей, правда без изменения заголовка. В дальнейшем развитие Windows битмапов пошло стремительно — практически в каждой новой версии Windows добавляется что–то новое и в заголовках битмапов появляются новые поля. Так появились битмапы 4ой версии (для Windows–95 и WindowsNT 4.0) и даже 5ой (для WindowsNT 5.0). Скорее всего этот процесс так скоро не остановится.
Утешает в этом два соображения:
Первое: все старые форматы битмапов поддерживаются. Таким образом, если ваше приложение само создает битмап, то он будет корректно обрабатываться и в последующих версиях Windows.
Второе: при загрузке битмапа (а он может быть создан в системе, разработанной позже вашего приложения), можно так построить алгоритм, что анализировать заголовок не потребуется. В этом случае ваше приложение опять–таки может использовать новые форматы битмапов (по крайней мере до тех пор, пока вы не собираетесь самостоятельно анализировать изображение).
Рисунок 18. Структура независимого от устройства битмапа.
Собственно независимый от устройства битмап содержит несколько структур данных, описывающих его характеристики. Эти структуры следуют друг за другом непрерывно, без промежутков. Если говорить о структуре DIB в общем, не вдаваясь в подробности описания этих структур данных, то его формат сохраняется во всех существующих версиях Windows.
Заголовок битмапа содержит данные о его размере (размере всего битмапа в байтах) и расстояние от начала файла до хранимого в нем изображения. В таком виде битмап хранится либо в файле, либо в виде ресурсов приложения. Загрузка битмапа может выполняться двумя разными способами:
В простейшем случае все, кроме заголовка файла, помещается в одну область данных (в случае 16ти разрядных платформ надо учитывать, что размер может быть существенно больше 64К). Битмап, загруженный таким образом, называется упакованным (packed DIB).
В другом случае битмап располагается в двух областях — в первой находится заголовок битмапа и данные о соответствии цветов (палитра, либо заменяющие ее данные), а во второй — собственно изображение.
Первый способ удобен при считывании битмапа с диска или из ресурсов приложения, второй — при создании нового битмапа в приложении, когда размер области данных для хранения изображения может быть заранее неизвестен (его можно узнать из заголовка битмапа). Многие функции GDI, работающие с независимыми от устройства битмапами, требуют задания двух указателей: на информацию о битмапе и на данные изображения. Однако некоторые функции ориентированы на использование упакованного битмапа, и тогда требуют задания хендла глобального блока памяти, содержащего упакованный DIB. С этой точки зрения первый способ (с использованием упакованного битмапа) универсален — вы можете легко вычислить указатель на данные изображения внутри единого блока (например, исходя из данных в заголовке файла).
Загрузка независимых от устройства битмапов
Формат заголовка файла одинаков для всех версий битмапов; он описывается структурой BITMAPFILEHEADER:
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
ПолеbfType, должнобытьсодержатьдвебуквы"BM" (значение0x4D42).
Поле bfSize указывает полный размер файла, включая этот заголовок. Обратите внимание на то, что размер задается двойным словом, так как может существенно превышать 64K. Например битмап 1280x1024, 24 бита/пиксель имеет размер более 3M. Вообще говоря, это поле может быть не заполнено; хотя и крайне редко, но может даже оказаться, что там указана некорректная величина, вместо правильного размера или 0. По крайней мере для битмапов OS/2 в поле bfSize может оказаться величина, равная размеру заголовка файла плюс заголовок битмапа (26). Во всех случаях лучше исходить не из этой величины, а из реального размера файла.
Поля bfReserved1 и bfReserved2 оба содержат 0. По крайней мере так считает Microsoft. В битмапах OS/2 часто эти поля содержат ненулевые данные.
Поле bfOffBits указывает адрес, с которого в данном файле размещаются собственно данные изображения. Этим полем удобно пользоваться для получения размера заголовка битмапа и данных о его цветах, а заодно для вычисления адреса начала данных изображения.
Так, благодаря наличию поля bfOffBits, можно сформулировать универсальный алгоритм загрузки битмапа в память, не зависящий от версии битмапа и его характеристик. В этом примере мы будем ориентироваться на работу с функциями WindowsAPI, что позволяет сделать более компактный, переносимый код, помимо этого введем дополнительную структуру, описывающую DIB. Она будет удобна по двум причинам — во–первых, после загрузки DIB удобно возвращать два указателя, которые могут понадобиться в дальнейшем, плюс хендл блока памяти, содержащего битмап; все это проще хранить в одной структуре. Во–вторых, эту же структуру мы сможем использовать еще раз, когда рассмотрим загрузку битмапов из ресурсов приложения. Подробнее обо всех указателях и их типах — см. в разделе “Заголовок независимого от устройства битмапа”.
#define STRICT
#include <windows.h>
#include <windowsx.h>
// описываем структуру, содержащую информацию о битмапе
typedefstruct _DIB {
HGLOBALhglbDib; // хендл блока памяти или ресурса
LPBITMAPINFOHEADERlpDibHdr; // указатель на заголовок битмапа
LPSTRlpImage; // указатель на изображение
UINTuDibFlags; // флаг 1-загружен из файла, 2-из ресурса
} FAR* LP_DIB;
#define DIB_FILE 1
#define DIB_RESOURCE 2
#define DIB_SIGNATURE 0x4D42
#ifdef __NT__
#define _memcpy_ (to,from,sz) CopyMemory ((LPVOID) (to), (LPVOID) (from), (sz))