【功能实现】通过GDI+绘制验证码图片,并显示在soui控件上

通过GDI+绘制验证码图片(png)

要求:绘制如图的验证码:
image.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"/>

运行结果:
image.png

注意:点击切换验证码的时候,点快了没反应(暂未解决)

参考:
文字变平滑: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、代码工程路径不知道是不是绝对路径,忘了,如果是绝对路径需要改一下路径

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容