Home > C++, Software Development > Hardware Accelerated Image Resizing with GDI

Hardware Accelerated Image Resizing with GDI

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

  1. Maurice
    October 13th, 2009 at 10:56 | #1

    Looks Great ! Thanks Christian !
    I have tried some resize with GDI+ that didn’t convince me in performance, and would tried GDI instead, and your code seems to fit perfectly. But i’m little novice with GDI, and don’t know how to implement this code, for example :
    Resize ImageOriginal.jpg ImageNewSize.jpg

    Do you have an example please ? Thanks a lot if you could !!

    void Resize( PBYTE pSrc, DWORD dwSrcWidth, DWORD dwSrcHeight, PBYTE pDst, DWORD dwDstWidth, DWORD dwDstHeight);

    // Maurice

  2. Maurice
    October 13th, 2009 at 11:06 | #2

    I can’t make the LoadBitmap function working correctly either :(

  3. October 14th, 2009 at 14:54 | #3

    Maurice,
    the code above is only for resizing image data in an RGB buffer. It does not decode any compressed data and also does not compress the resulting RGB data.
    There is a chance that you might be able to do the JPG decompression throught GDI, but that would only work if your graphics driver supports it. For this purpose, use BI_JPEG in the BITMAPINFOHEADER structure. MSDN says:
    “If the driver cannot support the JPEG or PNG file image passed to StretchDIBits, the function will fail and return GDI_ERROR. If failure does occur, the application must fall back on its own JPEG or PNG support to decompress the image into a bitmap, and then pass the bitmap to StretchDIBits.”
    Even if you can decode JPG files using StretchDIBits(), you are out of luck compressing them without a JPEG library. I would recommend the IJG 7 library for that purpose (and if you need more performance: http://cetus.sakura.ne.jp/softlab/jpeg-x86simd/jpegsimd.html)

  4. Maurice
    November 4th, 2009 at 09:58 | #4

    Hi Christian,
    Thank you very much for your answer. The library you have proposed was an amazing solution for performance! I have tried to figure out the japanese stuff with the Google translator, ;) then i realize what this library was done for : simply amazing!

    Talking about performance i was against a wall when i tried to figured out how to accelerate the picture loading from the Disk. No way i know. But that’s dreaming…
    Thanks again for your great professional answer that saved me a lot of time.

    Bye,
    Maurice

  1. No trackbacks yet.


one × = six