GetVideoInfoParameters ヘルパー関数
ビデオ イメージをデコードする場合や、既にデコードされたビデオ イメージを変更する場合は、ストライドとイメージの方向の両方を考慮する必要がある。ここでは、ビットマップ内のピクセルを、一定の方法で容易にループ処理できるようにするヘルパー関数について説明する。
この関数は次のように宣言される。
void GetVideoInfoParameters(
const VIDEOINFOHEADER *pvih, // VIDEOINFOHEADER へのポインタ
BYTE * const pbData, // バッファの先頭へのポインタ。
bool bYuv, // YUV フォーマットの場合は true
DWORD *pdwWidth, // イメージの幅 (ピクセル単位) を受け取る
DWORD *pdwHeight, // イメージの高さ (ピクセル単位) を受け取る
LONG *plStrideInBytes, // ストライド (バイト単位) を受け取る
BYTE **ppbTop // イメージの最初の行へのポインタを受け取る。
);
入力では、この関数は 3 つの情報を取る。ビットマップを表す VIDEOINFOHEADER 構造体へのポインタ (pvih)、ビットマップ データを保持するバッファのアドレス (pbData)、およびビットマップが YUV フォーマットであるか、RGB フォーマットであるかを示すブール型フラグ (bYuv) である。
出力では、この関数は次の情報を返す。
- ピクセル単位のイメージの幅と高さ (pdwWidth と pdwHeight)。
- バイト単位のストライド (plStrideInBytes)。
- ピクセルの最上位行の最初のバイトへのポインタ (ppbTop)。
ppbTop で返される値は、常に、画面に表示される際のイメージの左上隅である。トップダウン DIB では、これはメモリの最初のバイトであるが、ボトムアップ DIB では、最上位行はメモリ内の最後に配置される。
plStrideinBytes で返される値は、ある行の先頭から 1 つ下の行の先頭までのバイト オフセットである。したがって、上から 2 番目の行のアドレスは pbTop + lStrideInBytes である。
次のコードは、32 ビット RGB DIB でのこの関数の使い方を示している。
HRESULT WriteToBuffer_RGB32(BYTE *pData, VIDEOINFOHEADER *pVih)
{
DWORD dwWidth, dwHeight;
long lStride;
BYTE *pbTop;
GetVideoInfoParameters(pVih, pData, &dwWidth,
&dwHeight, &lStride, &pbTop, false);
// ピクセルの最上位行から最下位行までループする。
for (DWORD y = 0; y < dwHeight; y++)
{
// 行内の各ピクセルを左から右にループする。
RGBQUAD *pPixel = (RGBQUAD*)pbRow;
for (DWORD x = 0; x < dwWidth; x++)
{
// pPixel[x] は行内の x 番目のピクセルである。
pPixel[x].rgbBlue = blue value;
pPixel[x].rgbGreen = green value;
pPixel[x].rgbRed = red value;
pPixel[x].rgbReserved = 0;
}
// pbTop をストライド単位で進める。
pbTop += lStride;
}
}
外側のループは、最上位行から最下位行まで移動する。内側のループは、各行の左から右に移動する。32 ビット RGB ビットマップの場合、各ピクセルは RGBQUAD 値としてアドレス指定される。その他のフォーマットは他のバイト レイアウトを使う。
次のコードは、完全な GetVideoInfoParameters 関数を示している。
void GetVideoInfoParameters(
const VIDEOINFOHEADER *pvih, // フォーマット ヘッダーへのポインタ。
BYTE * const pbData, // バッファ内の最初のアドレスへのポインタ。
bool bYuv // これは YUV フォーマットか? (true = YUV, false = RGB)
DWORD *pdwWidth, // 幅 (ピクセル単位) を返す。
DWORD *pdwHeight, // 高さ (ピクセル単位) を返す。
LONG *plStrideInBytes, // 新しい行を下げるために行にこれを追加する。
BYTE **ppbTop, // ピクセルの最上位行の最初のバイトへのポインタを
// 返す。
)
{
LONG lStride;
// '標準' のフォーマットの場合、biWidth はピクセル単位である。
// バイトに拡張し、4 の倍数に切り上げる。
if ((pvih->bmiHeader.biBitCount != 0) &&
(0 == (7 & pvih->bmiHeader.biBitCount)))
{
lStride = (pvih->bmiHeader.biWidth * (pvih->bmiHeader.biBitCount / 8) + 3) & ~3;
}
else // それ以外の場合は、biWidth はバイト単位である。
{
lStride = pvih->bmiHeader.biWidth;
}
// rcTarget が空である場合は、イメージ全体を使用する。
if (IsRectEmpty(&pvih->rcTarget))
{
*pdwWidth = (DWORD)pvih->bmiHeader.biWidth;
*pdwHeight = (DWORD)(abs(pvih->bmiHeader.biHeight));
if (pvih->bmiHeader.biHeight < 0 || bYuv) // トップダウン ビットマップ。
{
*plStrideInBytes = lStride; // ストライドは "下" へ向かう。
*ppbTop = pbData; // 最上位行が最初である。
}
else // ボトムアップ ビットマップ。
{
*plStrideInBytes = -lStride; // ストライドは "上" へ向かう。
// 最下位行が最初である。
*ppbTop = pbData + lStride * (*pdwHeight - 1);
}
}
else // rcTarget は空ではない。 イメージ内のサブ矩形を使用する。
{
*pdwWidth = (DWORD)(pvih->rcTarget.right - pvih->rcTarget.left);
*pdwHeight = (DWORD)(pvih->rcTarget.bottom - pvih->rcTarget.top);
if (pvih->bmiHeader.biHeight < 0 || bYuv) // トップダウン ビットマップ。
{
// 上と同じストライドだが、最初のピクセルがターゲット矩形によって、
// 次のように変更されている。
*plStrideInBytes = lStride;
*ppbTop = pbData +
lStride * pvih->rcTarget.top +
(pvih->bmiHeader.biBitCount * pvih->rcTarget.left) / 8;
}
else // ボトムアップ ビットマップ。
{
*plStrideInBytes = -lStride;
*ppbTop = pbData +
lStride * (pvih->bmiHeader.biHeight - pvih->rcTarget.top - 1) +
(pvih->bmiHeader.biBitCount * pvih->rcTarget.left) / 8;
}
}
}
参照