转自:http://hi.baidu.com/cwcblog/blog/item/02f426509555e70f377abe31.html
这里所说的水印,就是在图片内部添加文字标记,通常用于标明版权。为了提高重用性,我把这个功能的实现封装成了一个类。
实现原理:
1.加载一个位图到内存
2.创建一个与屏幕内存设备兼容的内存设备上下文memDC
3.把位图选进memDC
4.设置自定义字体font
5.把font选进memDC
6.设置memDC的TextColor(文字颜色),BkColor(文字背景色),Transparent(文字背景是否透明)
7.自动或手动设置文字在图片的位置
8.使用TextOut函数将水印文字输出到内存设备memDC
9.把memDC中的内容保存到位图文件
代码实现:-类CWaterMark
头文件
/* Copyright (C) wuchen 2011 ALL RIGHTS RESERVED.
* Author:niewucai
* Email:niewucai@126.com
* Last Update:2011/05/11
*实现对位图图片添加文字水印功能 --头文件
*/
#pragmaonce
#include<string>
usingnamespace std;
class CWaterMark{
public:
/*构造方法
* @lpszImgFile:要添加水印的位图图片文件
*/
CWaterMark(LPCTSTR lpszImgFile);
/*构造方法
* @nIDResource:位图资源标识
*/
CWaterMark(UINT nIDResource);
~CWaterMark();
//水印文字位置枚举
typedefenum
{
/*表示左上位置。
*/
LeftTop = 1,
/*表示中上位置。
*/
CenterTop,
/*表示右上位置。
*/
RightTop,
/*表示左中位置。
*/
LeftMiddle,
/*表示中心位置。
*/
CenterMiddle,
/*表示右中位置。
*/
RightMiddle,
/*表示左下位置。
*/
LeftBottom,
/*表示中下位置。
*/
CenterBottom,
/*表示右下位置。
*/
RightBottom
}Location;
public:
int m_topSpacing;//水印文字与图片顶部的间距(像素)
int m_rightSpacing;//水印文字与图片右端的间距(像素)
int m_bottomSpacing;//水印文字与图片底部的间距(像素)
int m_leftSpacing;//水印文字与图片左端的间距(像素)
public:
/*更换当前操作位图
* @nIDResouce:位图资源标识
* return:是否更换成功
*/
bool ChangeBitmap(UINT nIDResource);
/*更换当前操作位图
* @lpszImgFile:位图文件路径
* return:是否更换成功
*/
bool ChangeBitmap(LPCTSTR lpszImgFile);
/*获取与设备无关的内存设备上下文*/
inline HDC GetMemoryDC()
{
return m_hMemDC;
}
/*获取当前处理位图的BITMAP结构*/
int GetBitmap(BITMAP* pBitMap);
/*获取水印文字的前景色*/
COLORREF GetForeColor();
/*设置水印文字的前景色*/
void SetForeColor(COLORREF foreColor = 0);
/*获取水印文字的背景色*/
COLORREF GetBkColor();
/*设置水印文字的背景色*/
void SetBkColor(COLORREF bkColor = RGB(255, 255, 255));
/*获取当前是否设置水印文字为透明背景*/
bool IsTransparent();
/*设置当前是否设置水印文字为透明背景*/
void SetTransparent(bool transparent = true);
/*获取当前用于输出水印文字的字体*/
HFONT GetTextFont();
/*设置当前用于输出水印文字的字体,返回上一次设置的字体*/
HFONT SetTextFont(HFONT hFont);
/*添加水印文字
* @lpszText:水印文本
* @x:水印文本相对图片左上角的水平位置(像素)
* @y:水印文本相对图片左上角的垂直位置(像素)
*/
void AddWaterMark(LPCTSTR lpszText, int x, int y);
/*根据选择的方式和上下左右的间距,自动添加水印文本到合适的位置,默认添加到图片右下角*/
void AddWaterMark(LPCTSTR lpszText, Location type = Location::RightBottom);
/*保存到当前操作位图文件,返回是否保存成功-只针对以位图文件加载的情况*/
bool Save();
/*保存到指定位图文件,返回是否保存成功*/
bool Save(LPCTSTR lpszFile);
private:
/*初始化工作*/
void Init();
/*根据BITMAP句柄创建位图文件信息*/
PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp);
/*创建位图文件*/
void CreateBMPFile(HWND hwnd, LPCTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC);
private:
string m_imgFile;//要添加水印的图片文件
HFONT m_hFont;//水印文字的当前字体
HFONT m_hOldFont;
HFONT m_hInitFont;//水印文字的初始字体
COLORREF m_foreColor;//水印文字的文本颜色
COLORREF m_bkColor;//水印文字的背景颜色
bool m_isTransparant;//水印文字是否透明
HBITMAP m_hBmp;//HBITMAP对象
HBITMAPm_hOldBmp;
HDCm_hMemDC;//内存设备句柄
};
实现文件:
/* Copyright (C) wuchen 2011 ALL RIGHTS RESERVED.
* Author:niewucai
* Email:niewucai@126.com
* Last Update:2011/05/11
*实现对位图图片添加文字水印功能--实现文件
*/
#include<stdafx.h>
#include"WaterMark.h"
CWaterMark::CWaterMark(LPCTSTR lpszImgFile)
: m_topSpacing(10),
m_rightSpacing(10),
m_bottomSpacing(10),
m_leftSpacing(10),
m_isTransparant(true),
m_foreColor(0),
m_bkColor(RGB(255, 255, 255))
{
this->m_imgFile = lpszImgFile;
//创建默认字体
m_hFont = CreateFontA(0, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, _T("宋体"));
m_hInitFont = m_hFont;
m_hBmp = (HBITMAP)LoadImage(AfxGetInstanceHandle(), lpszImgFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
Init();
}
CWaterMark::CWaterMark(UINT nIDResource)
: m_topSpacing(10),
m_rightSpacing(10),
m_bottomSpacing(10),
m_leftSpacing(10),
m_isTransparant(true),
m_foreColor(0),
m_bkColor(RGB(255, 255, 255))
{
//创建默认字体
m_hFont = CreateFontA(0, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, _T("宋体"));
m_hInitFont = m_hFont;
m_hBmp = (HBITMAP)LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(nIDResource));
Init();
}
CWaterMark::~CWaterMark()
{
SelectObject(m_hMemDC, m_hOldBmp);
SelectObject(m_hMemDC, m_hOldFont);
DeleteObject(m_hInitFont);
}
void CWaterMark::Init()
{
m_hMemDC = CreateCompatibleDC(NULL);//创建一个与屏幕内存设备兼容的内存设备
m_hOldFont = (HFONT)SelectObject(m_hMemDC, (HFONT)m_hFont);//选择默认字体
m_hOldBmp = (HBITMAP)SelectObject(m_hMemDC, m_hBmp);//选择位图到内存设备
SetBkMode(m_hMemDC, m_isTransparant ? TRANSPARENT : OPAQUE);//是否背景透明
SetTextColor(m_hMemDC, m_foreColor);//前景色
::SetBkColor(m_hMemDC, m_bkColor);//背景色
}
/*更换当前操作位图
* @nIDResouce:位图资源标识
* return:是否更换成功
*/
bool CWaterMark::ChangeBitmap(UINT nIDResource)
{
m_hBmp = (HBITMAP)LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(nIDResource));
if(!m_hBmp)
{
returnfalse;
}
m_imgFile.clear();
m_hOldBmp = (HBITMAP)SelectObject(m_hMemDC, m_hBmp);//选择位图到内存设备
returntrue;
}
/*更换当前操作位图
* @lpszImgFile:位图文件路径
* return:是否更换成功
*/
bool CWaterMark::ChangeBitmap(LPCTSTR lpszImgFile)
{
m_hBmp = (HBITMAP)LoadImage(AfxGetInstanceHandle(), lpszImgFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if(!m_hBmp)
{
returnfalse;
}
this->m_imgFile = lpszImgFile;
m_hOldBmp = (HBITMAP)SelectObject(m_hMemDC, m_hBmp);//选择位图到内存设备
returntrue;
}
/*获取当前处理位图的BITMAP结构*/
int CWaterMark::GetBitmap(BITMAP* pBitMap)
{
if(!m_hBmp)
{
return 0;
}
return CBitmap::FromHandle(m_hBmp)->GetBitmap(pBitMap);
}
/*获取水印文字的前景色*/
COLORREF CWaterMark::GetForeColor()
{
return m_foreColor;
}
/*设置水印文字的前景色*/
void CWaterMark::SetForeColor(COLORREF foreColor/* = 0*/)
{
m_foreColor = foreColor;
SetTextColor(m_hMemDC, m_foreColor);//设置前景色
}
/*获取水印文字的背景色*/
COLORREF CWaterMark::GetBkColor()
{
return m_bkColor;
}
/*设置水印文字的背景色*/
void CWaterMark::SetBkColor(COLORREF bkColor/* = RGB(255, 255, 255)*/)
{
m_bkColor = bkColor;
::SetBkColor(m_hMemDC, m_bkColor);//背景色
}
/*获取当前是否设置水印文字为透明背景*/
bool CWaterMark::IsTransparent()
{
return m_isTransparant;
}
/*设置当前是否设置水印文字为透明背景*/
void CWaterMark::SetTransparent(bool transparent/* = true*/)
{
m_isTransparant = transparent;
SetBkMode(m_hMemDC, m_isTransparant ? TRANSPARENT : OPAQUE);//是否背景透明
}
/*获取当前用于输出水印文字的字体*/
HFONT CWaterMark::GetTextFont()
{
return m_hFont;
}
/*设置当前用于输出水印文字的字体,返回上一次设置的字体*/
HFONT CWaterMark::SetTextFont(HFONT hFont)
{
m_hFont = hFont;
m_hOldFont = (HFONT)SelectObject(m_hMemDC, (HFONT)m_hFont);//设置水印文字字体
return m_hOldFont;
}
/*添加水印文字
* @lpszText:水印文本
* @x:水印文本相对图片左上角的水平位置(像素)
* @y:水印文本相对图片左上角的垂直位置(像素)
*/
void CWaterMark::AddWaterMark(LPCTSTR lpszText, int x, int y)
{
TextOut(m_hMemDC, x, y, lpszText, strlen(lpszText));
}
/*根据选择的方式和上下左右的间距,自动添加水印文本到合适的位置,默认添加到图片右下角*/
void CWaterMark::AddWaterMark(LPCTSTR lpszText, Location type/* = Location::RightBottom*/)
{
//获取字体宽度
TEXTMETRIC txtMetric = {0};
::GetTextMetrics(m_hMemDC, &txtMetric);
//水印文字的总高度和宽度
SIZE size;
::GetTextExtentPoint(m_hMemDC, lpszText, strlen(lpszText), &size);
int height = size.cy;
int width = size.cx;
//水印文本相对图片左上角的水平位置(像素)
int x = 0;
//水印文本相对图片左上角的垂直位置(像素)
int y = 0;
BITMAP bmp;
this->GetBitmap(&bmp);
//位图的高度和宽度
int nBmpWidth = bmp.bmWidth;
int nBmpHeight = bmp.bmHeight;
switch(type)
{
case Location::LeftTop:
x = m_leftSpacing;
y = m_topSpacing;
break;
case Location::CenterTop:
x = (nBmpWidth - width) / 2;
y = m_topSpacing;
break;
case Location::RightTop:
x = nBmpWidth - width - m_rightSpacing;
y = m_topSpacing;
break;
case Location::LeftMiddle:
x = m_leftSpacing;
y = (nBmpHeight - height) / 2;
break;
case Location::CenterMiddle:
x = (nBmpWidth - width) / 2;
y = (nBmpHeight - height) / 2;
break;
case Location::RightMiddle:
x = nBmpWidth - width - m_rightSpacing;
y = (nBmpHeight - height) / 2;
break;
case Location::LeftBottom:
x = m_leftSpacing;
y = nBmpHeight - height - m_bottomSpacing;
break;
case Location::CenterBottom:
x = (nBmpWidth - width) / 2;
y = nBmpHeight - height - m_bottomSpacing;
break;
case Location::RightBottom:
x = nBmpWidth - width - m_rightSpacing;
y = nBmpHeight - height - m_bottomSpacing;
break;
default:
break;
}
TextOut(m_hMemDC, x, y, lpszText, strlen(lpszText));
}
/*保存到当前操作位图文件,返回是否保存成功-只针对以位图文件加载的情况*/
bool CWaterMark::Save()
{
if(m_imgFile.empty())
{
returnfalse;
}
if(m_hBmp)
{
PBITMAPINFO bmpInfo = CreateBitmapInfoStruct(NULL, m_hBmp);
CreateBMPFile(NULL, m_imgFile.c_str(), bmpInfo, m_hBmp, m_hMemDC);
returntrue;
}
else
{
returnfalse;
}
}
/*保存到指定位图文件,返回是否保存成功*/
bool CWaterMark::Save(LPCTSTR lpszFile)
{
if(m_hBmp)
{
PBITMAPINFO bmpInfo = CreateBitmapInfoStruct(NULL, m_hBmp);
CreateBMPFile(NULL, lpszFile, bmpInfo, m_hBmp, m_hMemDC);
returntrue;
}
else
{
returnfalse;
}
}
PBITMAPINFO CWaterMark::CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp)
{
BITMAP bmp;
PBITMAPINFO pbmi;
WORDcClrBits;
// Retrieve the bitmap color format, width, and height.
if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp))
return NULL;
// Convert the color format to a count of bits.
cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
if (cClrBits == 1)
cClrBits = 1;
elseif (cClrBits <= 4)
cClrBits = 4;
elseif (cClrBits <= 8)
cClrBits = 8;
elseif (cClrBits <= 16)
cClrBits = 16;
elseif (cClrBits <= 24)
cClrBits = 24;
else cClrBits = 32;
// Allocate memory for the BITMAPINFO structure. (This structure
// contains a BITMAPINFOHEADER structure and an array of RGBQUAD
// data structures.)
if (cClrBits < 24)
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * (1<< cClrBits));
// There is no RGBQUAD array for these formats: 24-bit-per-pixel or 32-bit-per-pixel
else
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER));
// Initialize the fields in the BITMAPINFO structure.
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
if (cClrBits < 24)
pbmi->bmiHeader.biClrUsed = (1<<cClrBits);
// If the bitmap is not compressed, set the BI_RGB flag.
pbmi->bmiHeader.biCompression = BI_RGB;
// Compute the number of bytes in the array of color
// indices and store the result in biSizeImage.
// The width must be DWORD aligned unless the bitmap is RLE
// compressed.
pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
* pbmi->bmiHeader.biHeight;
// Set biClrImportant to 0, indicating that all of the
// device colors are important.
pbmi->bmiHeader.biClrImportant = 0;
return pbmi;
}
void CWaterMark::CreateBMPFile(HWND hwnd, LPCTSTR pszFile, PBITMAPINFO pbi,
HBITMAP hBMP, HDC hDC)
{
HANDLE hf;// file handle
BITMAPFILEHEADER hdr;// bitmap file-header
PBITMAPINFOHEADER pbih;// bitmap info-header
LPBYTE lpBits;// memory pointer
DWORD dwTotal;// total count of bytes
DWORD cb;// incremental count of bytes
BYTE *hp;// byte pointer
DWORD dwTmp;
pbih = (PBITMAPINFOHEADER) pbi;
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
if (!lpBits)
return;
// Retrieve the color table (RGBQUAD array) and the bits
// (array of palette indices) from the DIB.
if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi,
DIB_RGB_COLORS))
{
return ;
}
// Create the .BMP file.
hf = CreateFile(pszFile,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hf == INVALID_HANDLE_VALUE)
return;
hdr.bfType = 0x4d42;// 0x42 = "B" 0x4d = "M"
// Compute the size of the entire file.
hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof(RGBQUAD) + pbih->biSizeImage);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
// Compute the offset to the array of color indices.
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof (RGBQUAD);
// Copy the BITMAPFILEHEADER into the .BMP file.
if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),
(LPDWORD) &dwTmp,NULL))
{
return;
}
// Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER)
+ pbih->biClrUsed * sizeof (RGBQUAD),
(LPDWORD) &dwTmp, ( NULL)))
return;
// Copy the array of color indices into the .BMP file.
dwTotal = cb = pbih->biSizeImage;
hp = lpBits;
if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL))
return;
// Close the .BMP file.
if (!CloseHandle(hf))
return;
// Free memory.
GlobalFree((HGLOBAL)lpBits);
}
测试代码:新建一个单文档,新建一个测试菜单项
void CBasicCGView::OnTestMenu()
{
//构造一个水印操作类对象
CWaterMark mark(IDB_BITMAP1);
//创建一个字体GDI对象
HFONT hFont = CreateFontA(28, 0, 0, 0, FW_BOLD, 1, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, _T("华文楷体"));
//设置水印文字文本字体
mark.SetTextFont(hFont);
//设置水印文字文本颜色
mark.SetForeColor(RGB(255, 128, 0));
//添加一条水印文本到位图的右下角,也可以添加多条
//自动添加水印时,文字与图片上下左右顶端的间距由
//mark.m_leftSpacing, mark.m_rightSpacing, mark.m_topSpacing, mark.m_bottomSpacing控制
mark.AddWaterMark("Hello World", CWaterMark::Location::RightBottom);
BITMAP bmp;
mark.GetBitmap(&bmp);
CDC* pDC = GetDC();
//拷贝结果到客户区视图DC
pDC->BitBlt(0, 0, bmp.bmWidth, bmp.bmHeight, CDC::FromHandle(mark.GetMemoryDC()), 0, 0, SRCCOPY);
ReleaseDC(pDC);
//保存结果到指定位图文件
mark.Save("C://1.bmp");
}
目前,该类只能处理位图图片