Конференция "Media" » Как у 32bit'ных битмапов указывать/указывается RGBA они или XRGB? [D7, WinXP]
 
  • Зодчий (24.06.18 11:44) [0]
    Я создал 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]
  • Зодчий (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/
  • Зодчий (04.07.18 06:44) [20]
    Описание GetObject():
    * Если функция завершается с ошибкой, величина возвращаемого значения - ноль.
    * Чтобы получить дополнительные сведения об ошибке, вызовите GetLastError.

    Но по факту эта гадина LastError не устанавливает вообще!
    А я блин долго гадал какой-такой "Не удается найти указанный файл" ищет GetObject()..!
    Дошло что происходит только когда словил от GetObject() ошибку "Указанный тег не найден".

    Но стало довольно любопытно, когда выяснилось что "Не удается найти указанный файл" и "Указанный тег не найден" ставил CreateDIBSection(), да ещё и при успехе... В зависимости от заполнения tagBITMAPINFO.
    Что-то оно там пытается cделать, но оно явно не доделано и брошено. Да и сама ОС как-то же понимает хотя бы порядок строк? Допустим альфу исторически игнорирует, но порядок строк-то она не путает! Значит где-то же хранит?
  • invis © (04.07.18 21:30) [21]
    Попробуй GetDIBits с указателем на bits=nil и смотри структуру BITMAPINFO.

    Вообще с чистым GDI работать действительно муторно. Проще использовать класс-контейнер для битмапа, а если нужно передать в DLL, можно слепить простенькую структуру вроде [указатель, высота, ширина, глубина].
  • K-1000 © (16.07.18 20:50) [22]
  • имя (04.09.18 13:15) [23]
    Удалено модератором
  • Сапёр (11.01.19 08:17) [24]
    Это только проверять вручную. Задать пиксель через API и анализировать где изменилось в памяти.
 
Конференция "Media" » Как у 32bit'ных битмапов указывать/указывается RGBA они или XRGB? [D7, WinXP]
Есть новые Нет новых   [118609   +48][b:0][p:0.001]