numREDbits=(WORD)(hiREDbit-loREDbit+1);
loGREENbit = LowBitPos(format.dwGBitMask);
WORD hiGREENbit = HighBitPos(format.dwGBitMask);
numGREENbits=(WORD)(hiGREENbit-loGREENbit+1);
loBLUEbit = LowBitPos(format.dwBBitMask);
WORD hiBLUEbit = HighBitPos(format.dwBBitMask);
numBLUEbits=(WORD)(hiBLUEbit-loBLUEbit+1);
return TRUE;
}
Функция StorePixelFormatData() присваивает значения шести переменным формата с помощью масок, полученных функцией GetPixelFormat() интерфейса DirectDrawSurface. Это следующие переменные:
• loREDbit
• numREDbits
• loGREENbit
• numGREENbits
• loBLUEbit
• numBLUEbits
Как вы убедитесь при изучении кода для работы с беспалитровыми поверхностями, эти переменные оказываются очень удобными.
Для прямого доступа к поверхности необходимо предварительно вызвать функцию Lock() интерфейса DirectDrawSurface. Lock() получает экземпляр структуры DDSURFACEDESC и возвращает указатель на левый верхний пиксель поверхности, шаг поверхности, ее размеры и даже формат пикселей (структура DDSURFACEDESC содержит экземпляр DDPIXELFORMAT, поэтому вызов GetPixelFormat() интерфейса DirectDrawSurface оказывается необязательным). Прототип функции Lock() выглядит так:
HRESULT Lock(LPRECT rect, LPDDSURFACEDESC desc, DWORD flags, HANDLE event);
Первый аргумент является указателем на структуру RECT, которая описывает рабочую область поверхности. Если этот аргумент равен нулю, доступ осуществляется ко всей поверхности. Применение этого аргумента для описания рабочих прямоугольников упрощает код, следующий за вызовом Lock(), поскольку вам не нужно вычислять лишние смещения. Тем не менее при задании такого прямоугольника оказывается еще важнее использовать шаг поверхности при доступе к памяти.
Второй аргумент функции Lock() — структура DDSURFACEDESC, которая используется для возвращения указателя на память поверхности (поле lpSurface) и шага поверхности (поле lPitch). Функция Lock() (как и другие функции DirectDraw) требует правильно присвоить значение полю dwSize структуры DDSURFACEDESC.
Третий аргумент используется для настройки параметров Lock(). В него могут входить следующие флаги:
• DDLOCK_EVENT
• DDLOCK_READONLY
• DDLOCK_WRITEONLY
• DDLOCK_SURFACEMEMORYPTR
• DDLOCK_WAIT
На момент выхода DirectX 5 флаг DDLOCK_EVENT не поддерживался. Возможно, в будущих версиях DirectDraw он будет использоваться совместно с последним аргументом Lock() для реализации альтернативного метода блокировки поверхностей.
Флаги DDLOCK_READONLY и DDLOCK_WRITEONLY следует использовать в том случае, когда доступ к памяти поверхности осуществляется исключительно для чтения или записи. В большинстве ситуаций эти флаги ни на что не действуют, однако в видеорежимах «Mode X» DirectDraw использует их для оптимизации доступа к поверхности.
Флаг DDLOCK_SURFACEMEMORYPTR необязателен, потому что он задает поведение Lock(), которое и так является стандартным. Lock() возвращает указатель на память поверхности как с этим флагом, так и без него, поэтому мы не станем использовать его в своих программах (флаг DDLOCK_SURFACEMEMORYPTR на самом деле определен равным 0, так что я нисколько не преувеличиваю, говоря, что он ни на что не влияет).
Флаг DDLOCK_WAIT показывает, что функция Lock() должна дождаться завершения блокировки в том случае, если в данный момент поверхность используется для другой цели — например, участвует в операции блиттинга или переключения поверхностей. Если этот флаг задан, Lock() работает в цикле до тех пор, пока поверхность не освободится для блокировки или пока не произойдет ошибка. При отсутствии флага DDLOCK_WAIT функция Lock() для занятой поверхности возвратит код DDERR_SURFACEBUSY, и блокировка не состоится. Для упрощения кода мы будем использовать этот флаг.
BMP-файлы
Теперь мы знаем, как получить прямой доступ к поверхности и что делать с ее памятью, чтобы изменить значения отдельных пикселей. Давайте используем полученные знания на практике. В этой главе мы напишем приложение DirectDraw для просмотра графических файлов формата BMP. Но перед тем как браться за такую программу, необходимо научиться загружать BMP-файлы.
BMP — стандартный формат графических файлов Windows. Подавляющее большинство BMP-файлов хранится без сжатия, что облегчает работу с ними. Даже в сжатых BMP-файлах нет ничего особенно сложного, но мы ограничимся файлами без сжатия.
BMP-файлы состоят из трех основных частей:
• заголовок;
• палитра;
• графические данные (значения пикселей).
Заголовок содержит информацию о файле и находящемся в нем графическом изображении. Здесь хранятся параметры изображения (ширина, высота, глубина пикселей), а также количество цветов в нем.
Палитра присутствует только в BMP-файлах, содержащих палитровые изображения (с глубиной пикселей 8 бит и менее). К 8-битным изображениям прикладывается палитра, состоящая из не более чем 256 элементов.
Графические данные — это и есть само изображение. Формат этих данных зависит от глубины пикселей. Хотя BMP-файлы делятся на несколько типов, мы ограничимся 8-битными и 24-битными изображениями. 8-битные BMP-файлы будут использоваться для работы с 8-битными поверхностями, а 24-битные — для беспалитровых поверхностей. Хотя, по слухам, в природе существуют 16-битные и 32-битные BMP-файлы, они встречаются очень редко — например, мне таковые ни разу не попадались. Впрочем, это не имеет особого значения, так как 24-битную графику можно легко преобразовать в 16- или 32-битный формат.
Данные заголовка BMP-файла хранятся в двух структурах: BITMAPFILEHEADER и BITMAPINFOHEADER. Структура BITMAPFILEHEADER присутствует в начале любого BMP-файла и содержит информацию о самом файле. Для нас в этой структуре представляет интерес лишь одно поле — bfType, сигнатура BMP-файла (информацию об остальных полях можно найти в справочной системе Visual C++). В BMP-файлах это поле содержит буквы BM (обе буквы — прописные). По содержимому этого поля мы будем убеждаться в том, что выбранные файлы действительно имеют формат BMP.
Структура BITMAPINFOHEADER содержит информацию об изображении, хранящемся в BMP-файле. Эта структура объявляется так:
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, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
Первое поле, biSize, определяет размер структуры BITMAPINFOHEADER в байтах. Если ваша программа создает BMP-файл, это поле заполняется тривиально — достаточно определить размер структуры функцией sizeof. Однако при чтении BMP-файла по