Archive

Posts Tagged ‘Hardware Acceleration’

Hardware Accelerated Image Resizing with GDI

August 31st, 2009 4 comments

I recently had a situation where we needed a very fast RGB image resizing algorithm that would also come with a low foot print. After looking into Intel® Integrated Performance Primitives (which is not free any more) there was the idea to use Win32 GDI functions directly.

Basically what we do here is create a memory device contexts based on the current display DC, then associate it with the destination memory bitmap. This enables us to use the fast StretchDIBits() API for doing arbitrary image scaling. Then we copy the RGB array from the destination bitmap structure into the destination buffer and delete the destination bitmap.

void Resize( PBYTE pSrc, DWORD dwSrcWidth, DWORD dwSrcHeight, PBYTE pDst, DWORD dwDstWidth, DWORD dwDstHeight )
{
    HDC hDC = GetDC( NULL );
    HDC hDstDC = CreateCompatibleDC( hDC );
 
    BITMAPINFO bi_dst;
    ZeroMemory( &bi_dst, sizeof( bi_dst ) );
    bi_dst.bmiHeader.biSize = sizeof( bi_dst.bmiHeader );
    bi_dst.bmiHeader.biWidth = dwDstWidth;
    bi_dst.bmiHeader.biHeight = dwDstHeight;
    bi_dst.bmiHeader.biPlanes = 1;
    bi_dst.bmiHeader.biBitCount = 24;
    bi_dst.bmiHeader.biCompression = BI_RGB;
 
    PVOID bits;
    HBITMAP hDstBm = CreateDIBSection( NULL, &bi_dst, DIB_RGB_COLORS, &bits, NULL, 0 );
    SelectObject( hDstDC, hDstBm );
 
    BITMAPINFO bi_src;
    ZeroMemory( &bi_src, sizeof( bi_src ) );
    bi_src.bmiHeader.biSize = sizeof( bi_src.bmiHeader );
    bi_src.bmiHeader.biWidth = dwSrcWidth;
    bi_src.bmiHeader.biHeight = dwSrcHeight;
    bi_src.bmiHeader.biPlanes = 1;
    bi_src.bmiHeader.biBitCount = 24;
    bi_src.bmiHeader.biCompression = BI_RGB;    
 
    SetStretchBltMode( hDstDC, HALFTONE );
    StretchDIBits( hDstDC, 0, 0, dwDstWidth, dwDstHeight, 0, 0, dwSrcWidth, dwSrcHeight, pSrc, &bi_src, DIB_RGB_COLORS, SRCCOPY );
 
    DWORD dwDstStride = ( ( dwDstWidth * 3 ) + 3 ) & ~3 );
    CopyMemory( pDst, bits, dwDstStride * dwDstHeight );
 
    DeleteObject( hDstBm );
    DeleteDC( hDstDC );
    ReleaseDC( NULL, hDC );
}

For creating a device independent bitmap, we use the CreateDIBSection() API. This will allocate an RGB (or better BGR) buffer which will be automatically freed when calling DeleteObject().

This code cannot replace any image processing library. Yet it is a working solution that simply resizes data in an RGB buffer. Depending on your graphics driver, there might be some restrictions regarding maximum image size to resize from/to and if your display DC supports less than 24 bit colors. On my integrated graphics chipset (Radeon Express) I am getting good results however. Images of all sizes can be enlarged and reduced, the only apparent limit being the amount of RAM needed to hold the data.

If the graphics driver supports hardware acceleration, there is a remarkable speedup when resizing. Evidence suggests that 2D hardware acceleration is only available up to Windows XP and most likely Windows 7, which would be just another reason for not getting Windows Vista ;-).

There are several free and open source libraries available for resizing images. Although using them in your own code might be quite simple, many of them will come with a bigger memory and code footprint than necessary, especially when you only want to do some resizing or simple drawing. When developing commercial software, one would also have to think about licensing issues.

Update: Thanks to a careful reader a bug was discovered in the source which would only work well on images where the width was divisible by four without remainder. It was fixed now by calculating a so-called stride length before copying RGB data to the destination buffer.

See also: UI rendering in Windows 7