Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Plain C Resampling DLL

0.00/5 (No votes)
19 Dec 2007 11  
A small DLL providing two functions to resample GDI-based bitmap
Screenshot -

Introduction

When I first saw Libor Tinka's great article on resampling Image Resizing - Outperform GDI+, I said to myself: "very good stuff indeed, I really need a similar tool for GDI based bitmaps."

The Library

Resample.dll is a small dynamic library that exports two resampling functions. The design aims to resemble the Win32 GDI API interface and behaviour.

The function scenario is simple: Suppose we have a GDI HBITMAP (for instance loaded from a file via LoadImage), say hBmp, whose size is w x h and we need to resize it to wNew x hNew without a big loss in image quality. We can accomplish the above task with just a call to the library provided function CreateResampledBitmap in the following way:

hBmpNew = CreateResampledBitmap(hdc, hBmp, wNew, hNew, STOCK_FILTER_LANCZOS8);

Note that (as with, for instance, GDI CreateCompatibleBitmap), the obtained handle must be deleted (via GDI DeleteObject) when it is no longer needed. The other exported function is CreateUserFilterResampledBitmap that allows the caller to provide a custom filter function pointer.

Acknowledgements

This article is based on Libor Tinka's one: Image Resizing - Outperform GDI+. I just ported his C# code to a pure C one (changing the algorithm a bit) and packaged it all inside a DLL. All stock filters are the ones used in the original article by Libor, that ultimately can be used as reference.

Background

A general understanding of Win32 GDI API is required to use the code. A slightly deeper understanding of GDI bitmaps and C language is needed in order to hack the library internals. To use custom filters, a familiarity with callback functions may help.

Sample resampling image, CP
Image downsampling example.


Sample resampling image, CP
The same image upsampled.

Library Reference

Since the library contains only two functions, I can give reference information in the classical GDI documentation style.

Resampling With Stock Filters

CreateResampledBitmap

The CreateResampledBitmap function creates a resampled bitmap compatible with the device that is associated with the specified device context. The resampling filter is chosen between available stock filters.

HBITMAP CreateResampledBitmap(
   HDC hdc,             // handle to DC
   HBITMAP hBmpSource,  // handle to original bitmap
   DWORD dwWidth,       // width of the resampled bitmap, in pixels
   DWORD dwHeight,      // height of the resampled bitmap, in pixels
   DWORD dwFilter       // index of the stock resampling filter used
);

Parameters

    hdc
        [in] Handle to a device context
    hBmpSource
        [in] Handle to the original bitmap
    dwWidth
        [in] Specifies the resampled bitmap width, in pixels
    dwHeight
        [in] Specifies the resampled bitmap height, in pixels
    dwFilter
        [in] Specifies the index of the stock resampling filter
        Can be one of the following values
        STOCK_FILTER_BELL
        STOCK_FILTER_BOX
        STOCK_FILTER_CATMULLROM
        STOCK_FILTER_COSINE
        STOCK_FILTER_CUBICCONVOLUTION
        STOCK_FILTER_CUBICSPLINE
        STOCK_FILTER_HERMITE
        STOCK_FILTER_LANCZOS3
        STOCK_FILTER_LANCZOS8
        STOCK_FILTER_MITCHELL
        STOCK_FILTER_QUADRATIC
        STOCK_FILTER_QUADRATICBSPLINE
        STOCK_FILTER_TRIANGLE

Return Values

If the function succeeds, the return value is the handle of the resampled bitmap. If the function fails, the return value is NULL. To get extended error information, call GetLastError.

Remarks

  • dwWidth and dwHeight are clipped to the range 1-4096
  • dwFilter is wrapped around the available stock filter range (i.e. dwFilter=STOCK_FILTER_TRIANGLE+1 becomes dwFilter=STOCK_FILTER_BELL).

Resampling With Custom Filters

CreateUserFilterResampledBitmap

The CreateUserFilterResampledBitmap function creates a resampled bitmap compatible with the device that is associated with the specified device context. The resampling filter is provided by the caller.

HBITMAP CreateUserFilterResampledBitmap(
   HDC hdc,                     // handle to DC
   HBITMAP hBmpSource,          // handle to original bitmap
   DWORD dwWidth,               // width of the resampled bitmap, in pixels
   DWORD dwHeight,              // height of the resampled bitmap, in pixels
   double (*pCustomFilter)(double),    // custom filter function pointer
   double dRadius             // custom filter radius
);

Parameters

    hdc
        [in] Handle to a device context
    hBmpSource
        [in] Handle to the original bitmap
    dwWidth
        [in] Specifies the resampled bitmap width, in pixels
    dwHeight
        [in] Specifies the resampled bitmap height, in pixels
    pCustomFilter
        [in] Specifies the pointer to the custom filter function.
    dRadius
        [in] Radius of the custom filter.

Return Values

If the function succeeds, the return value is the handle of the resampled bitmap. If the function fails, the return value is NULL. To get extended error information, call GetLastError.

Remarks

  • dwWidth and dwHeight are clipped to the range 1-4096
  • Radius should be the proper filter radius for the pCustomFilter function
  • Legal range for Radius is 0.0-16.0.

Using the Code

Project Setup

In order to use Resample.dll functions, the application must:

  1. Include Resample.h header file
  2. Link with Resample.lib file

This of course implies that Visual Studio Environment must be able to find both header and library file paths.

Using the library is quite straightforward, the following code snippet loads a bitmap from test.bmp file and resamples it using the BOX stock filter:

  ...
  // load the original bitmap
  hBmp = (HBITMAP) LoadImage( hInstance, _T("test.bmp"),
        IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
  if (! hBmp ) return FALSE;
  // create the 1024x768 resampled bitmap with stock filter
  hBmpResampled = CreateResampledBitmap(hdc, hBmp, 1024, 768, STOCK_FILTER_BOX);
  ...

The following one shows instead the use of a custom filter:

  ...
  // user-filter radius
  const double dRad = 3.0;
  ...
  // user-filter function
  double myFilter( double x)
  {
    if ( x < 0.0 ) x = -x;
    if (x < dRad) return (dRad * dRad - 2 * dRad * x + x * x);
    return 0.0;
  }
  ...
  // load the original bitmap
  hBmp = (HBITMAP) LoadImage( hInstance, _T("test.bmp"),
        IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
  if (! hBmp ) return FALSE;
  // create the 1024x768 resampled bitmap with user filter
  hBmpResampled = CreateUserFilterResampledBitmap(hdc, hBmp, 1024, 768, myFilter, dRad);
  ...

The created HBITMAP can be used (as any other valid bitmap handle) later on, inside the application WM_PAINT message handler as follows:

  // WM_PAINT message handler
  ...
  // hdc is the painting device context
  HDC hMemdc = CreateCompatibleDC(hdc);
  if (hMemDC)
  {
    HBITMAP hBmpOld = (HBITMAP) SelectObject(hMemdc, hBmpResampled);
    BitBlt(hdc, 0, 0, 1024, 768, hMemdc, 0, 0, SRCCOPY);
    SelectObject(hMemdc, hBmpOld);
    DeleteDC(hMemDC);
  }
  ...

The Test Application

I have included a test application project, namely ResampleTestApp. It allows the user to load a bitmap, then the loaded image is shown (using its original dimensions) inside the main window while its resampled twin is painted in a child window. The user can select filter type and scale (zoom) of the resampled bitmap. The application (standard C++ Windows app, no MFC), though very basic and rough, allows to try all filters on different images. The child window title bar shows some information about the occurred resampling:

Child window title bar image

Namely:

  • Name of the filter used
  • Effective size of the resampled image
  • Resampling effective scale
  • Elapsed time

The resampling effective scale may differ significantly with the requested one due to the constraints on the dimension range of the resampled bitmap (1-4096).

Be aware that the application naively creates a resampled bitmap with chosen scale, e.g. if you ask it a 4x of the original 1024 x 768 bitmap, it calls the resampling function even if the window itself is far smaller than 4096 x 3072 (on painting the image is centered and clipped) this can be a very time consuming task (especially with high quality filters like the Lanczos ones).

Points of Interest

I have modified the original Libor algorithm to:

  1. Reduce memory allocation/deallocation calls (memory is allocated in a big chunk)
  2. Avoid unnecessary memory transfer

The resulting function is a bit faster (trade-off: code is less clean...) than the original Libor one (BTW there is, of course, the 100% pure unmanaged code impact...). By design, the resampling happens on all of the RGBQUAD components. It is also worth noting that resampling intermediate results are held by unsigned char instead of unsigned short (used by Libor) this may degrade the quality but, as far as I can perceive, there is no significant effect.

History

  • 19th December, 2007: First release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here