Click here to Skip to main content
15,879,535 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.1K   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

 
AnswerRe: 16bpp question Pin
CPallini14-Jan-11 11:24
mveCPallini14-Jan-11 11:24 
GeneralExcellent library, but blows up when called from my VS 2005 application. Pin
Leif Simon Goodwin30-Dec-10 2:25
Leif Simon Goodwin30-Dec-10 2:25 
GeneralRe: Excellent library, but blows up when called from my VS 2005 application. Pin
CPallini30-Dec-10 3:09
mveCPallini30-Dec-10 3:09 
GeneralRe: Excellent library, but blows up when called from my VS 2005 application. Pin
Leif Simon Goodwin30-Dec-10 3:38
Leif Simon Goodwin30-Dec-10 3:38 
GeneralMy vote of 5 Pin
Hard Coder23-Mar-10 4:22
Hard Coder23-Mar-10 4:22 
GeneralRe: My vote of 5 Pin
CPallini23-Mar-10 5:59
mveCPallini23-Mar-10 5:59 
GeneralResized 16-bit bitmap has horizontal and vertical gray ( or perhaps blue) lines Pin
Member 33450956-Oct-09 4:09
Member 33450956-Oct-09 4:09 
GeneralI am getting Unresolved external error! Pin
kapardhi11-May-09 2:29
kapardhi11-May-09 2:29 
Thanks for the article
I have included the header file an linked the library, the following is my code snippet

//mcsMyImage is my picture control ID
HWND hImage = mcsMyImage.GetSafeHwnd();
HDC hdc = ::GetDC (hImage);

//I used to load the bitmap image with original size using following code
//mcsMyImage.SetBitmap (m_hBitmap);

// Trying to resample
HBITMAP hBmpResampled;

// create the 1024x768 resampled bitmap with stock filter
hBmpResampled = CreateResampledBitmap(hdc, m_hBitmap, 1024, 768, STOCK_FILTER_BOX);

on compiling i get
error LNK2019: unresolved external symbol __imp_CreateResampledBitmap referenced in function "public: void __cdecl Dg2::DisplayData(void)" (?DisplayData@Dg2@@QAAXXZ)

How to resolve this error!
Thanks!
GeneralRe: I am getting Unresolved external error! Pin
CPallini11-May-09 2:42
mveCPallini11-May-09 2:42 
GeneralNeed to support RGB images Pin
harry4fun14-Nov-08 0:59
harry4fun14-Nov-08 0:59 
QuestionRe: Need to support RGB images Pin
CPallini14-Nov-08 2:10
mveCPallini14-Nov-08 2:10 
AnswerRe: Need to support RGB images Pin
harry4fun16-Nov-08 19:28
harry4fun16-Nov-08 19:28 
QuestionRe: Need to support RGB images Pin
CPallini16-Nov-08 21:24
mveCPallini16-Nov-08 21:24 
AnswerRe: Need to support RGB images Pin
harry4fun16-Nov-08 22:15
harry4fun16-Nov-08 22:15 
GeneralRe: Need to support RGB images Pin
CPallini16-Nov-08 22:33
mveCPallini16-Nov-08 22:33 
GeneralRe: Need to support RGB images Pin
harry4fun17-Nov-08 18:54
harry4fun17-Nov-08 18:54 
GeneralRe: Need to support RGB images Pin
CPallini18-Nov-08 2:02
mveCPallini18-Nov-08 2:02 
GeneralRe: Need to support RGB images Pin
harry4fun18-Nov-08 23:14
harry4fun18-Nov-08 23:14 
Questioncode not working properly in my vc++ application [modified] Pin
madhuri11-Jul-08 21:04
madhuri11-Jul-08 21:04 
QuestionRe: code not working properly in my vc++ application Pin
CPallini1-Jul-08 21:37
mveCPallini1-Jul-08 21:37 
Answer[Message Deleted] Pin
madhuri11-Jul-08 22:07
madhuri11-Jul-08 22:07 
GeneralRe: code not working properly in my vc++ application Pin
CPallini1-Jul-08 22:16
mveCPallini1-Jul-08 22:16 
GeneralRe: code not working properly in my vc++ application Pin
madhuri11-Jul-08 22:31
madhuri11-Jul-08 22:31 
GeneralRe: code not working properly in my vc++ application Pin
CPallini1-Jul-08 22:43
mveCPallini1-Jul-08 22:43 
GeneralRe: code not working properly in my vc++ application Pin
madhuri12-Jul-08 0:52
madhuri12-Jul-08 0:52 

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.