Для организации наиболее эффективного доступа к памяти поверхности необходимо знать глубину ее пикселей. Если вы собираетесь заблокировать поверхность и обратиться к ее памяти, нужно знать, как добраться до каждого отдельного пикселя, как назначить ему нужный цвет и как интерпретировать существующие цветовые данные. Начнем с 8-битных поверхностей.
Наверное, с 8-битными, или палитровыми, поверхностями работать проще всего, потому что каждый пиксель в них представляется одним байтом. Как вы вскоре убедитесь, это особенно упрощает интерпретацию BMP-файлов. Кроме того, каждый пиксель 8-битной поверхности просто соответствует целочисленному индексу палитры, а не закодированному цветовому значению. Впрочем, это достоинство отчасти компенсируется хлопотами по работе с палитрой.
Пиксели High Color (16-битные) выглядят несколько сложнее, однако результат часто оправдывает усилия. Простота использования, характерная для беспалитровых поверхностей, сочетается в них с умеренным расходом памяти (по сравнению с пикселями глубины True Color). Каждый пиксель High Color содержит не индекс, а цвет. Цвета выражаются в виде комбинации трех цветовых составляющих: красной, зеленой и синей (RGB).
Пиксели True Color делятся на две категории (24- и 32-битные), но в обоих случаях используются только 24 бита данных RGB. Лишние 8 бит 32-битных пикселей иногда используются для хранения альфа-канала (то есть данных о прозрачности пикселя). К сожалению, в DirectDraw все еще отсутствует возможность эмуляции альфа-наложения, так что лишние биты 32-битного пикселя часто пропадают впустую.
Разумеется, достоинства поверхностей True Color отчасти снижаются увеличенным расходом памяти. Сказанное поясняет рис. 5.1, на котором наглядно изображены глубины всех четырех вариантов пикселей.
Хотя рис. 5.1 не содержит никаких особых откровений, он позволяет понять, как представление пикселя в памяти зависит от его глубины. Кроме того, его общая структура будет использоваться в других рисунках данного раздела.
Рис. 5.1. Зависимость требований к памяти от глубины пикселей
Шаг поверхности Шагом поверхности называется объем памяти (в байтах), необходимой для представления горизонтальной строки пикселей. Шаг поверхности может совпадать с объемом памяти, необходимой для хранения горизонтальной строки пикселей, но часто оказывается больше.
Для примера возьмем 8-битную поверхность (поскольку один пиксель в таких поверхностях представляется одним байтом, что упрощает вычисления). Предположим, ваше видеоустройство требует, чтобы во внутреннем (для DirectDraw) представлении ширина поверхности была выровнена по границам параграфов (то есть была кратна 4 байтам). В этом случае поверхность с шириной в 10 пикселей будет иметь внутреннюю ширину в 12 байт. Если мы заблокируем эту поверхность и назначим значения пикселей, предполагая, что развертка одной горизонтальной строки занимает 10 байт, изображение получится перекошенным. Проблему можно легко решить, если вместо ширины при развертке поверхностей будет использоваться шаг. При этом независимо от внутренней ширины поверхностей, используемой DirectDraw, вычисление адресов пикселей будет давать правильный результат. Ситуация поясняется на рис. 5.2.
Рис. 5.2. Небольшая 8-битная поверхность с разными значениями шага и ширины
Для беспалитровых поверхностей шаг и ширина поверхности уже не связаны между собой, поскольку каждый пиксель занимает несколько байт. К примеру, возьмем поверхность High Color (16-битную). При ширине поверхности в 5 пикселей каждая строка будет занимать 10 байт. Если видеоустройство требует, чтобы фактическая ширина поверхности выравнивалась по границе параграфа, DirectDraw создает поверхность с внутренней шириной в 12 байт (см. рис. 5.3).
Обратите внимание: если бы ширина 16-битной поверхности на рис. 5.3 была равна 6 пикселям вместо 5, шаг поверхности остался бы прежним, потому что в каждой строке остается свободное место для одного дополнительного пикселя.
Рис. 5.3. Небольшая 16-битная поверхность с разными значениями шага и ширины
Давайте рассмотрим еще два примера, на этот раз с 24-битными поверхностями. При 12-байтовой модели, использованной выше, и 24-битной глубине пикселя в одной строке можно будет хранить 4 пикселя и избежать потерь памяти. Но что произойдет, если поверхность имеет ширину в 5 пикселей? Шаг увеличится до 16 байт, а в каждой строке будет напрасно пропадать один байт. Обе ситуации изображены на рис. 5.4.
Перед тем как двигаться дальше, я хотел бы пояснить, что пример с выравниваем по границе параграфов не следует понимать слишком буквально. В соответствии с идеологией DirectDraw вам необходимо знать лишь то, что для перемещения между горизонтальными строками пикселей необходимо использовать шаг поверхности, а не ее ширину. Примеры лишь показывают, почему шаг поверхности настолько важен для работы.
Рис. 5.4. Две небольшие 24-битные поверхности
Шаг поверхности важен еще и потому, что при прямом доступе к памяти поверхности вам приходится работать с отдельными строками пикселей. Полное сохранение всей поверхности на диске и повторная загрузка ее в виде единого блока — неудачная мысль. Такой вариант сработает лишь в том случае, если при загрузке вы пользуетесь тем же видеорежимом и видеоустройством, что и при сохранении поверхности.
Форматы пикселей
Теперь давайте посмотрим, как хранятся в памяти отдельные пиксели. Мы уже знаем, что для 8-битных поверхностей значение пикселя является индексом в палитре. В этом случае поверхность заполняется не цветовыми данными, а ссылками на них. Палитра представляет собой список цветов, в каждом элементе которого хранятся RGB-составляющие, описывающие данный цвет.
В 16-битных пикселях хранится непосредственно цветовая информация. 16-битные форматы пикселей делятся на две основные разновидности: в первой каждая RGB-составляющая представлена 5 битами (оставшийся бит не используется), а во второй 5 битами представлены только красная и синяя составляющие, а для зеленой используется 6 бит. Для этих форматов часто применяются условные обозначения 5-5-5 и 5-6-5 соответственно.
В формате 5-5-5 каждая цветовая составляющая может принимать значения из интервала от 0 до 31. Чем больше значение составляющей, тем интенсивнее она проявляется в результирующем цвете. Формат 5-6-5 работает аналогично, за исключением того, что зеленая составляющая может принимать значения из интервала 0–63. Эти два вида 16-битных пикселей изображены на рис. 5.5.
Дело осложняется тем, что форматы 5-5-5 и 5-6-5 тоже делятся на два типа. На рис. 5.5 изображен RGB -формат, в котором цветовые составляющие хранятся в порядке «красный, зеленый, синий». Также существует BGR-формат, в котором красная и синяя составляющая меняются местами. Вряд ли в ближайшем будущем появятся еще какие-нибудь варианты.
Рис. 5.5. Два распространенных 16-битных формата пикселей
Следовательно, чтобы ваш код был по-настоящему переносимым, вы не должны полагаться на конкретный формат пикселей или класс форматов. Ведь библиотека DirectDraw была создана именно для того, чтобы предоставить обобщенный интерфейс для работы с аппаратными устройствами. Любое ненужное допущение в вашей программе снижает потенциал приложения. Позднее в этой главе мы рассмотрим универсальный код, работающий с любым 16-битным форматом пикселей.
С пикселями формата True Color работать проще, потому что каждая из RGB-составляющих представлена одним байтом. В этом случае значение каждой составляющей принадлежит интервалу 0–255; ноль означает, что составляющая вообще не влияет на результирующий цвет, а 255 - что ее влияние максимально. Форматы пикселей для 24- и 32-битных вариантов изображены на рис. 5.6.
24- и 32-битные пиксели, как и 16-битные, делятся на две разновидности: RGB и BGR. Следовательно, код для работы с пикселями True Color должен использовать сведения о формате, полученные от DirectDraw, и не делать никаких безусловных предположений.
Напоминаю, что альфа-байт в 32-битных пикселях часто остается неиспользованным. Если ваша программа не работает с альфа-данными, вы можете выбирать между 24- и 32-битным видеорежимами. Работа в 24-битном режиме экономит память.
Рис. 5.6. Типичные форматы пикселей True Color
Получение данных о формате пикселей
Сведения о формате пикселей поверхности можно получить функцией GetPixelFormat() интерфейса DirectDrawSurface, в которой для передачи данных используется структура DDPIXELFORMAT. Функция GetPixelFormat() применяется так:
DDPIXELFORMAT format;
ZeroMemory(&format, sizeof(format));
format.dwSize=sizeof(format);
surf->GetPixelFormat(&format);