Формат пикселя (TBitmap.PixelFormat) влияет на способ хранения информации о цвете. Подразумевалась небольшая справочная статья, но материала оказалось неожиданно много, что незаметная статья из глубин сайта выросла до целой записи на главную страницу.
Какие бывают форматы
Все способы хранения информации о цвете можно поделить на два класса: с палитрой и без палитры.
- Без палитры означает, что цвет зашифрован прямо в байтах пикселя.
- С палитрой — означает, что в байте (или части байта) пикселя содержится номер в палитре. Палитра — это нумерованная таблица, где каждому цвету сопоставлен номер. Сам цвет представлен тройкой байт R-G-B.
Формат | Палитра | Возможное количество цветов | Описание |
---|---|---|---|
pfDevice | | | Установка этого свойства приведет к созданию DDB — аппаратно-зависимого битового образа. Подробнее… |
pf1Bit | Используется | 2 | Палитра 0-1. Это не обязательно черный-белый, зависит от настроек палитры. Подробнее… |
pf4Bit | Используется | 24 = 16 | Палитра на 16 цветов. Подробнее… |
pf8Bit | Используется | 28 = 256 | Палитра на 256 цветов. Подробнее… |
pf15Bit | — | 215 = 32768 | Три цвета размазаны на два байта по формуле R-G-B = 5-5-5. Для получения цветовых составляющих надо использовать маски: 0x001F — синяя, 0x03E0 — зеленая и 0x7C00 — красная. Подробнее… |
pf16Bit | — | 216 = 65536 | Три цвета размазаны на два байта по формуле R-G-B = 5-6-5. Для получения цветовых составляющих надо использовать маски 0x001F — синяя, 0x07E0 — зеленая и 0xF800 — красная. Подробнее… |
pf24Bit | — | 224 = 16 777 216 | Три байта цвета — три цветовых компонента. Порядок следования: Blue-Green-Red. Подробнее… |
pf32Bit | — | 232 = 4 294 967 296 | Четыре байта на цвет — это три цветовых компонента плюс что-то. На самом деле цветов 16 777 216, остальное разнообразие достигается от использования 4-го байта. Обычно это альфа-канал, прозрачность. Порядок следования: Blue-Green-Red-Alpha. Подробнее… |
pfCustom | | | Установка этого свойства приведет к генерации исключения. Однако, он существует для реализации собственных наследников TBitmap. Подробнее… |
Число байт на пиксель
Предлагаются следующие функции, где происходит точное определение числа байт на пиксель. Для форматов, подразумевающих неполный байт, вернёт 0. Там где число байт непредсказуемо, вычисляем по факту.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
// Определить число байт на пиксель по формату пикселя function GetBytesPerPixel( APixelFormat: Vcl.Graphics.TPixelFormat): Byte; overload; const ByteCounts: array [Vcl.Graphics.TPixelFormat] of Byte = (4,0,0,1,2,2,3,4,2); begin Result := ByteCounts[APixelFormat]; end; // Определить число байт на пиксель // или вычислить по битмапу function GetBytesPerPixel( ABitmap: Vcl.Graphics.TBitmap): Byte; overload; var p1, p2: PByte; w1, w2, i: Integer; begin if (ABitmap.PixelFormat in [pfDevice, pfCustom]) and (ABitmap.Height>1) and (ABitmap.Width>1) then begin // Вычисляем число байт p1 := ABitmap.ScanLine[ABitmap.Height-1]; p2 := ABitmap.ScanLine[ABitmap.Height-2]; w1 := NativeInt(p2) - NativeInt(p1); Result := 4; for i := 4 downto 1 do begin w2 := BytesPerScanline(ABitmap.Width, i*8, 32); if w2 = w1 then exit(i); end; end else // Определяем число байт Result := GetBytesPerPixel(ABitmap.PixelFormat); end; |
Подробно о каждом формате
Как правило, следующая информация нужна, если работаем с битмапом через его свойство ScanLine. Это свойство возвращает указатель на начало массива байт, в котором зашифрованы пиксели запрошенной строки. Размер массива и способ работы с ним напрямую зависит от текущего PixelFormat битмапа.
Если мы запросим ScanLine[15], то нам вернёт массив, состоящий из всех пикселей в строке 15.
На рисунке видим, что для битмапа с шириной в 32 пикселя, массив пикселей занимает 128 байт. Это связано с тем, что для 32-битного формата под цвет отводится 4 байта. Сейчас рассмотрим каждый PixelFormat в отдельности.
pf1Bit
Размер пикселя | 1 бит |
В одном байте 8 пикселей. Биты пикселей расположены слева направо, то есть наблюдаем обратный порядок следования бит | |
Тип указателя | PByte |
Inc(P, X) сместит указатель на X байт. Поэтому для получения нужного байта используем операцию Inc(P, X div 8) | |
Диапазон цвета | 2 |
Палитра. Не обязательно черный-белая. Цвета можно задать любые. Просто их два. | |
Рассмотрим рисунок.
Нас интересует пиксель с индексом X=17. Выходим на 2-й байт в массиве байт, 17 div 8 = 2.
В полученном байте нас интересует бит по смещению +1 слева. Он на самом деле соответствует биту +6 в байте. Поэтому, чтобы найти смещение слева находим остаток от деления 17 mod 8 = 1 и инвертируем его (строка в коде ниже F := 7-X MOD 8)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
// Получить бит с номером X из массива P function GetBit(P: PByte; X: Integer): Byte; var M,F: Byte; begin // Получаем нужный байт в массиве Inc(P, X div 8); // Смещение для перевёрнутого индекса бита в байте F := 7-X MOD 8; // Маска для получения бита M := $01 SHL F; // Получаем бит по маске и смещаем на 0-ю позицию // чтобы было либо 0, либо 1, без вариантов Result := (P^ AND M) SHR F; end; // Установить бит с номером X в массиве P procedure SetBit(P: PByte; X: Integer; V : Byte); var M,F: Byte; begin // Получаем нужный байт в массиве Inc(P, X div 8); // Отбросим мусор в байте, нужен только 0-й бит V := V AND $01; // Смещение для перевёрнутого индекса бита в байте F := 7-X MOD 8; // Маска для бита M := $01 SHL F; // Установка бита if V <> 0 then P^ := P^ OR M // В этом бите ставим 1 else P^ := P^ AND NOT M; // Ставим жёсткий 0 end; |
Для получения цвета используем такую конструкцию:
1 |
Result := GetPaletteColor(BMP, GetBit(P,X)); |
Для проверки используем в проге при выделении, и пикселя, и строки, процедуру инвертирования:
1 2 3 4 5 |
procedure Invert1BitPixel(P: PByte; X: Integer); begin SetBit(P, X, not GetBit(P,X)); end; |
pf4Bit
Размер пикселя | 4 бит |
В одном байте 2 пикселя. Сложность только в получении и назначении полубайта. Внутри полубайта порядок следования бит стандартный, как положено, справа налево. Т.е. 0-й, это самый правый в полубайте. | |
Тип указателя | PByte |
Inc(P, X) сместит указатель на X байт. Поэтому для получения нужного байта используем операцию Inc(P, X div 2) | |
Диапазон цвета | 16 |
Палитра | |
Тут есть масса способов, например, не вычислять, а хранить маски в массиве из двух элементов. Индекс в массиве находить через X MOD 2. Вместо + использовать OR. И так далее. Ставка сделана на возможность прокомментировать действия и некоей наглядности в последовательности действий.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// Получить полубайт с номером X из массива P function GetByteSemi(P: PByte; X: Integer): Byte; var M,F: Byte; begin // Получаем нужный байт в массиве Inc(P, X div 2); // Смещение для полубайта (либо 0, либо 4) F := 4*(1-X MOD 2); // Маска (либо 11110000, либо 00001111) M := $0F SHL F; // Получаем значение по маске и // смещаем вправо, если есть на что смещать Result := (P^ AND M) SHR F; end; // Установить полубайт с номером X в массиве P procedure SetByteSemi(P: PByte; X: Integer; V: Byte); var V1,F1,V2,F2,MM: Byte; begin // Получаем нужный байт в массиве Inc(P, X div 2); // Получить значение всего байта V2 := P^; // Убрать ненужное в левой части значения V1 := V AND $0F; // Смещение для остающегося полубайта F2 := 4*(X MOD 2); // Маска для остающегося полубайта MM := $0F SHL F2; // Чистим остающийся полубайт V2 := V2 AND MM; // Смещение для требуемого полубайта F1 := 4*(1 - X MOD 2); // Либо остаёмся на месте, либо смещаемся влево V1 := V1 SHL F1; // Складываем обе уже "чистые" части байта P^ := V2 + V1; end; |
Для получения цвета используем такую конструкцию:
1 |
Result := GetPaletteColor(BMP, GetByteSemi(P,X)); |
Для проверки используем процедуру инвертирования:
1 2 3 4 |
procedure Invert4BitPixel(P: PByte; X: Integer); begin SetByteSemi(P, X, not GetByteSemi(P,X)); end; |
pf8Bit
Размер пикселя | 8 бит |
Один пиксель — один байт. Тут всё настолько понятно, что дополнительные комментарии классифицирую, как «вода» | |
Тип указателя | PByte |
Inc(P, X) сместит указатель на X байт | |
Диапазон цвета | 256 |
Палитра | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Получить байт с номером X из массива P function GetByte(P: PByte; X: Integer): Byte; begin // Получаем нужный байт в массиве Inc(P, X); // Возвращаем целиком Result := P^; end; // Установить байт с номером X в массиве P procedure SetByte(P: PByte; X: Integer; V: Byte); begin // Получаем нужный байт в массиве Inc(P, X); // Устанавливаем байт P^ := V; end; |
Для получения цвета используем такую конструкцию:
1 |
Result := GetPaletteColor(BMP, GetByte(P,X)); |
Проверяем через инвертирование:
1 2 3 4 |
procedure Invert8BitPixel(P: PByte; X: Integer); begin SetByte(P, X, not GetByte(P,X)); end; |
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 | |
Особенность следующая. Если рассмотрим «сырые» данные, на рисунке 5 это верхняя строка «raw», то увидим, что если воспринимать байты в порядке следования, то наложив маски, мы ни разу не получим ожидаемые значения для каналов.
Однако, если понять два байта, как указатель на WORD, на рисунке это вторая строка «word», то всё встаёт на свои места. И можно заметить, что самый старший бит в слове не задействован никак. Он просто есть — пользуй.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// Получить 15-битный цвет по индексу в массиве P function Get15BitColor(P: PWord; X: Integer): TColor; var R,G,B: Word; begin // Получаем нужное слово в массиве Inc(P,X); // Применяем маски и смещаем R := (P^ AND $7C00) SHR 10; // 0..31 G := (P^ AND $03E0) SHR 5; // 0..31 B := (P^ AND $001F); // 0..31 // Масштабируем 31<=>255 R := Min(Round(R*255/31),255); G := Min(Round(G*255/31),255); B := Min(Round(B*255/31),255); // Получаем цвет Result := RGB(R,G,B); end; // Установить 15-битный цвет по индексу в массиве P procedure Set15BitColor(P: PWord; X: Integer; V: TColor); var R,G,B: Word; begin // Получаем нужное слово в массиве Inc(P,X); // Обратное масштабирование V1*V2/V3 R := MulDiv(GetRValue(V),31,255); G := MulDiv(GetGValue(V),31,255); B := MulDiv(GetBValue(V),31,255); // Смещаем и применяем маску R := (R SHL 10) AND $7C00; G := (G SHL 5) AND $03E0; B := (B AND $001F); // Всё складываем P^ := R+G+B; end; |
Есть ещё одна особенность. Чтобы получить значение канала в интервале 0..255, мы должны масштабировать полученные значения. Понимаем, что для 5-битного числа, максимальным является 31. Что должно соответствовать 255 на выходе. Поэтому просто находим через пропорцию нужное нам правильное значение для цветового канала.
Проверяем аналогично через инвертирование:
1 2 3 4 |
procedure Invert15BitPixel(P: PWord; X: Integer); begin Set15BitColor(P, X, not Get15BitColor(P,X)); end; |
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 | |
Всё точно также, как для 15-битного. За исключением того, что маски для каналов чуть другие и для зелёного канала учитываем максимум 6-битного числа, равный 63.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// Получить 16-битный цвет по индексу в массиве P function Get16BitColor(P: PWord; X: Integer): TColor; var R,G,B: Word; begin // Получаем нужное слово в массиве Inc(P,X); // Применяем маски и смещаем R := (P^ AND $F800) SHR 11; //0..31 G := (P^ AND $07E0) SHR 5; //0..63 B := (P^ AND $001F); //0..31 // Масштабируем 31<=>255 R := Min(Round(R*255/31),255); // 31<=>255 G := Min(Round(G*255/63),255); // 63<=>255 B := Min(Round(B*255/31),255); // 31<=>255 // Получаем цвет Result := RGB(R,G,B); end; // Установить 16-битный цвет по индексу в массиве P procedure Set16BitColor(P: PWord; X: Integer; V: TColor); var R,G,B: Word; begin // Получаем нужное слово в массиве Inc(P,X); // Обратное масштабирование V1*V2/V3 R := MulDiv(GetRValue(V),31,255); G := MulDiv(GetGValue(V),63,255); B := MulDiv(GetBValue(V),31,255); // Смещаем и применяем маску R := (R SHL 11) AND $F800; G := (G SHL 5) AND $07E0; B := (B AND $001F); // Помещаем сумму в искомое слово P^ := R+G+B; end; |
Проверяем аналогично через инвертирование:
1 2 3 4 |
procedure Invert16BitPixel(P: PWord; X: Integer); begin Set16BitColor(P, X, not Get16BitColor(P,X)); end; |
pf24Bit
Размер пикселя | 3 байта |
Каждый байт хранит значение значение 0..255 соответствующего цветового канала. Последовательность байт, отвечающих за каналы: Blue-Green-Red. | |
Тип указателя | PRGBTriple |
Inc(P, X) сместит указатель на X*3 байта | |
Диапазон цвета | 16 777 216 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// Получить 24-битный цвет по индексу в массиве P function Get24BitColor(P: PRGBTriple; X: Integer): TColor; begin // Получаем нужный трипл в массиве Inc(P,X); // Получаем цвет Result := RGB(P^.rgbtRed, P^.rgbtGreen, P^.rgbtBlue); end; // Установить 24-битный цвет по индексу в массиве P procedure Set24BitColor(P: PRGBTriple; X: Integer; V : TColor); begin // Получаем нужный трипл в массиве Inc(P,X); // Помещаем в поля нужные каналы P^.rgbtRed := GetRValue(V); P^.rgbtGreen := GetGValue(V); P^.rgbtBlue := GetBValue(V); end; |
pf32Bit
Размер пикселя | 4 байта |
Каждый байт хранит значение в интервале 0..255 для соответствующего цветового канала. Последовательность байт, отвечающих за каналы: Blue-Green-Red-Alpha. | |
Тип указателя | PRGBQuad |
Inc(P, X) сместит указатель на X*4 байта | |
Диапазон цвета | 4 294 967 296 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
// Получить 32-битный цвет по индексу в массиве P function Get32BitColor(P: PRGBQuad; X: Integer; var A: Byte): TColor; begin // Получаем нужную квадру в массиве Inc(P,X); // Получаем цвет Result := RGB(P^.rgbRed,P^.rgbGreen,P^.rgbBlue); // Получаем значение альфа-канала A := P^.rgbReserved; end; function Get32BitColor(P: PRGBQuad; X: Integer): TColor; var A: Byte; begin Result := Get32BitColor(P,X,A); end; // Установить 32-битный цвет по индексу в массиве P procedure Set32BitColor(P: PRGBQuad; X: Integer; V: TColor; A: Byte = 255); begin // Получаем нужную квадру в массиве Inc(P,X); // Распихиваем значения каналов по полям P^.rgbRed := GetRValue(V); P^.rgbGreen := GetGValue(V); P^.rgbBlue := GetBValue(V); P^.rgbReserved := A; end; |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
// Получить цвет по индексу в массиве P // ранее выбранной строки битмапа function GetBitmapRowColor(P: Pointer; X: Integer; BMP: TBitmap): TColor; overload; begin case BMP.PixelFormat of pf1Bit: // PByte Result := GetPaletteColor(BMP, GetBit(P,X)); pf4Bit: // PByte Result := GetPaletteColor(BMP, GetByteSemi(P,X)); pf8Bit: // PByte Result := GetPaletteColor(BMP, GetByte(P,X)); pf15Bit, pfCustom: // PWord Result := Get15BitColor(P,X); pf16Bit: // PWord Result := Get16BitColor(P,X); pf24Bit: // PRGBTriple Result := Get24BitColor(P,X); pf32Bit: // PRGBQuad Result := Get32BitColor(P,X); else // pfDevice // В любом случае, это попытка понять данные // Может сработать, может нет // Алгоритмы строить на предположениях НЕЛЬЗЯ!!! case GetBytesPerPixel(BMP) of 1: Result := GetPaletteColor(BMP, GetByte(P,X)); 2: Result := Get16BitColor(PWord(P),X); 3: Result := Get24BitColor(PRGBTriple(P),X); 4: Result := Get32BitColor(PRGBQuad(P),X); else raise Exception.Create('Unknown PixelFormat'); end; end; end; // Получить цвет из битмапа для пикселя (X,Y) // Использовать в практических целях не рекомендуется // Получать каждый раз указатель на строку пикселей // - не самая быстрая затея function GetBitmapRowColor(BMP: TBitmap; X,Y: Integer): TColor; overload; begin Result := GetBitmapRowColor(BMP.ScanLine[Y], X, BMP); end; |
Как было сказано выше, представленные функции преследуют исключительно академические цели. Они здесь только для демонстрации особенностей работы с каждым из форматов. На практике алгоритмы работы с битмапом совсем другие.
Во-первых, мы вольны назначать формат пикселя сами, и поэтому не нужно выбирать всякий раз метод получения цвета. Просто перед использованием, рукам выставляем PixelFormat, например, в pf24Bit и погнали ))).
Во-вторых, лично я беру указатель на массив пикселей один раз и потом уже работаю непосредственно с массивом. ScanLine вызывается один раз, в самом начале. Свойство ScanLine, ввиду своей параноидальной реализации, не слишком быстрое, поэтому его регулярное использование притормаживает процесс.
В-третьих, никто не заставляет «догадываться» о текущем PixelFormat. Что указали, с тем и работаем. Самые популярные и быстрые форматы для работы с битмапом — это pf8Bit, pf24Bit, pf32Bit.
pf1Bit — это про упаковку монохромного изображения, к технологии монохромного изображения имеет весьма опосредованное отношение.
Палитра
Получить цвет из палитры
PixelFormat в диапазоне pf1bit, pf4bit, pf8bit шифрует не цвет, а индекс в таблице цветов. Поэтому, чтобы получить цвет, необходимо по добытому значению вызвать следующую функцию.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// Получить цвет из палитры BMP function GetPaletteColor(BMP: TBitmap; V: Byte): Integer; var Entry: array [0..$FF] of PALETTEENTRY; Entries: Integer; begin Entries := GetPaletteEntries(BMP.Palette, 0, $100, Entry); if Entries = 0 then raise Exception.Create('GetPaletteEntries'); if V >= Entries then raise Exception.Create('Palette Index'); Result := RGB(Entry[V].peRed, Entry[V].peGreen, Entry[V].peBlue); end; |
Рисуем палитру
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
// Поменять местами значения в A и B procedure Swap(var A,B: Integer); var C: Integer; begin C := A; A := B; B := C; end; // Нарисовать палитру из BMP // APoint - верхняя левая точка откуда рисовать // AIndex - индекс в палитре // AVertical - рисовать ли палитру вертикально // Возвращает прямоугольник цвета с индексом AIndex function DrawPalette(ACanvas: TCanvas; BMP: TBitmap; const APoint: TPoint; AIndex: Integer; AVertical: Boolean): TRect; var Entry: array [0..$FF] of PALETTEENTRY; Entries,i: Integer; h,x,y: Integer; rct, tmp: TRect; s: string; begin Entries := GetPaletteEntries(BMP.Palette, 0, $100, Entry); if Entries = 0 then raise Exception.Create('GetPaletteEntries'); if AIndex >= Entries then raise Exception.Create('Palette Index'); h := Entries div 16; Result := TRect.Empty; with ACanvas do begin Font.Color := clBtnShadow; Pen.Color := clBtnFace; for i := 0 to Entries-1 do begin x := i mod 16; y := i div 16; if AVertical then Swap(x,y); rct := Rect(APoint.X + x * 12, APoint.Y + y * 12, 0,0); rct.Width := 12; rct.Height := 12; if i=0 then tmp := rct else tmp.BottomRight := rct.BottomRight; if i = AIndex then Result := rct; Brush.Color := RGB(Entry[i].peRed, Entry[i].peGreen, Entry[i].peBlue); Rectangle(rct.Left, rct.Top,rct.Right+1, rct.Bottom+1); if (x = 0) and (AVertical and (h>0) or (h>1)) then begin s := IntToHex(y*h,2); Brush.Style := bsClear; TextOut(APoint.X - 4 - TextWidth(s), rct.Top, s); TextOut(APoint.X + 12*h + 4, rct.Top, IntToStr(y*h)); end; end; Brush.Style := bsClear; Pen.Color := clBtnShadow; Rectangle(tmp); end; end; |
Работа с палитрой
Примеры из телеги. Там уже много всего интересного, чего нет на сайте. Подписываемся, комментируем, критикуем, улучшаем контент.
Эффект пламени (исходники)
Плазма (исходники)
Скачать
Друзья, спасибо за внимание!
Исходник (zip) 270 Кб. Delphi XE 7, XE 11
Исполняемый файл (zip) 1.12 Мб.
Спасибо за статью. А демка вообще загляденье!
Рад, что пригодилось ))))