65.9K
CodeProject 正在变化。 阅读更多。
Home

纯 C 重采样 DLL

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (37投票s)

2007 年 12 月 19 日

CPOL

5分钟阅读

viewsIcon

204064

downloadIcon

4976

一个小型 DLL,提供两个函数来重采样基于 GDI 的位图

Screenshot -

引言

当我第一次看到 Libor Tinka 关于重采样出色的文章 Image Resizing - Outperform GDI+ 时,我对自己说:“这确实是非常好的东西,我确实需要一个类似的工具来处理基于 GDI 的位图。”

Resample.dll 是一个小型动态链接库,它导出了两个重采样函数。其设计旨在模仿 Win32 GDI API 的接口和行为。

函数场景很简单:假设我们有一个 GDI HBITMAP(例如通过 LoadImage 从文件加载),大小为 w x h,我们需要将其调整到 wNew x hNew,而图像质量损失不大。我们可以通过简单调用库提供的函数 CreateResampledBitmap 来完成上述任务,如下所示:

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

请注意(例如,与 GDI CreateCompatibleBitmap 一样),获取的句柄在使用完毕后必须通过 GDI DeleteObject 进行删除。另一个导出的函数是 CreateUserFilterResampledBitmap,它允许调用者提供一个自定义过滤器函数指针。

致谢

本文基于 Libor Tinka 的文章:Image Resizing - Outperform GDI+。我只是将他的 C# 代码移植到了纯 C 代码(稍稍修改了算法),并将所有内容打包到一个 DLL 中。所有内置过滤器都是 Libor 在原文章中使用的过滤器,可以作为参考。

背景

使用此代码需要对 Win32 GDI API 有 general 的理解。要修改库的内部实现,则需要对 GDI 位图和 C 语言有更深入的理解。使用自定义过滤器时,熟悉回调函数可能会有所帮助。

Sample resampling image, CP
图像下采样示例。


Sample resampling image, CP
同一图像上采样。

库参考

由于该库只包含两个函数,我可以用经典的 GDI 文档风格提供参考信息。

使用内置过滤器进行重采样

CreateResampledBitmap

CreateResampledBitmap 函数创建一个与指定设备上下文关联的设备的兼容的重采样位图。重采样过滤器将从可用的内置过滤器中选择。

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
);

参数

    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

返回值

如果函数成功,返回值是重采样位图的句柄。如果函数失败,返回值是 NULL。要获取扩展的错误信息,请调用 GetLastError

备注

  • dwWidthdwHeight 将被裁剪到 1-4096 的范围。
  • dwFilter 将围绕可用的内置过滤器范围进行循环(即 dwFilter=STOCK_FILTER_TRIANGLE+1 将变为 dwFilter=STOCK_FILTER_BELL)。

使用自定义过滤器进行重采样

CreateUserFilterResampledBitmap

CreateUserFilterResampledBitmap 函数创建一个与指定设备上下文关联的设备的兼容的重采样位图。重采样过滤器由调用者提供。

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
);

参数

    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.

返回值

如果函数成功,返回值是重采样位图的句柄。如果函数失败,返回值是 NULL。要获取扩展的错误信息,请调用 GetLastError

备注

  • dwWidthdwHeight 将被裁剪到 1-4096 的范围。
  • Radius 应该是 pCustomFilter 函数的合适过滤器半径。
  • Radius 的合法范围是 0.0-16.0。

Using the Code

项目设置

为了使用 Resample.dll 函数,应用程序必须

  1. 包含 Resample.h 头文件。
  2. 链接 Resample.lib 文件。

这当然意味着 Visual Studio 环境必须能够找到头文件和库文件的路径。

使用该库非常直接,以下代码片段从 test.bmp 文件加载一个位图,并使用 BOX 内置过滤器对其进行重采样。

  ...
  // 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);
  ...

以下代码段则演示了自定义过滤器的使用。

  ...
  // 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);
  ...

创建的 HBITMAP 可以在应用程序的 WM_PAINT 消息处理程序中像任何其他有效的位图句柄一样使用,如下所示:

  // 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);
  }
  ...

测试应用程序

我包含了一个名为 ResampleTestApp 的测试应用程序项目。它允许用户加载位图,然后加载的图像(使用其原始尺寸)显示在主窗口中,而其重采样后的副本则绘制在一个子窗口中。用户可以选择过滤器类型和重采样位图的缩放比例。该应用程序(标准的 C++ Windows 应用,无 MFC),虽然非常基础且粗糙,但允许在不同图像上尝试所有过滤器。子窗口的标题栏显示有关发生的重采样的一些信息。

Child window title bar image

  • 使用的过滤器名称。
  • 重采样图像的有效尺寸。
  • 重采样的有效比例。
  • 经过的时间。

由于重采样位图尺寸范围(1-4096)的限制,重采样有效比例可能与请求的比例有显著差异。

请注意,该应用程序会简单地创建一个具有所选比例的重采样位图,例如,如果您请求将原始 1024 x 768 位图放大 4x,即使窗口本身远小于 4096 x 3072,它也会调用重采样函数(在绘制图像时,图像会被居中并裁剪)。这可能是一项非常耗时的任务(尤其是使用 Lanczos 等高质量过滤器时)。

关注点

我对 Libor 的原始算法进行了修改,以

  1. 减少内存分配/释放调用(内存分配在一个大块中)。
  2. 避免不必要的内存传输。

生成的函数比 Libor 的原始函数稍快(权衡:代码不太整洁……)。(顺便说一句,这当然有 100% 纯非托管代码的影响……)。根据设计,重采样会作用于所有的 RGBQUAD 分量。还值得注意的是,重采样的中间结果由 unsigned char 保存,而不是 unsigned short(Libor 使用的),这可能会降低质量,但据我所知,没有明显的影响。

历史

  • 2007 年 12 月 19 日:首次发布
© . All rights reserved.