format.dwSize=sizeof(format);
surf->GetPixelFormat(&format);
Структура DDPIXELFORMAT содержит четыре поля, представляющих для нас интерес:
• dwRGBBitCount
• dwRBitMask
• dwGBitMask
• dwBBitMask
Поле dwRGBBitCount показывает глубину пикселей поверхности. Три оставшихся поля являются масками, определяющими, в каких битах пикселя хранятся данные красной, зеленой и синей составляющих. Например, типичные значения полей для поверхности High Color формата 5-6-5 приведены в табл. 5.1.
Три маски показывают, в каких позициях пикселя хранятся данные отдельных цветовых составляющих. С помощью этих масок можно корректно читать и записывать данные пикселя независимо от его формата.
Таблица 5.1. Типичные данные формата для 16-битных пикселей
Поле Значение Двоичное значение dwRGBBitCount 16 (неважно)
dwRBitMask 63488 1111100000000000
dwGBitMask 2016 0000011111100000
dwBBitMask 31 0000000000011111
В приведенном ниже коде маски используются для вычисления двух величин: начальной позиции каждой цветовой составляющей внутри пикселя (бит, с которого начинается составляющая, или стартовый бит), и количества бит для представления каждой цветовой составляющей. Значения этих величин приведены в табл. 5.2. Обратите внимание на то, что стартовый бит отсчитывается справа налево (старшие биты в двоичных величинах находятся слева).
Рассчитанные значения облегчают операции с пикселями. Стартовый бит показывает, на сколько позиций сдвигаются данные цветовой составляющей, а количество — сколько бит занимает составляющая в двоичной величине.
Таблица 5.2. Типичные данные формата для 16-битных пикселей
Поле Значение Двоичное значение Стартовый бит Количество бит dwRBitMask 63488 1111100000000000 11 5
dwGBitMask 2016 0000011111100000 5 6
dwBBitMask 31 0000000000011111 0 5
Однако до сих пор мы рассматривали лишь 16-битные пиксели. 8-битные пиксели нас не интересуют, но перед тем, как идти дальше, необходимо уделить внимание пикселям формата True Color. В табл. 5.3 приведены данные формата пикселей (в том числе две вычисленные величины для каждой цветовой составляющей) для типичного 24-битного формата.
Таблица 5.3. Типичные данные формата для 24-битных пикселей
Поле Значение Двоичное значение Стартовый бит Количество бит dwRBitMask 16711680 111111110000000000000000 16 8
dwGBitMask 65280 000000001111111110000000 8 8
dwBBitMask 255 000000000000000011111111 0 8
Помните — данные в этих таблицах относятся к конкретной аппаратуре. Они представлены лишь для примера того, как могут выглядеть такие данные, а не как исчерпывающее руководство по форматам пикселей.
Переменные формата пикселей в классе DirectDrawWin
При описании класса DirectDrawWin в главе 3 мы видели, что функция DirectDrawWin::ActivateDisplayMode() после активизации нового видеорежима, но до создания вспомогательных поверхностей приложения, вызывает функцию StorePixelFormatData(). Описание этой функции было отложено до настоящего момента.
Функция StorePixelFormatData() присваивает значения шести переменным класса DirectDrawWin в соответствии с форматом пикселей текущего активного видеорежима; эти переменные определяют стартовый бит и количество бит для каждой цветовой составляющей пикселя. В следующем разделе мы увидим, как эти переменные используются при манипуляциях с памятью поверхности. Функция StorePixelFormatData() выглядит так:
BOOL DirectDrawWin::StorePixelFormatData() {
DDPIXELFORMAT format;
ZeroMemory(&format, sizeof(format));
format.dwSize=sizeof(format);
if (primsurf->GetPixelFormat(&format)!=DD_OK) {
TRACE("StorePixelFormatData() failedn");
return FALSE;
}
loREDbit = LowBitPos(format.dwRBitMask);
WORD hiREDbit = HighBitPos(format.dwRBitMask);
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, и блокировка не состоится. Для упрощения кода мы будем использовать этот флаг.
Теперь мы знаем, как получить прямой доступ к поверхности и что делать с ее памятью, чтобы изменить значения отдельных пикселей. Давайте используем полученные знания на практике. В этой главе мы напишем приложение DirectDraw для просмотра графических файлов формата BMP. Но перед тем как браться за такую программу, необходимо научиться загружать BMP-файлы.