Microsoft DirectX 9.0

GetVideoInfoParameters Helper Function

If you are decoding a video image, or modifying a video image that is already decoded, you must take into account both the stride and the orientation of the image. This section presents a helper function that makes it easier to loop over the pixels in a bitmap in a consistent way.

The function is declared as follows:

void GetVideoInfoParameters(
    const VIDEOINFOHEADER *pvih, // Pointer to the VIDEOINFOHEADER
    BYTE * const pbData,         // Pointer to the start of the buffer.
    bool bYuv,                   // true if YUV format 
    DWORD *pdwWidth,             // Receives the image width, in pixels
    DWORD *pdwHeight,            // Receives the image height, in pixels
    LONG *plStrideInBytes,       // Receives the stride, in bytes
    BYTE **ppbTop                // Receives a pointer to the first image row.
);

On input, this function takes three pieces of information: A pointer to a VIDEOINFOHEADER structure that describes the bitmap (pvih), the address of the buffer that holds the bitmap data (pbData), and a Boolean flag that indicates whether the bitmap is a YUV or an RGB format (bYuv).

On output, the function returns the following information:

The value returned in ppbTop is always the upper-left corner of the image as it will be displayed on the screen. In a top-down DIB, this is the first byte in memory, but in a bottom-up DIB, the top row appears last in memory.

The value returned in plStrideinBytes is the byte offset from the start of one row to the start of the next row down. Thus, the address of the second row from the top is pbTop + lStrideInBytes.

The following code shows how to use this function on a 32-bit RGB DIB:

HRESULT WriteToBuffer_RGB32(BYTE *pData, VIDEOINFOHEADER *pVih)
{
    DWORD dwWidth, dwHeight;
    long lStride;
    BYTE *pbTop;
    GetVideoInfoParameters(pVih, pData, &dwWidth, 
        &dwHeight, &lStride, &pbTop, false);

    // Loop from the top row of pixels to the bottom row.
    for (DWORD y = 0; y < dwHeight; y++)
    {
        // Loop across each pixel in the row, from left to right.
        RGBQUAD *pPixel = (RGBQUAD*)pbRow;
        for (DWORD x = 0; x < dwWidth; x++)
        {
            // pPixel[x] is the x'th pixel in the row.
            pPixel[x].rgbBlue = blue value;
            pPixel[x].rgbGreen = green value;
            pPixel[x].rgbRed = red value;
            pPixel[x].rgbReserved = 0;
        }
        // Advance pbTop by the stride.
        pbTop += lStride;
    }
}

The outer loop moves from the top row to the bottom row. The inner loop moves across each row from left to right. For a 32-bit RGB bitmap, each pixel should be addressed as an RGBQUAD value. (Other formats use other byte layouts.)

The following code shows the complete GetVideoInfoParameters function:

void GetVideoInfoParameters(
    const VIDEOINFOHEADER *pvih, // Pointer to the format header.
    BYTE  * const pbData,   // Pointer to the first address in the buffer.
    bool bYuv      // Is this a YUV format? (true = YUV, false = RGB)
    DWORD *pdwWidth,        // Returns the width in pixels.
    DWORD *pdwHeight,       // Returns the height in pixels.
    LONG  *plStrideInBytes, // Add this to a row to get the new row down.
    BYTE **ppbTop,          // Returns a pointer to the first byte in the 
                            // top row of pixels.
    )
{
    LONG lStride;

    //  For 'normal' formats, biWidth is in pixels. 
    //  Expand to bytes and round up to a multiple of 4.
    if ((pvih->bmiHeader.biBitCount != 0) &&
        (0 == (7 & pvih->bmiHeader.biBitCount))) 
    {
        lStride = (pvih->bmiHeader.biWidth * (pvih->bmiHeader.biBitCount / 8) + 3) & ~3;
    } 
    else   // Otherwise, biWidth is in bytes.
    {
        lStride = pvih->bmiHeader.biWidth;
    }

    //  If rcTarget is empty, use the whole image.
    if (IsRectEmpty(&pvih->rcTarget)) 
    {
        *pdwWidth = (DWORD)pvih->bmiHeader.biWidth;
        *pdwHeight = (DWORD)(abs(pvih->bmiHeader.biHeight));
        
        if (pvih->bmiHeader.biHeight < 0 || bYuv)   // Top-down bitmap. 
        {
            *plStrideInBytes = lStride; // Stride goes "down".
            *ppbTop           = pbData; // Top row is first.
        } 
        else        // Bottom-up bitmap.
        {
            *plStrideInBytes = -lStride;    // Stride goes "up".
            // Bottom row is first.
            *ppbTop = pbData + lStride * (*pdwHeight - 1);  
        }
    } 
    else   // rcTarget is NOT empty. Use a sub-rectangle in the image.
    {
        *pdwWidth = (DWORD)(pvih->rcTarget.right - pvih->rcTarget.left);
        *pdwHeight = (DWORD)(pvih->rcTarget.bottom - pvih->rcTarget.top);
        
        if (pvih->bmiHeader.biHeight < 0 || bYuv)   // Top-down bitmap.
        {
            // Same stride as above, but first pixel is modified down
            // and over by the target rectangle.
            *plStrideInBytes = lStride;     
            *ppbTop = pbData +
                     lStride * pvih->rcTarget.top +
                     (pvih->bmiHeader.biBitCount * pvih->rcTarget.left) / 8;
        } 
        else  // Bottom-up bitmap.
        {
            *plStrideInBytes = -lStride;
            *ppbTop = pbData +
                     lStride * (pvih->bmiHeader.biHeight - pvih->rcTarget.top - 1) +
                     (pvih->bmiHeader.biBitCount * pvih->rcTarget.left) / 8;
        }
    }
}

See Also