-
Я создал 32-bit'ный битмап. Судя по некоторым симптомам он получился так называемым XRGB. А я-то хотел RGBA. Что вообще их отличает? Что-то указывается в BITMAPINFOHEADER? Или где?
Или вот допустим мне передали уже DIB-секцию - как получить информацию: она XRGB или RGBA? И как самому явно задавать нужный мне тип (при создании HBITMAP)?
А так же - как правильно определить row order?
Спасибо!
-
В 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;
-
Переворот байтов можно делать так:
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;
Или иным способом, который вам больше нравится.
-
>RowOrder определяется знаком числа:
Конечно же речь идет об этой строке:
biHeight := -FBitmap.dbHeight;
Если минус, то BitBlt рисует сверху вниз, если без минуса, то BitBlt рисует снизу вверх.
-
Можно сразу строку конвертировать если надо:
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;
-
Возможно, я не так написал: не "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. Может надо как-то иначе получать?
-
Кстати у вас в [1] мини-опечатка. Я за выходные кучу страниц MSDN перелопатил, и просто случайно запомнилось... Там писали что как бы почему-то неправильно передавать в CreateDIBSection() результат CreateCompatibleDC(). Их первые параметры должны быть одинаковыми:
specifiedDC := 0; dbMemDC := CreateCompatibleDC(specifiedDC); dbBitmap := CreateDIBSection(specifiedDC, ... );
И почему в CreateDIBSection такое странное < PBitmapInfo(@FInfo)^ >, а не просто FInfo? Вроде ж там обычный var-параметр...
Хотел изучить ваши функции, но Делфи не понимает ".NOFRAME", а Лазарус не понимает "asm"...
-
По умолчанию в битмапах обратный порядок - BGRA, BGRX. Там есть какие-то маски, которыми можно задать произвольный порядок байт, но по-моему никто их не использует. Чёткого способа отличить BGRA от BGRX нет. Можно исходить из того, что если уж передают 32 бита, а не 24, то альфа-канал там есть - ну и да, пройти по битмапу, проверить альфу на 0.
-
Почему вы написали наоборот? Просто так, для иллюстрации обратного порядка? А то я уже запутался в обозначениях. На сайте 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" и даже вообще случайный мусор. Но это в общем случае...
Я планирую передавать дальше, каким-нибудь функциям/библиотекам/объектам - надо чтоб они могли правильно определить, какой тип я создал/хотел. Можно подробнее про "маски"? Что-то не находил подобного...
-
Возможно, у меня нетрадиционное обозначение, просто я чаще работаю с цветами через указатели на структуру вроде 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
-
>Кстати у вас в [1] мини-опечатка. Это не опечатка. Так создается контекст совместимый с текущим режимом дисплея.
-
>Я говорю про как узнать 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.
-
-
>Почему Inc() на NumPlanes? У меня NumPlanes - это кол-во цветовых слоёв или байтов в пикселе. Я вам просто привел пример. Вы вольны делать как вам угодно.
>Разве Planes не всегда строго равен 1? Planes это не Слой (Layer) в моем случае.
RealeseDC это сброс счетчика или ссылки на контекст. Поскольку у Вас после CreateDIBSection есть свой контекст, то ссылаться на структуру другого не нужно. Это конечно же предположение. Внутренней структуры windows я не знаю. Взял из книги Фень Юаня.
>Я не путаю - мне надо выяснить оба вопроса. :) Один вопрос уже объяснил - вы никак не узнаете в каком порядке хранятся байты пока не зададите специальные идентификаторы. Либо используйте форматы файлов, где это указано явно. Вроде TIFF и т.п.
В DIBSection байты хранятся в формате ARGB. Уже писал выше.
-
-
Любопытная ссылка, не находил такую прежде, спасибо. Но как она относится к моей теме?
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, но все без ответов...
-
А какой ответ нужен? Как отличить XRGB от ARGB? Я уже писал, что никак. Во всяком случае, через GDI создаётся просто 32-битный битмап, и как использовать 4-й байт - остаётся на совести разработчика. В GDI+ может быть есть разница.
-
Когда вы создаете 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+.
-
>если будете использовать GDI то четвёртая компонента битмепа будет обнуляться. Неверно. Ничего не обнуляется. Вот рисуется через GDI: https://yadi.sk/i/EDIiJHfL3VNJAYWindows вообще буфер не «трогает».
-
Искал фик знает сколько, тестил, проверял... В общем походу вы правы. Вырисовывается что 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/
|