Click here to Skip to main content
15,881,715 members
Articles / Desktop Programming / Win32
Article

Plain C Resampling DLL

Rate me:
Please Sign up or sign in to vote.
4.86/5 (40 votes)
19 Dec 2007CPOL5 min read 192.2K   4.8K   53   65
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:

C++
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.

C++
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.

C++
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:

C++
...
// 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:

C++
...
// 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:

C++
// 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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Biotecnica Instruments S.p.A.
Italy Italy




Debugging? Klingons do not debug. Our software does not coddle the weak. Bugs are good for building character in the user.
-- The Klingon programmer



Beelzebub for his friends [^].





Comments and Discussions

 
QuestionHelp with Resizing BMP C.Pallini DLL function call! Pin
EternalWyrm6-Dec-19 9:24
EternalWyrm6-Dec-19 9:24 
GeneralMy vote of 5 Pin
Sergey Alexandrovich Kryukov18-Sep-13 11:56
mvaSergey Alexandrovich Kryukov18-Sep-13 11:56 
QuestionImplementation problem Pin
_Flaviu28-Jun-13 1:56
_Flaviu28-Jun-13 1:56 
AnswerRe: Implementation problem Pin
CPallini28-Jun-13 2:18
mveCPallini28-Jun-13 2:18 
GeneralRe: Implementation problem Pin
_Flaviu28-Jun-13 2:22
_Flaviu28-Jun-13 2:22 
AnswerRe: Implementation problem Pin
CPallini28-Jun-13 11:21
mveCPallini28-Jun-13 11:21 
GeneralRe: Implementation problem Pin
_Flaviu30-Jun-13 21:43
_Flaviu30-Jun-13 21:43 
QuestionExtra functionality: resample filterout-out pixels Pin
radus21516-Feb-12 22:50
radus21516-Feb-12 22:50 
AnswerRe: Extra functionality: resample filterout-out pixels Pin
CPallini16-Feb-12 23:53
mveCPallini16-Feb-12 23:53 
GeneralRe: Extra functionality: resample filterout-out pixels Pin
radus21517-Feb-12 0:01
radus21517-Feb-12 0:01 
GeneralLines in Resampled Images (Fix) Pin
jlpilkin3-Jun-11 20:44
jlpilkin3-Jun-11 20:44 
GeneralRe: Lines in Resampled Images (Fix) Pin
CPallini3-Jun-11 23:24
mveCPallini3-Jun-11 23:24 
GeneralMy vote of 5 Pin
Nagy Vilmos13-May-11 4:30
professionalNagy Vilmos13-May-11 4:30 
GeneralRe: My vote of 5 Pin
CPallini23-May-11 6:34
mveCPallini23-May-11 6:34 
Answer11x speed improvement Pin
Guilherme C. Hazan12-May-11 3:39
Guilherme C. Hazan12-May-11 3:39 
GeneralRe: 11x speed improvement Pin
CPallini12-May-11 5:57
mveCPallini12-May-11 5:57 
GeneralRe: 11x speed improvement Pin
guju17-May-11 3:36
guju17-May-11 3:36 
GeneralRe: 11x speed improvement Pin
_Flaviu9-Jul-13 23:33
_Flaviu9-Jul-13 23:33 
GeneralStride Pin
guju12-Apr-11 8:16
guju12-Apr-11 8:16 
GeneralRe: Stride Pin
CPallini12-May-11 6:00
mveCPallini12-May-11 6:00 
GeneralRe: Stride Pin
guju17-May-11 3:35
guju17-May-11 3:35 
GeneralDoesn't seem to work with large images Pin
jlpilkin10-Mar-11 7:48
jlpilkin10-Mar-11 7:48 
GeneralRe: Doesn't seem to work with large images Pin
CPallini10-Mar-11 8:06
mveCPallini10-Mar-11 8:06 
GeneralRe: Doesn't seem to work with large images Pin
CPallini12-Mar-11 9:49
mveCPallini12-Mar-11 9:49 
GeneralRe: Doesn't seem to work with large images Pin
jlpilkin12-Mar-11 10:13
jlpilkin12-Mar-11 10:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.