Plain C Resampling DLL
A small DLL providing two functions to resample GDI-based bitmap
- Download DLL - 53.8 KB
- Download DLL source - 18.56 KB
- Download test app - 100.66 KB
- Download test app source - 30.29 KB

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.


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
anddwHeight
are clipped to the range1-4096
dwFilter
is wrapped around the available stock filter range (i.e.dwFilter=STOCK_FILTER_TRIANGLE+1
becomesdwFilter=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
anddwHeight
are clipped to the range1-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:
- Include Resample.h header file
- 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:

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:
- Reduce memory allocation/deallocation calls (memory is allocated in a big chunk)
- 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