【功能实现】通过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、代码工程路径不知道是不是绝对路径,忘了,如果是绝对路径需要改一下路径

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容