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) である。

出力では、この関数は次の情報を返す。

  • ピクセル単位のイメージの幅と高さ (pdwWidthpdwHeight)。
  • バイト単位のストライド (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;
        }
    }
}

参照