TBitmap.PixelFormat

PixelFormat

Формат пикселя (TBitmap.PixelFormat) влияет на способ хранения информации о цвете. Подразумевалась небольшая справочная статья, но материала оказалось неожиданно много, что незаметная статья из глубин сайта выросла до целой записи на главную страницу.

Какие бывают форматы

Все способы хранения информации о цвете можно поделить на два класса: с палитрой и без палитры.

  • Без палитры означает, что цвет зашифрован прямо в байтах пикселя.
  • С палитрой — означает, что в байте (или части байта) пикселя содержится номер в палитре. Палитра — это нумерованная таблица, где каждому цвету сопоставлен номер. Сам цвет представлен тройкой байт R-G-B.
ФорматПалитраВозможное количество цветовОписание
pfDeviceУстановка этого свойства приведет к созданию DDB — аппаратно-зависимого битового образа.
Подробнее…
pf1BitИспользуется2Палитра 0-1. Это не обязательно черный-белый, зависит от настроек палитры.
Подробнее…
pf4BitИспользуется24 = 16Палитра на 16 цветов.
Подробнее…
pf8BitИспользуется28 = 256Палитра на 256 цветов.
Подробнее…
pf15Bit215 = 32768Три цвета размазаны на два байта по формуле R-G-B = 5-5-5. Для получения цветовых составляющих надо использовать маски: 0x001F — синяя, 0x03E0 — зеленая и 0x7C00 — красная.
Подробнее…
pf16Bit216 = 65536Три цвета размазаны на два байта по формуле R-G-B = 5-6-5. Для получения цветовых составляющих надо использовать маски 0x001F — синяя, 0x07E0 — зеленая и 0xF800 — красная.
Подробнее…
pf24Bit224 = 16 777 216Три байта цвета — три цветовых компонента. Порядок следования: Blue-Green-Red.
Подробнее…
pf32Bit232 = 4 294 967 296Четыре байта на цвет — это три цветовых компонента плюс что-то. На самом деле цветов 16 777 216, остальное разнообразие достигается от использования 4-го байта. Обычно это альфа-канал, прозрачность.
Порядок следования: Blue-Green-Red-Alpha.
Подробнее…
pfCustomУстановка этого свойства приведет к генерации исключения. Однако, он существует для реализации собственных наследников TBitmap.
Подробнее…

Число байт на пиксель

Предлагаются следующие функции, где происходит точное определение числа байт на пиксель. Для форматов, подразумевающих неполный байт, вернёт 0. Там где число байт непредсказуемо, вычисляем по факту.

Подробно о каждом формате

Как правило, следующая информация нужна, если работаем с битмапом через его свойство ScanLine. Это свойство возвращает указатель на начало массива байт, в котором зашифрованы пиксели запрошенной строки. Размер массива и способ работы с ним напрямую зависит от текущего PixelFormat битмапа.

Если мы запросим ScanLine[15], то нам вернёт массив, состоящий из всех пикселей в строке 15.

Это изображение имеет пустой атрибут alt; его имя файла - s1.jpg
Рис.1. Изображение 32×32. PixelFormat=pf32bit

На рисунке видим, что для битмапа с шириной в 32 пикселя, массив пикселей занимает 128 байт. Это связано с тем, что для 32-битного формата под цвет отводится 4 байта. Сейчас рассмотрим каждый PixelFormat в отдельности.

pf1Bit

Размер пикселя1 бит
В одном байте 8 пикселей. Биты пикселей расположены слева направо, то есть наблюдаем обратный порядок следования бит
Тип указателяPByte
Inc(P, X) сместит указатель на X байт. Поэтому для получения нужного байта используем операцию Inc(P, X div 8)
Диапазон цвета2
Палитра. Не обязательно черный-белая. Цвета можно задать любые. Просто их два.
Это изображение имеет пустой атрибут alt; его имя файла - s2.jpg

Рассмотрим рисунок.

Нас интересует пиксель с индексом X=17. Выходим на 2-й байт в массиве байт, 17 div 8 = 2.

В полученном байте нас интересует бит по смещению +1 слева. Он на самом деле соответствует биту +6 в байте. Поэтому, чтобы найти смещение слева находим остаток от деления 17 mod 8 = 1 и инвертируем его (строка в коде ниже F := 7-X MOD 8)

Это изображение имеет пустой атрибут alt; его имя файла - g1bit.gif
Рис.2. PixelFormat=pf1bit (Ctrl+2). Массив 4 байта

Для получения цвета используем такую конструкцию:

Для проверки используем в проге при выделении, и пикселя, и строки, процедуру инвертирования:

pf4Bit

Размер пикселя4 бит
В одном байте 2 пикселя. Сложность только в получении и назначении полубайта. Внутри полубайта порядок следования бит стандартный, как положено, справа налево. Т.е. 0-й, это самый правый в полубайте.
Тип указателяPByte
Inc(P, X) сместит указатель на X байт. Поэтому для получения нужного байта используем операцию Inc(P, X div 2)
Диапазон цвета16
Палитра
Это изображение имеет пустой атрибут alt; его имя файла - g4bit.gif
Рис.3. PixelFormat=pf4bit (Ctrl+3). Массив 16 байт

Тут есть масса способов, например, не вычислять, а хранить маски в массиве из двух элементов. Индекс в массиве находить через X MOD 2. Вместо + использовать OR. И так далее. Ставка сделана на возможность прокомментировать действия и некоей наглядности в последовательности действий.

Для получения цвета используем такую конструкцию:

Для проверки используем процедуру инвертирования:

pf8Bit

Размер пикселя8 бит
Один пиксель — один байт. Тут всё настолько понятно, что дополнительные комментарии классифицирую, как «вода»
Тип указателяPByte
Inc(P, X) сместит указатель на X байт
Диапазон цвета256
Палитра
Это изображение имеет пустой атрибут alt; его имя файла - g8bit.gif
Рис.4. PixelFormat=pf8bit (Ctrl+4). Массив 32 байт

Для получения цвета используем такую конструкцию:

Проверяем через инвертирование:

pf15Bit

Размер пикселя2 байта
Под сам цвет используется 15 бит. И 1 бит на своё усмотрение. Это может быть бит прозрачности, например. Из-за этого своего усмотрения, если присвоить PixelFormat := pf15bit, мы получим pfCustom. Подразумевается, что тут программист сам решает, куда девать лишний бит.
На каждый канал R-G-B выделяется по 5 бит: 5-5-5

Тип указателяPWord
Рассматриваем последовательность из двух байт сразу как Word-структуру.
Inc(P, X) смещает указатель на X*2 байта

Диапазон цвета32768
Палитры нет. В пикселе зашифрован непосредственно цвет.
Маска для красного7C00
01111100 00000000
Маска для зелёного03E0
00000011 11100000
Маска для синего001F
00000000 00011111
Это изображение имеет пустой атрибут alt; его имя файла - g15bit.gif
Рис.5. PixelFormat=pf15bit (Ctrl+5). Массив 64 байт

Особенность следующая. Если рассмотрим «сырые» данные, на рисунке 5 это верхняя строка «raw», то увидим, что если воспринимать байты в порядке следования, то наложив маски, мы ни разу не получим ожидаемые значения для каналов.

Однако, если понять два байта, как указатель на WORD, на рисунке это вторая строка «word», то всё встаёт на свои места. И можно заметить, что самый старший бит в слове не задействован никак. Он просто есть — пользуй.

Есть ещё одна особенность. Чтобы получить значение канала в интервале 0..255, мы должны масштабировать полученные значения. Понимаем, что для 5-битного числа, максимальным является 31. Что должно соответствовать 255 на выходе. Поэтому просто находим через пропорцию нужное нам правильное значение для цветового канала.

Проверяем аналогично через инвертирование:

pf16Bit

Размер пикселя2 байта
На зелёный канал отводится 6 бит: R-G-B = 5-6-5. Поэтому на весь пиксель выделено 16 бит, ровно 2 байта
Тип указателяPWord
Рассматриваем последовательность из двух байт сразу как Word-структуру.
Inc(P, X) смещает указатель на X*2 байта

Диапазон цвета65536
Палитры нет. В пикселе зашифрован непосредственно цвет.
Маска для красногоF800
11111000 00000000
Маска для зелёного07E0
00000111 11100000
Маска для синего001F
00000000 00011111
Это изображение имеет пустой атрибут alt; его имя файла - g16bit.gif
Рис.6. PixelFormat=pf16bit (Ctrl+6). Массив 64 байт

Всё точно также, как для 15-битного. За исключением того, что маски для каналов чуть другие и для зелёного канала учитываем максимум 6-битного числа, равный 63.

Проверяем аналогично через инвертирование:

pf24Bit

Размер пикселя3 байта
Каждый байт хранит значение значение 0..255 соответствующего цветового канала. Последовательность байт, отвечающих за каналы: Blue-Green-Red.
Тип указателяPRGBTriple
Inc(P, X) сместит указатель на X*3 байта
Диапазон цвета16 777 216
Это изображение имеет пустой атрибут alt; его имя файла - g24bit.gif
Рис.7. PixelFormat=pf24bit (Ctrl+7). Массив 96 байт

pf32Bit

Размер пикселя4 байта
Каждый байт хранит значение в интервале 0..255 для соответствующего цветового канала. Последовательность байт, отвечающих за каналы: Blue-Green-Red-Alpha.
Тип указателяPRGBQuad
Inc(P, X) сместит указатель на X*4 байта
Диапазон цвета4 294 967 296
Это изображение имеет пустой атрибут alt; его имя файла - g32bit.gif
Рис.8. PixelFormat=pf32bit (Ctrl+8). Массив 128 байт

pfCustom

Существует для реализации собственных наследников TBitmap.

Присвоить его напрямую не получится, Delphi сгенерирует исключение. Но вот если присвоить pf15bit, то у битмапа свойство PixelFormat станет равным pfCustom. По уму, самым правильным было бы, при наличии 2 байт на пиксел (через определялку GetBytesPerPixel ), однозначно воспринимать и обрабатывать его, как будто он 15-битный.

Но мы будем считать, что это верно всегда, поэтому в итоговой функции получении цвета, анализировать количество байт на пиксель даже не станем, pfCustom однозначно понимаем, как 15-битный пиксель.

pfDevice

Установка этого свойства приведет к созданию DDB — аппаратно-зависимого битового образа. Это означает определённую непредсказуемость в количестве байт на пиксель и в поведении алгоритмов обработки изображения.
При использовании этого формата не рекомендуется использовать ScanLine для модификации пикселей. Однако, на таком битмапе можно спокойной рисовать средствами GDI и декларируется, что работать с таким битмапом быстрее, нежели с DIB — аппаратно-независимым образом.
Все остальные форматы производят именно DIB и они вполне предсказуемы.

Однако, если мы определили, что используется 4 байта на пиксель, то можно рискнуть использовать 32-битный подход и, возможно, всё будет хорошо. Менять пиксели через ScanLine тоже сможем. Но это риски, которые программист берёт на себя. Система, в этом случае, ответственность за действия программиста не несёт.

Использовать данный формат для обработки изображения не имеет смысла. Для этого существуют все остальные форматы. Форматов хранения информации о цвете намного больше, чем представлено в PixelFormat. Для 4-байтового пикселя в том же GDIPlus существует 3 разновидности:

PixelFormat32bppRGBПо 8 бит на каждый канал ARGB
PixelFormat32bppARGBПо 8 бит на каждый канал ARGB.
Улучшен механизм смешивания компонент RGB и альфа канала
PixelFormat32bppPARGBПо 8 бит на каналы RGB.
Альфа канал не используется.

Как там карточка шифрует пиксель — это уж её узко-частное дело. Можно попытаться пойти в сторону DescribePixelFormat и окольными путями вызнать всё, но это во-первых, не нужно, во вторых, конкретно к нашему битмапу отношения не имеет. В нашем битмапе есть pf1Bit..pf32Bit, все остальные прелести ищем в других технологиях.

Итоги

В качестве итога можно рассмотреть сугубо академическую функцию, которая сама определяет с чем имеет дело, и возвращает цвет по переданному указателю на массив пикселей и координаты по X.

Как было сказано выше, представленные функции преследуют исключительно академические цели. Они здесь только для демонстрации особенностей работы с каждым из форматов. На практике алгоритмы работы с битмапом совсем другие.

Во-первых, мы вольны назначать формат пикселя сами, и поэтому не нужно выбирать всякий раз метод получения цвета. Просто перед использованием, рукам выставляем PixelFormat, например, в pf24Bit и погнали ))).

Во-вторых, лично я беру указатель на массив пикселей один раз и потом уже работаю непосредственно с массивом. ScanLine вызывается один раз, в самом начале. Свойство ScanLine, ввиду своей параноидальной реализации, не слишком быстрое, поэтому его регулярное использование притормаживает процесс.

В-третьих, никто не заставляет «догадываться» о текущем PixelFormat. Что указали, с тем и работаем. Самые популярные и быстрые форматы для работы с битмапом — это pf8Bit, pf24Bit, pf32Bit.

pf1Bit — это про упаковку монохромного изображения, к технологии монохромного изображения имеет весьма опосредованное отношение.

Палитра

Получить цвет из палитры

PixelFormat в диапазоне pf1bit, pf4bit, pf8bit шифрует не цвет, а индекс в таблице цветов. Поэтому, чтобы получить цвет, необходимо по добытому значению вызвать следующую функцию.

Рисуем палитру

Листинг

[свернуть]

Работа с палитрой

Примеры из телеги. Там уже много всего интересного, чего нет на сайте. Подписываемся, комментируем, критикуем, улучшаем контент.

Эффект пламени (исходники)

Предпросмотр

[свернуть]

Плазма (исходники)

Предпросмотр

[свернуть]


Скачать

Друзья, спасибо за внимание!

Исходник (zip) 270 Кб. Delphi XE 7, XE 11

Исполняемый файл (zip) 1.12 Мб.

Это изображение имеет пустой атрибут alt; его имя файла - g3.gif

5 12 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

2 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
Denis

Спасибо за статью. А демка вообще загляденье!

2
0
Не нашли ответ на свой вопрос? Задайте его здесь!...x