通过GDI+绘制验证码图片(png)
要求:绘制如图的验证码:做法:一个一个字符(2、6、j、c、p)绘制,绘制一个小图后,再画到大图上面。最后保存成png。
1、随机函数
char lettter[62] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','s','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','S','Y','Z'};
int RangedRand(int range_min, int range_max)
{
int u = (double)rand() / (RAND_MAX + 1) * (range_max - range_min) + range_min;
return u;
}
2、绘制一个字符
//画一个字符 生成一个
Gdiplus::Bitmap *letterToPic(wchar_t *s/*, Gdiplus::Color c,float py*/)
{
using namespace Gdiplus;
Gdiplus::Bitmap *bmp = new Gdiplus::Bitmap(24, 40, PixelFormat32bppARGB);
Gdiplus::Graphics g(bmp);
for (int i = 0; i < bmp->GetWidth(); i++)
{
for (int j = 0; j < bmp->GetHeight(); j++){
bmp->SetPixel(i, j, Gdiplus::Color::Transparent);
}
}
//设置画板的坐标原点为中点
g.TranslateTransform(bmp->GetWidth()/2, bmp->GetHeight()/2);
//以指定角度对画板进行旋转
g.RotateTransform(-30+rand()%60);
std::vector<std::wstring> fonts;
fonts.push_back(L"微软雅黑");
fonts.push_back(L"宋体");
fonts.push_back(L"楷体");
fonts.push_back(L"仿宋");
std::vector<DWORD> colors;
colors.push_back(Color::Yellow);
colors.push_back(Color::Blue);
colors.push_back(Color::Red);
colors.push_back(Color::Green);
colors.push_back(Color::Purple);
Color clr;
clr.SetFromCOLORREF(colors[rand() % colors.size()]);
Gdiplus::PointF p(0,0);
Gdiplus::RectF size;
int fontSize = rand()%5+15;
g.MeasureString((const WCHAR*)s,1,&Gdiplus::Font(&FontFamily(L"微软雅黑"/*fonts[rand() % fonts.size()].c_str()*/), fontSize, Gdiplus::FontStyleRegular, Gdiplus::Unit::UnitPoint), p, &size);
Gdiplus::PointF p1((bmp->GetWidth() - size.Width) / 2 - bmp->GetWidth() / 2, (bmp->GetHeight() - size.Height) / 2 - bmp->GetHeight() / 2);
g.SetTextRenderingHint(TextRenderingHintAntiAliasGridFit);//使文字变得平滑
g.DrawString((const WCHAR*)s,
1,
&Gdiplus::Font(&FontFamily(L"微??软???雅?黑??"/*fonts[rand() % fonts.size()].c_str()*/), fontSize, Gdiplus::FontStyleRegular, Gdiplus::Unit::UnitPoint),
p1,
&SolidBrush(clr));
return bmp;
}
3、生成随机字符 并画到大图上去
void CMainDlg::createCodePic()
{
ULONG_PTR m_gdiplusToken;
Gdiplus::GdiplusStartupInput StartupInput;
GdiplusStartup(&m_gdiplusToken,&StartupInput,NULL);
DWORD dwBegin = GetTickCount();
using namespace Gdiplus;
srand((unsigned)time(NULL));
std::string yzm;
Gdiplus::Bitmap bmp(120, 40, PixelFormat32bppARGB);//整个图大小120*40
Graphics g(&bmp);
std::vector<DWORD> colors;
colors.push_back(Color::Yellow);
colors.push_back(Color::Blue);
colors.push_back(Color::Red);
colors.push_back(Color::Green);
colors.push_back(Color::Purple);
for (int i = 0; i < bmp.GetWidth(); i++)
{
for (int j = 0; j < bmp.GetHeight(); j++){
bmp.SetPixel(i, j, Color::Transparent);
}
}
//随机生成验证码 并画到图上去
for(int i = 0; i < 5;i++){
yzm = lettter[rand()%62];//生成随机字符
wchar_t toDrawText[2] = {0};
swprintf_s(toDrawText, 2, L"%s", S_CA2T(yzm.c_str()));
Gdiplus::Bitmap *sonBitmap = letterToPic(toDrawText);//画到小图上去
Gdiplus::RectF rct(24*i,0,24,40);
g.DrawImage(sonBitmap,24*i,0/*(const Gdiplus::RectF)rct*/);//把小图画到大图上
}
// 干扰线
Color lineColor(colors[rand() % colors.size()]); //随机颜色
for (int i = 0; i < 3; i++){
PointF p1(RangedRand(0, bmp.GetWidth()), RangedRand(0, bmp.GetHeight()));
PointF p2(RangedRand(0, bmp.GetWidth()), RangedRand(0, bmp.GetHeight()));
g.DrawLine(&Gdiplus::Pen(lineColor), p1, p2);
}
Color lineColor2(colors[rand() % colors.size()]);
for (int i = 0; i < 3; i++){
PointF p1(RangedRand(0, bmp.GetWidth()), RangedRand(0, bmp.GetHeight()));
PointF p2(RangedRand(0, bmp.GetWidth()), RangedRand(0, bmp.GetHeight()));
g.DrawLine(&Gdiplus::Pen(lineColor2), p1, p2);
}
// 干扰点
for (int i = 0; i < 50; i++)
{
PointF p(RangedRand(0, bmp.GetWidth()), RangedRand(0, bmp.GetHeight()));
bmp.SetPixel(p.X, p.Y, colors[rand() % colors.size()]);
}
IStream* pIStream = NULL;
CreateStreamOnHGlobal(NULL, TRUE, &pIStream);
std::vector<BYTE> data;
CLSID clsID = GUID_NULL;
wchar_t ext[_MAX_EXT] = {0};
wchar_t format[_MAX_EXT] = {L"image/"};
std::wstring strPicPath = L".\\uires\\image\\yanzhengma.png";//图片生成路径
_wsplitpath_s(strPicPath.c_str(), NULL, 0, NULL, 0, NULL, 0, ext, _MAX_EXT);
wcscat_s(format, _MAX_EXT, ext + 1);
if (GetEncoderClsid2(format, &clsID))
{
Gdiplus::Status status = bmp.Save(pIStream, &clsID);
if (status != Gdiplus::Status::Ok)
return ;
HGLOBAL hg = NULL;
GetHGlobalFromStream(pIStream, &hg);
int bufSize = GlobalSize(hg);
data.resize(bufSize);
LPVOID pImage = GlobalLock(hg);
memcpy(&data[0], pImage, bufSize);
GlobalUnlock(hg);
pIStream->Release();
//printf("success\n");
bmp.Save(strPicPath.c_str(), &clsID, NULL);//保存到png
}
else{
printf("failed\n");
}
}
在img控件上绘制:
img控件好像不能直接通过点击绑定事件。(这样:EVENT_NAME_COMMAND(L"code", OnYanzhengma))
这里通过鼠标按下点击事件实现。(MSG_WM_LBUTTONDOWN(OnClick))
void CMainDlg::OnClick(UINT nHitTest, CPoint point)
{
CRect codeRect;
SImageWnd *img = FindChildByName2<SImageWnd>("code");
if(img){
codeRect = img->GetWindowRect();
}
//判断点击区域是否是图片区域
if (codeRect.PtInRect(point)) {
createCodePic();
SImageWnd *img = FindChildByName2<SImageWnd>("code");
//加载新生成的验证码图片到控件上
SStringT location_imgpath = L".\\uires\\image\\yanzhengma.png";
IBitmap *bmp1 = SResLoadFromFile::LoadImage(location_imgpath);
img->SetImage(bmp1);
}
SetMsgHandled(FALSE);
}
xml:
<img pos="20,50" name="code" skin="skin_yzm"/>
运行结果:注意:点击切换验证码的时候,点快了没反应(暂未解决)
参考:
文字变平滑:https://www.iteye.com/blog/andylin02-642688
生成验证码图片:https://blog.csdn.net/wutaozhao/article/details/109244892
文字旋转:https://blog.csdn.net/weixin_34238633/article/details/85583065?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.vipsorttest&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.vipsorttest
代码地址:https://github.com/xiongjie-1/my.git
(第一次用git,还不太熟悉)
注:
1、需要soui环境
2、代码工程路径不知道是不是绝对路径,忘了,如果是绝对路径需要改一下路径