阅读提示:
《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。
《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。
尽可能保持二者内容一致,可相互对照。
因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com
这里可访问《C++图像处理 -- 文章索引》
http://blog.youkuaiyun.com/maozefa/article/details/7188505
不经意间,用C++写了不少有关图像处理的文章,与《Delphi图像处理》系列文章相比,文章之间缺乏必要的联系性,因此有必要进行一些调整,并都纳入《C++图像处理系列》。
本文写了一个C++头文件BmpData.h,它包含了Gdiplus.h文件,并提供了几个最基础的函数。
以后,所有《C++图像处理》系列的文章都包含此头文件,并直接使用GDI+位图数据结构BitmapData类型处理图像。例子也以GDI+位图为主。
- //---------------------------------------------------------------------------
- #ifndef BmpDataH
- #define BmpDataH
- #include <windows.h>
- #include <algorithm>
- using std::min;
- using std::max;
- #include <gdiplus.h>
- using namespace Gdiplus;
- //---------------------------------------------------------------------------
- #define ScanAllocFlag 0x00000100
- #define PixelAlphaFlag 0x00010000
- //---------------------------------------------------------------------------
- // 定义象素插值方式
- typedef enum
- {
- InterpolateModeDefault, // 缺省为线形插值
- InterpolateModeNear, // 临近插值
- InterpolateModeBilinear,// 线形插值
- InterpolateModeBicubic // 双立方插值
- }InterpolateMode;
- typedef enum
- {
- // PixelFormatInValid,
- PixelFormat1bit,
- PixelFormat4bit,
- PixelFormat8bit,
- PixelFormat15bit,
- PixelFormat16bit,
- PixelFormat24bit,
- PixelFormat32bit
- }ImagePixelFormat;
- // 定义ARGB像素结构
- typedef union
- {
- ARGB Color;
- struct
- {
- BYTE Blue;
- BYTE Green;
- BYTE Red;
- BYTE Alpha;
- };
- }ARGBQuad, *PARGBQuad;
- //---------------------------------------------------------------------------
- FORCEINLINE
- VOID SetAlphaFlag(BitmapData *data, BOOL isAlpha)
- {
- if (isAlpha) data->Reserved |= PixelAlphaFlag;
- else data->Reserved &= ~PixelAlphaFlag;
- }
- //---------------------------------------------------------------------------
- FORCEINLINE
- BOOL HasAlphaFlag(CONST BitmapData *data)
- {
- return (data->Reserved & PixelAlphaFlag) != 0;
- }
- //---------------------------------------------------------------------------
- FORCEINLINE
- VOID SetInterpolateMode(BitmapData *data, InterpolateMode mode)
- {
- data->Reserved = (data->Reserved & 0xffffff) | (mode << 24);
- }
- //---------------------------------------------------------------------------
- FORCEINLINE
- InterpolateMode GetInterpolateMode(CONST BitmapData *data)
- {
- return (InterpolateMode)(data->Reserved >> 24);
- }
- //---------------------------------------------------------------------------
- // 锁定GDI+32位位图扫描线到data
- FORCEINLINE
- VOID LockBitmap(Gdiplus::Bitmap *bmp, BitmapData *data)
- {
- Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());
- BOOL hasAlpha = bmp->GetPixelFormat() & PixelFormatAlpha;
- bmp->LockBits(&r, ImageLockModeRead | ImageLockModeWrite,
- PixelFormat32bppARGB, data);
- SetAlphaFlag(data, hasAlpha);
- }
- //---------------------------------------------------------------------------
- // GDI+位图扫描线解锁
- FORCEINLINE
- VOID UnlockBitmap(Gdiplus::Bitmap *bmp, BitmapData *data)
- {
- data->Reserved &= 0xff;
- bmp->UnlockBits(data);
- }
- //---------------------------------------------------------------------------
- // 用给定的图像数据制造并返回与GDI+兼容的32位位图数据结构。
- // 参数;宽度,高度,扫描线宽度,扫描线首地址,alpha标记,返回的位图数据结构指针。
- // 注:如果stride=0,自动计算扫描线宽度
- FORCEINLINE
- VOID GetBitmapData(INT width, INT height, INT stride, LPVOID scan0,
- ImagePixelFormat format, BOOL isAlpha, BitmapData *data)
- {
- INT bits[] = {0x100, 0x400, 0x800, 0x1005, 0x1000, 0x1800, 0x2000};
- data->Width = width;
- data->Height = height;
- data->Scan0 = scan0;
- data->PixelFormat = bits[format];
- if (stride)
- data->Stride = stride;
- else
- data->Stride = (INT)((width * (data->PixelFormat >> 8) + 31) & ~31) >> 3;
- SetAlphaFlag(data, isAlpha);
- }
- //---------------------------------------------------------------------------
- // 用给定的宽度、高度和像素位数制造并返回新的与GDI+兼容的位图数据结构。
- // 必须用FreeBitmapData释放
- FORCEINLINE
- VOID GetBitmapData(INT width, INT height, ImagePixelFormat format, BitmapData *data)
- {
- GetBitmapData(width, height, 0, NULL, format, format == PixelFormat32bit, data);
- data->Scan0 = GlobalLock(GlobalAlloc(GHND, data->Height * data->Stride));
- if (data->Scan0) data->Reserved |= ScanAllocFlag;
- }
- //---------------------------------------------------------------------------
- // 用给定的宽度和高度制造并返回新的与GDI+兼容的32位位图数据结构。
- // 必须用FreeBitmapData释放
- FORCEINLINE
- VOID GetBitmapData(INT width, INT height, BitmapData *data)
- {
- GetBitmapData(width, height, PixelFormat32bit, data);
- }
- //---------------------------------------------------------------------------
- // 获取32位子位图数据结构
- FORCEINLINE
- BOOL GetBitmapData(CONST BitmapData *data, INT x, INT y, INT width, INT height, BitmapData *sub)
- {
- width += x;
- height += y;
- if (width > (INT)data->Width)
- width = data->Width;
- if (height > (INT)data->Height)
- height = data->Height;
- INT ScanOffset = 0;
- if (x > 0)
- {
- width -= x;
- ScanOffset += (x << 2);
- }
- if (y > 0)
- {
- height -= y;
- ScanOffset += (y * data->Stride);
- }
- sub->Scan0 = (LPBYTE)data->Scan0 + ScanOffset;
- if (width <= 0 || height <= 0)
- return FALSE;
- sub->Width = width;
- sub->Height = height;
- sub->Stride = data->Stride;
- sub->PixelFormat = data->PixelFormat;
- sub->Reserved = data->Reserved & ~ScanAllocFlag;
- return TRUE;
- }
- //---------------------------------------------------------------------------
- // 如果data分配了扫描线内存,释放扫描线内存
- FORCEINLINE
- VOID FreeBitmapData(BitmapData *data)
- {
- if ((data->Reserved & ScanAllocFlag) && data->Scan0)
- {
- HGLOBAL handle = GlobalHandle(data->Scan0);
- if (handle)
- {
- GlobalUnlock(handle);
- GlobalFree(handle);
- }
- data->Reserved = 0;
- }
- }
- //---------------------------------------------------------------------------
- // 获取32位位图数据拷贝参数
- // 参数:目标数据,源数据,宽度,高度,目标扫描线,源扫描线,目标偏移,源偏移
- FORCEINLINE
- VOID GetDataCopyParams(CONST BitmapData *dest, CONST BitmapData *source,
- UINT &width, UINT &height, PARGBQuad &dstScan0, PARGBQuad &srcScan0,
- INT &dstOffset, INT &srcOffset)
- {
- width = dest->Width < source->Width? dest->Width : source->Width;
- height = dest->Height < source->Height? dest->Height : source->Height;
- dstScan0 = (PARGBQuad)dest->Scan0;
- srcScan0 = (PARGBQuad)source->Scan0;
- dstOffset = (dest->Stride >> 2) - (INT)width;
- srcOffset = (source->Stride >> 2) - (INT)width;
- }
- //---------------------------------------------------------------------------
- // 获取并返回32位位图数据结构data的边框扩展图像数据结构。Radius:扩展半径
- FORCEINLINE
- VOID GetExpendData(CONST BitmapData *data, UINT radius, BitmapData *exp)
- {
- GetBitmapData(data->Width + (radius << 1), data->Height + (radius << 1),
- PixelFormat32bit, exp);
- SetAlphaFlag(exp, HasAlphaFlag(data));
- BitmapData sub;
- PARGBQuad pd, ps;
- UINT width, height;
- INT dstOffset, srcOffset;
- GetBitmapData(exp, radius, radius, data->Width, data->Height, &sub);
- GetDataCopyParams(&sub, data, width, height, pd, ps, dstOffset, srcOffset);
- PARGBQuad pt = pd - radius;
- UINT x, y;
- // 如果图像数据含Alpha,转换为PARGB像素格式
- if (HasAlphaFlag(data))
- {
- for (y = 0; y < height; y ++, pd += dstOffset, ps += srcOffset)
- {
- for (x = 0; x < width; x ++, pd ++, ps ++)
- {
- pd->Blue = (ps->Blue * ps->Alpha + 127) / 255;
- pd->Green = (ps->Green * ps->Alpha + 127) / 255;
- pd->Red = (ps->Red * ps->Alpha + 127) / 255;
- pd->Alpha = ps->Alpha;
- }
- }
- }
- // 否则, 直接像素拷贝
- else
- {
- for (y = 0; y < height; y ++, pd += dstOffset, ps += srcOffset)
- {
- for (x = 0; x < width; *pd ++ = *ps ++, x ++);
- }
- }
- // 扩展左右边框像素
- for (y = 0, pd = pt; y < height; y ++)
- {
- for (x = 0, ps = pd + radius; x < radius; *pd ++ = *ps, x ++);
- for (x = 0, pd += width, ps = pd - 1; x < radius; *pd ++ = *ps, x ++);
- }
- // 扩展头尾边框像素
- PARGBQuad pb = (PARGBQuad)((LPBYTE)pd - exp->Stride);
- PARGBQuad pd0 = (PARGBQuad)exp->Scan0;
- for (y = 0; y < radius; y ++)
- {
- for (x = 0; x < exp->Width; *pd0 ++ = pt[x], *pd ++ = pb[x], x ++);
- }
- }
- //---------------------------------------------------------------------------
- // PARGB格式转换成ARGB格式
- FORCEINLINE
- VOID PArgbConvertArgb(BitmapData *data)
- {
- PARGBQuad p = (PARGBQuad)data->Scan0;
- INT dataOffset = (data->Stride >> 2) - (INT)data->Width;
- for (UINT y = 0; y < data->Height; y ++, p += dataOffset)
- {
- for (UINT x = 0; x < data->Width; x ++, p ++)
- {
- p->Blue = p->Blue * 255 / p->Alpha;
- p->Green = p->Green * 255 / p->Alpha;
- p->Red = p->Red * 255 / p->Alpha;
- }
- }
- }
- //---------------------------------------------------------------------------
- // ARGB格式转换成PARGB格式
- FORCEINLINE
- VOID ArgbConvertPArgb(BitmapData *data)
- {
- PARGBQuad p = (PARGBQuad)data->Scan0;
- INT dataOffset = (data->Stride >> 2) - (INT)data->Width;
- for (UINT y = 0; y < data->Height; y ++, p += dataOffset)
- {
- for (UINT x = 0; x < data->Width; x ++, p ++)
- {
- p->Blue = (p->Blue * p->Alpha + 127) / 255;
- p->Green = (p->Green * p->Alpha + 127) / 255;
- p->Red = (p->Red * p->Alpha + 127) / 255;
- }
- }
- }
- //---------------------------------------------------------------------------
- #endif