Мастера DELPHI, Delphi programming community Рейтинг@Mail.ru Титульная страница Поиск, карта сайта Написать письмо 
| Новости |
Новости сайта
Поиск |
Поиск по лучшим сайтам о Delphi
FAQ |
Огромная база часто задаваемых вопросов и, конечно же, ответы к ним ;)
Статьи |
Подборка статей на самые разные темы. Все о DELPHI
Книги |
Новинки книжного рынка
Новости VCL
Обзор свежих компонент со всего мира, по-русски!
|
| Форумы
Здесь вы можете задать свой вопрос и наверняка получите ответ
| ЧАТ |
Место для общения :)
Орешник |
Коллекция курьезных вопросов из форумов
KOL и MCK |
KOL и MCK - Компактные программы на Delphi
Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / rsdn.org

 
Чтобы не потерять эту дискуссию, сделайте закладку « предыдущая ветвь | форум | следующая ветвь »
Страницы: 1 2

Как у 32bit'ных битмапов указывать/указывается RGBA они или XRGB? [D7, WinXP]


Зодчий   (24.06.18 11:44

Я создал 32-bit'ный битмап. Судя по некоторым симптомам он получился так называемым XRGB. А я-то хотел RGBA.
Что вообще их отличает? Что-то указывается в BITMAPINFOHEADER? Или где?

Или вот допустим мне передали уже DIB-секцию - как получить информацию: она XRGB или RGBA?
И как самому явно задавать нужный мне тип (при создании HBITMAP)?

А так же - как правильно определить row order?

Спасибо!


dmk ©   (24.06.18 12:24[1]

В Windows тип пиксела ARGB. По крайней мере то, что создается через
dbBitmap := CreateDIBSection(dbMemDC, PBitmapInfo(@FInfo)^, DIB_RGB_COLORS, dbMemPtr, 0, 0);

ARGB потому, что об этом говорят константы:
crRed = $00FF0000; //Красный
crBlue = $000000FF; //Синий
crGreen = $0000FF00; //Зеленый


Хотя в VCL.Graphics имеются другие константы (ABGR):
clRed clRed: TColor = $FF;
clBlue: TColor = $FF0000;
clGreen clGreen: TColor = $8000;


Как видите точных правил представления цвета нет.
Поэтому каждый случай надо рассматривать персонально.

RowOrder определяется знаком числа:

 //Заполнение структуры
 with FInfo.bmiHeader do
 begin
   biSize := SizeOf(TBitmapInfoHeader);
   biWidth := FBitmap.dbWidth;
   biHeight := -FBitmap.dbHeight;
   biPlanes := 1;
   biBitCount := FBitmap.dbBpp;
   biCompression := BI_RGB;
   biSizeImage := FBitmap.dbMemSize;
   biXPelsPerMeter := 0;
   biYPelsPerMeter := 0;
   biClrUsed := 0;
   biClrImportant := 0;
 end;

 //Создаем Bitmap
 with FBitmap do
 begin
   dbMemDC := CreateCompatibleDC(0);
   dbBitmap := CreateDIBSection(dbMemDC, PBitmapInfo(@FInfo)^, DIB_RGB_COLORS, dbMemPtr, 0, 0);
   ReleaseDC(0, dbMemDC);
   SelectObject(dbMemDC, dbBitmap);
 end;


dmk ©   (24.06.18 12:26[2]

Переворот байтов можно делать так:
function ARGBToBGRA(AColor: TColorRef): DWord;
asm
 .NOFRAME

 mov eax, AColor
 bswap eax
end;

function ARGBToRGBArray(AColor: TColorRef): DWord;
asm
 .NOFRAME

 mov eax, AColor
 bswap eax
 ror eax, 8
end;

Или иным способом, который вам больше нравится.


dmk ©   (24.06.18 12:28[3]

>RowOrder определяется знаком числа:

Конечно же речь идет об этой строке:
biHeight := -FBitmap.dbHeight;

Если минус, то BitBlt рисует сверху вниз, если без минуса, то BitBlt рисует снизу вверх.


dmk ©   (24.06.18 12:30[4]

Можно сразу строку конвертировать если надо:
procedure BGRtoRGBScLine32(SrcAddr: UInt64; NumPixels: UInt32);
asm
 .NOFRAME

 mov r8, SrcAddr
 xor r9, r9
 mov r9d, NumPixels

@N:
 mov eax, [r8]
 shld edx, eax, 8 //сдвигаем альфу в edx
 bswap eax //разворот байтов BGR - > RGB
 shrd eax, edx, 8 //сдвигаем альфу обратно в eax
 mov [r8], eax
 add r8, 4 //смещение к следующему пикселу
 dec r9
 jnz @N
end;


Зодчий   (25.06.18 00:54[5]

Возможно, я не так написал: не "RGBA или XRGB", а - "XRGB или ARGB".
Вообще судя по MSDN для Windows вроде как базовыми считаются: RGB, XRGB, ARGB, RGBA. И как минимум у первых трёх - обратный порядок байт.

>> XRGB means leading zero, ARGB means leading alpha

XRGB это когда альфа-канал игнорируется - или он полностью обнулён, или принудительно считается что там мусор.
Изначально созданный мною битмап трактуется как XRGB. Как его создавать, чтоб он трактовался как ARGB?

>> biHeight := -FBitmap.dbHeight;

Я говорю про как узнать RowOrder у уже существующего битмапа, а не как создавать. :)
Мне дали неведомый HBITMAP. Надо узнать RowOrder и тип (XRGB или ARGB). Можно конечно обойти все пиксели и если вся альфа равна нулю можно предположить что XRGB, но это будет не факт.
Получать из HBITMAP значение biHeight через GetObject() и смотреть его знак - это я пробовал. В большинстве случаев работает. Но в некоторых случаях врёт и у меня из-за этого AV.
Может надо как-то иначе получать?


Зодчий   (25.06.18 00:56[6]

Кстати у вас в [1] мини-опечатка. Я за выходные кучу страниц MSDN перелопатил, и просто случайно запомнилось...
Там писали что как бы почему-то неправильно передавать в CreateDIBSection() результат CreateCompatibleDC(). Их первые параметры должны быть одинаковыми:

  specifiedDC := 0;
  dbMemDC := CreateCompatibleDC(specifiedDC);
  dbBitmap := CreateDIBSection(specifiedDC, ... );


И почему в CreateDIBSection такое странное < PBitmapInfo(@FInfo)^ >, а не просто FInfo? Вроде ж там обычный var-параметр...

Хотел изучить ваши функции, но Делфи не понимает ".NOFRAME", а Лазарус не понимает "asm"...


invis ©   (25.06.18 15:46[7]

По умолчанию в битмапах обратный порядок - BGRA, BGRX. Там есть какие-то маски, которыми можно задать произвольный порядок байт, но по-моему никто их не использует.
Чёткого способа отличить BGRA от BGRX нет. Можно исходить из того, что если уж передают 32 бита, а не 24, то альфа-канал там есть - ну и да, пройти по битмапу, проверить альфу на 0.


Зодчий   (25.06.18 22:55[8]

Почему вы написали наоборот? Просто так, для иллюстрации обратного порядка? А то я уже запутался в обозначениях.
На сайте MSDN чётко написано - тип: 32-bpp, BI_RGB; данный формат называется ARGB; в виде структуры: { Blue, Green, Red, Alpha: BYTE }; порядок байт таков: "Alpha~31:24, Red~23:16, Green~15:08, Blue~07:00".
А для XRGB другая структура - древняя tagRGBQUAD, там вместо Alpha идёт rgbReserved. Хотя по сути от структуры для ARGB оно отличается только названиями полей... Да и XRGB также характеризуются 32-bpp, BI_RGB.
И да, функции рисования в базовой GDI32 - RGB/XRGB only.

У этого XRGB в общем случае вроде как Альфа не "0", а "игнорируется". То есть может быть и "вся 0" и "вся 255" и даже вообще случайный мусор. Но это в общем случае...

Я планирую передавать дальше, каким-нибудь функциям/библиотекам/объектам - надо чтоб они могли правильно определить, какой тип я создал/хотел.
Можно подробнее про "маски"? Что-то не находил подобного...


invis ©   (26.06.18 01:11[9]

Возможно, у меня нетрадиционное обозначение, просто я чаще работаю с цветами через указатели на структуру вроде tagRGBQUAD, а там очевидное BGRX:
 tagRGBQUAD = packed record
   rgbBlue: Byte;
   rgbGreen: Byte;
   rgbRed: Byte;
   rgbReserved: Byte;
 end;
Но если тот же цвет прочитать в Integer/DWord, то там оказывается XXRRGGBB, потому как little-endian, в памяти Integer хранится задом наперёд.
https://ru.wikipedia.org/wiki/Порядок_байтов#Порядок_от_младшего_к_старшему
Отсюда видимо обозначения MSDN.

Про маски:
If the biCompression member of the BITMAPINFOHEADER is BI_BITFIELDS, the bmiColors member contains three DWORD color masks that specify the red, green, and blue components, respectively, of each pixel.
https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd183376(v=vs.85).aspx


dmk ©   (26.06.18 01:29[10]

>Кстати у вас в [1] мини-опечатка.
Это не опечатка. Так создается контекст совместимый с текущим режимом дисплея.


dmk ©   (26.06.18 01:44[11]

>Я говорю про как узнать RowOrder у уже существующего битмапа, а не как создавать. :)
RowOrder - порядок строк, а не порядок байтов в пикселе. Вы сами путаете.
Тут либо сверху вниз, либо наоборот.

А чтобы узнать порядок байтов в пикселе, надо в какой-либо структуре иметь идентификатор порядка компонентов пиксела или подбирать визуально. Иначе никак.

В формате TIFF например есть тэг отвечающий за порядок байтов в пикселе.
В BMP например все хранится в структуре:
type
 PRGBQuad = ^TRGBQuad;
 tagRGBQUAD = record
   rgbBlue: Byte;
   rgbGreen: Byte;
   rgbRed: Byte;
   rgbReserved: Byte;
 end;

При чтении файла приходится переворачивать байты

 procedure AdjustScanLine24(SrcAddr: QWord; NumPixels: dword; NumPlanes: dword);
 var
   i: integer;
   sA: QWord;

 begin
   //Адрес скан-линии
   sA := SrcAddr;

   for i := 0 to (NumPixels - 1) do
   begin
     BGRToRGB24(sA);
     Inc(sA, NumPlanes);
   end;
 end;


>Хотел изучить ваши функции,
>но Делфи не понимает ".NOFRAME", а Лазарус не понимает "asm"...
Надо Delphi Platform x64.
Лазарус прекрасно понимает asm64. Там надо после декларации процедуры указывать директиву assembler.

>.NOFRAME
Это только для Delphi 64.


Зодчий   (26.06.18 07:13[12]

Говорю, в CreateDIBSection() не надо передавать dbMemDC - заместо него надо тоже ноль.
гм... А зачем там дальше ReleaseDC()? Мы же не делали ни GetDC(), ни GetWindowDC().

> Вы сами путаете.

Я не путаю - мне надо выяснить оба вопроса. :)
Как-то же должно быть возможно узнать...

> переворачивать байты

Почему Inc() на NumPlanes? Разве Planes не всегда строго равен 1?


З.Ы. У вас тоже ссылки вида https://msdn.microsoft.com/en-us/library/windows/desktop/dd... теперь перекидывает на какую-то хрень вида https://docs.microsoft.com/ru-ru/windows/desktop/api/... ?


dmk ©   (26.06.18 12:16[13]

>Почему Inc() на NumPlanes?
У меня NumPlanes - это кол-во цветовых слоёв или байтов в пикселе.
Я вам просто привел пример. Вы вольны делать как вам угодно.

>Разве Planes не всегда строго равен 1?
Planes это не Слой (Layer) в моем случае.

RealeseDC это сброс счетчика или ссылки на контекст. Поскольку у Вас после
CreateDIBSection есть свой контекст, то ссылаться на структуру другого не нужно.
Это конечно же предположение. Внутренней структуры windows я не знаю.
Взял из книги Фень Юаня.

>Я не путаю - мне надо выяснить оба вопроса. :)
Один вопрос уже объяснил - вы никак не узнаете в каком порядке хранятся байты пока
не зададите специальные идентификаторы. Либо используйте форматы файлов,
где это указано явно. Вроде TIFF и т.п.

В DIBSection байты хранятся в формате ARGB. Уже писал выше.


Pavia ©   (26.06.18 17:55[14]

https://docs.microsoft.com/ru-ru/windows/desktop/DirectShow/uncompressed-rgb-video-subtypes


Зодчий   (29.06.18 01:19[15]

Любопытная ссылка, не находил такую прежде, спасибо. Но как она относится к моей теме?

dmk, я как бы же тоже не настаиваю, можете прислушиваться, можете не прислушиваться - как хотите, но я всё же напишу.
Полностью корректно вот так:
ZeroMemory(@FInfo, SizeOf(FInfo));
with FInfo, bmiHeader do
begin
  biSize := SizeOf(bmiHeader);
  biWidth := FBitmap.dbWidth;
  biHeight := -FBitmap.dbHeight;
  biPlanes := 1;
  biBitCount := FBitmap.dbBpp;
  biCompression := BI_RGB; // почему не FBitmap.dbCompression?
  // biSizeImage := 0; // а для BI_RGB указывать FBitmap.dbMemSize во-первых не обязательно (из документации)
  // а судя по тестам при BI_RGB это поле вовсе игнорируется, и применяется значение посчитанное ОС
end;
with FBitmap do
begin
  dbMemDC := CreateCompatibleDC(0);
  if (dbMemDC=0) then raise EOSError.Create('CreateCompatibleDC has failed.');
  SetLastError(ERROR_INVALID_DATA); // CreateDIBSection почему-то не во всех ситуациях устанавливает код
  dbBitmap := CreateDIBSection(0, FInfo, DIB_RGB_COLORS, dbMemPtr, 0, 0);
  if (dbBitmap=0) then raise EOSError.Create('CreateDIBSection has failed:'#13#10+SysErrorMessage(GetLastError()));
  DeleteObject(SelectObject(dbMemDC, dbBitmap));
end;


Накопал я тут кучу тем на форумах по моей теме, от 2003 до 2017, но все без ответов...


invis ©   (29.06.18 03:12[16]

А какой ответ нужен? Как отличить XRGB от ARGB? Я уже писал, что никак. Во всяком случае, через GDI создаётся просто 32-битный битмап, и как использовать 4-й байт - остаётся на совести разработчика.
В GDI+ может быть есть разница.


Pavia ©   (30.06.18 13:32[17]

Когда вы создаете DIB с ипользованием
biBitCount=32
biCompression := BI_RGB;

Вы получаете формат RGBA.

А теперь о подводных камнях.
1)
Байты в памяти рисуются с лева на права от младших к старшему.
Байты в регистрах и переменных рисуются с права на лево от старших разрядов к младшим.
Те рисуются на диаграммах и пояснительных рисунках.

Соответственно это надо учитывать и не путаться.
2) canvas.pixels[x,y] - получает переменные в формате TColor
TColor имеет свой формат с инвертированным значением бит и др особенностями.

3) RGBA и RGBX.  API GDI не различает эти форматы.
В основном он трактует формат как RGBX. При использование BitBlt четвертая компонента игнорируется. Что-бы она использовалась как альфа надо вызывать AlphaBlend() см
https://docs.microsoft.com/ru-ru/windows/desktop/gdi/alpha-blending-a-bitmap

А если хотите рисовать, то если будете использовать GDI то четвёртая компонента битмепа будет обнуляться. Что-бы не обнулялась надо использовать GDI+.


dmk ©   (30.06.18 17:48[18]

>если будете использовать GDI то четвёртая компонента битмепа будет обнуляться.
Неверно. Ничего не обнуляется.
Вот рисуется через GDI: https://yadi.sk/i/EDIiJHfL3VNJAY
Windows вообще буфер не «трогает».


Зодчий   (04.07.18 06:36[19]

Искал фик знает сколько, тестил, проверял... В общем походу вы правы.
Вырисовывается что Microsoft решили доработать GDI32, что-то даже начали делать, но на полпути свернули работу и забросили. Возможно переключились на создание GDI+, решив что старое латать накладно...

По задумке порядок строк походу должен был получаться через GetObject() вот отсюда: tagDIBSECTION.dsBmih.b*Height, но почему-то возвращается не настоящее значение, а абсолютное (модуль).
Что не имеет смысла, так как данное поле описано как раз именно как знаковое и показывающее направление строк, а абсолютное значение высоты и так уже отдельно имеется в tagDIBSECTION.dsBm.bmHeight - и зачем мне два раза модуль?

А отличать XRGB или ARGB по задумке можно было бы через tagDIBSECTION.dsBmih.b*AlphaMask (и возможно даже порядок компонентов через *RedMask и прочие), но GetObject() просто отказывается заполнять структуру tagDIBSECTION.dsBmih нужной для этого версии заголовка.

Видимо ответ на оба моих вопроса - никак, потому что данный функционал в GDI32 хоть и в теории есть, но по факту просто не работает...

З.Ы. Полезная ссылка: http://everything.explained.today/BMP_file_format/


Страницы: 1 2 версия для печати

Написать ответ

Ваше имя (регистрация  E-mail 







Разрешается использование тегов форматирования текста:
<b>жирный</b> <i>наклонный</i> <u>подчеркнутый</u>,
а для выделения текста программ, используйте <code> ... </code>
и не забывайте закрывать теги! </b></i></u></code> :)


Наверх

  Рейтинг@Mail.ru     Титульная страница Поиск, карта сайта Написать письмо