syghの新フラグメント置き場

プログラミングTipsやコード断片の保管場所です。お絵描きもときどき載せます。

BGRA/RGBAに関してそろそろ一言いっておくか

世には4つのカラーチャンネル(Red, Green, Blue, Alpha)をサポートする画像フォーマットは数多あるのですが、整理も兼ねて Direct3DOpenGL のテクスチャ フォーマットとの関連に言及しながらまとめてみようと思います。

早速ですがいきなりサンプルコードです。

#include <cstdio>
#include <conio.h>
#include <d3d9.h>
#include <gdiplus.h>

struct Byte4
{
  BYTE b[4];
};

int main()
{
  const D3DCOLOR color1 = D3DCOLOR_ARGB(4, 3, 2, 1); // ARGB 順で指定。
  const COLORREF color2 = RGB(3, 2, 1); // RGB 順で指定。
  const RGBQUAD color3 = { 1, 2, 3, 4 }; // BGRX 順で指定。
  const Gdiplus::Color color4(4, 3, 2, 1); // ARGB 順で指定。

  const Byte4 temp[] =
  {
    *reinterpret_cast<const Byte4*>(&color1),
    *reinterpret_cast<const Byte4*>(&color2),
    *reinterpret_cast<const Byte4*>(&color3),
    *reinterpret_cast<const Byte4*>(&color4), // Color::Argb フィールドは protected になっている。
  };

  for (int i = 0; i < ARRAYSIZE(temp); ++i)
  {
    printf("%d, %d, %d, %d\n", temp[i].b[0], temp[i].b[1], temp[i].b[2], temp[i].b[3]);
  }

  puts("Press any...");
  _getch();

  return 0;
}

とりあえず Windows 環境前提で申し訳ないのですが、結果がどうなるかコンパイル&実行せずに予測できる人はこんな三文記事を読む必要はありません。すでに Windows グラフィックス アプリ開発に必要な知識を備えています。

実行結果

1, 2, 3, 4
3, 2, 1, 0
1, 2, 3, 4
1, 2, 3, 4

C/C++ で GDI/GDI+ アプリケーションを開発したことがある人は、COLORREF 型、RGBQUAD 型、Gdiplus::Color 型にはなじみ深いと思います。また、Direct3D 9 を使った 3D グラフィックス アプリケーションを開発したことがある人は、D3DCOLOR 型もよく知っていることと思います。これらの型は 32bit 整数型の範囲でピクセルのカラー値(各チャンネル8bit)を指定するときに非常によく使われます。
実は COLORREF を除いて、これらのメモリー上のビット表現はすべて同じ BGRA/BGRX 順になります。COLORREF のみ RGBX 順になります。

Windows GDI と Direct3DOpenGL、そして Direct2D

Windows GDI の 32bit ネイティブ ピクセル フォーマットは RGBQUAD 構造体と同様で、BGRX の順に並びます。
32bit の Windows ビットマップ ファイルも同様。
OpenCV の IplImage は 24bit カラーの場合 BGR 順になります。
Direct3D テクスチャおよび DXGI サーフェイスは RGBA および BGRA の両方をサポートしますが、D3D9 の場合バックバッファ フォーマットは BGRA/BGRX のみです。

  • D3D9 の D3DFMT_A8R8G8B8 および DXGI 1.1 の DXGI_FORMAT_B8G8R8A8_UNORM

→ GDI 互換の BGRA。

  • D3D9 の D3DFMT_A8B8G8R8 および DXGI 1.0 の DXGI_FORMAT_R8G8B8A8_UNORM

GPU ネイティブ 8bit テクスチャ RGBA。

識別子名のチャンネルの並びが D3D9 と DXGI(D3D10/11)とで異なっているので注意。どちらかというと古い D3D9 のほうがまぎらわしいです(リトルエンディアン基準)。
実は自分は Direct3D 10.x + DXGI 1.0 はほとんど使ってなくて、本格的に使い始めたのは Direct3D 10.1/11.0 + DXGI 1.1 以降(主に Direct2D 1.0 連携用途)だったので、Vista 用の Direct3D 10.x 時代の DXGI 1.0 では当初 BGRA が逆にサポートされておらず、GPU ネイティブの RGBA のみだったのは知りませんでした。ちなみに Direct3D 10 以降では D3DCOLOR 型はもはや存在しません。

OpenGL は GL_RGBA がネイティブ フォーマットです。GL_BGRA は拡張扱いで、最近のデスクトップ GPU だと普通に使えると思うんですが、OpenGL ES では未サポートらしいです。
TIFF は RGBA の順らしいです。PNG も RGBA らしいです。
なお、WGL を使って GDI バックバッファに描画するときは、ピクセルフォーマットに RGBA を指定していても、最終的には BGRA に変換されるものと思われます。

Direct3D / OpenGL どちらの API を使うにせよ、画像ファイルからピクセルデータを取り出してテクスチャを作成する場合、RGBA/BGRA には注意を払っておく必要があります。WIC のようにチャンネルの並び順や深度をコンバートしてくれる機能を持つライブラリを使うと扱いが楽になります。

また、Windows Vista SP2 + Platform Update / Windows 7 以降の OS では、Direct3D を使って作られた高レベル グラフィックス API である Direct2D が使えるんですが、Direct2D で GDI 相互運用をする場合、D2D レンダーターゲットのフォーマットは BGRA のみサポートされます。
Direct2D で DXGI 相互運用をする場合、BGRA と RGBA の両方がサポートされます。

参考:
・D3DFORMAT
http://msdn.microsoft.com/ja-jp/library/ee416489.aspx
・DXGI_FORMAT
http://msdn.microsoft.com/ja-jp/library/ee418116.aspx
Direct3D 9 から Direct3D 10 への移行時の考慮事項 (Direct3D 10)
http://msdn.microsoft.com/ja-jp/library/bb205073.aspx#Porting_Content
・Legacy Formats: Map Direct3D 9 Formats to Direct3D 10
http://msdn.microsoft.com/en-us/library/windows/desktop/cc308051.aspx
・サポートされているピクセル形式とアルファ モード
http://msdn.microsoft.com/ja-jp/library/windows/desktop/dd756766.aspx