Конференция "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/
 
Конференция "Media" » Как у 32bit'ных битмапов указывать/указывается RGBA они или XRGB? [D7, WinXP]
Есть новые Нет новых   [118480   +43][b:0][p:0.001]