从零开始学习GDI+ (二) 基本概念与基本操做

 

       从零开始学习GDI+ (一)个人第一个GDI+程序html

       上文给新手学习GDI+讲述了vs环境等的准备工做,而且能够直接用GDI+绘图了。本文开始,讲述的可能偏理论,建议学习的过程当中大胆尝试,多使用API。git

       首先上官方文档https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-gdi-startgithub

       官方文档是最权威与第一手(固然有时候有错误)的,其余人的说法通过本身的加工,增长了解释,也会带来错误的风险。英文能力强,强烈建议经过官网算法

学习与尝试。编程

         

         GDI+的新特性。windows

        一、图像(Graphics)对象 与画图工具(如Pen、Brush、GraphicsPath、Font、Image)分离,这与GDI中须要将画图工具导入DC中彻底不一样。所以GDI也dom

称为状态模型编程,而GDI+则称为非状态模型编程。松耦合老是易于方便拓展。在咱们这个例子中就是绘图更自由了,调整画笔时,再也不须要频繁取出dc与存入dc了。函数

        二、多函数重载。以DrawLine为例,能够传入Point,也能够传入int,方便不一样场景使用不一样的API。而GDI则比较固定。工具

 

         三、当前位置,GDI讲究当前的绘图点,模拟一我的画画的全过程。如画一条线的话,须要调用MoveToEx(该函数甚至返回以前的点),再调用LineTo学习

               而GDI+则无当前位置的概念,讲究的是绘制过程。DrawLine传入的起始点与结束点即可以划线。

         四、绘制与填充,GDI中Rectangle(矩形)直接使用dc的画笔画边界(border),用dc的画刷填充。而GDI+则分红了两个部分DrawRectangle与FillRectangle。

         五、区域的操做,GDI提供的区域函数比较简单 CreateRoundRectRgn、CreatePolygonRgn、CreateEllipseRgn等。GDI+不提供相似函数,经过Region维护,并提供了

Intersect,Union,Xor,Exclude,Complement,Translate等功能函数来构建复杂地区域。

 

        GDI+的使用:

        咱们通常在main函数入口附近进行初始化GdiplusStartup ,并在程序结束前进行资源回收GdiplusShutdown。若是不进行初始化,任何使用GDI+编译不会报错,甚至也能运行,

但如你所见,UI全是空的,由于GDI+对象没法正常工做。

 

        GDI+的基本操做:

        一、重要的Graphics对象。

        

         Graphics是GDI+的核心,他的构造函数的参数解释下。

         (绘图本质是利用绘图工具在绘图平面上作画,如下我把绘图平面称为【画布】)

         hdevice:设备句柄,此时画布如打印机

         hwnd:窗口句柄,此时画布是窗口

         image:图像对象,此时画布是图片(没错,图片也能够做画,画完后保存的话图片变了)

         hdc:设备上下文句柄,此时画布要根据上下文才能知道

         icm:是否使用色彩配置文件校订色彩。

         总之,Graphics能够在应用程序窗口、图片、打印机、绘图仪、传真机等等做画!

         动手试一试吧:

         1)写一个在打印机做画的函数 注意须要 #include <commdlg.h>

void OnPrintOut() { //要打印的文档信息
 DOCINFO docInfo; ZeroMemory(&docInfo, sizeof(docInfo)); docInfo.cbSize = sizeof(docInfo); docInfo.lpszDocName = _T("TestPrint"); PRINTDLG printDlg; ZeroMemory(&printDlg, sizeof(printDlg)); printDlg.lStructSize = sizeof(printDlg); printDlg.Flags = PD_RETURNDC; if (PrintDlg(&printDlg)) { StartDoc(printDlg.hDC, &docInfo); StartPage(printDlg.hDC); //开始在打印机上做画
 Graphics graphics(printDlg.hDC); //调试的话放到cpp目录,不然放到exe目录
        Image image(_T("test.png")); graphics.DrawImage(&image, 0, 0); Pen blue(Color(255, 0, 0, 255)); graphics.DrawRectangle(&blue, 200, 500, 200, 150); graphics.DrawEllipse(&blue, 200, 500, 200, 150); EndPage(printDlg.hDC); EndDoc(printDlg.hDC); } if (printDlg.hDevMode) { GlobalFree(printDlg.hDevMode); } if (printDlg.hDevNames) { GlobalFree(printDlg.hDevNames); } if (printDlg.hDC) { DeleteDC(printDlg.hDC); } }

          2)在菜单功能,把“关于”菜单项的功能注释掉,改为咱们的功能

         3)编译运行看看,若是有打印机,看看打印出来的是不是你画的呢?

 

          二、画基本图形

           GDI+的默认的坐标系的原点位于画布的左上角,x轴向右,y轴向下。默认的单位是像素。(让我想起了高DPI的恐慌,目前网易云信也是不支持动态变化的,但重启会生效。还得抽时间攻克下。)注意:不一样的画布的像素的大小不必定同样,更改了分辨率也会影响像素。好比从480*720 变到920*1440,明显程序变小了。

           1)画直线

           一条直线先前已经玩过了,这里补充下一次画多根线。

           

void GDIPlusDrawLines(HDC hdc) { Graphics graphics(hdc); Pen green(Color(255, 0, 255, 0), 3); PointF p1(10, 10); PointF p2(10, 100); PointF p3(50, 50); PointF p4(10, 10); PointF point[] = { p1, p2, p3, p4 }; graphics.DrawLines(&green, point, sizeof(point) / sizeof(point[0])); }

 

       二、画矩形,以前也玩过了,GDI+支持一次性画多个矩形,试试吧。

     

void GDIPlusDrawRectangles(HDC hdc) { Graphics graphics(hdc); Pen blue(Color(255, 0, 255, 0), 3); RectF r1(10,10,100,50); RectF r2(40,40,100,50); RectF r3(80,40,50,100); RectF rs[] = { r1, r2, r3}; graphics.DrawRectangles(&blue, rs, sizeof(rs) / sizeof(rs[0])); }

      

 

     三、画曲线

      DrawCurve、DrawClosedCurve(闭合曲线)、DrawBezier(贝塞尔曲线)

      额,发现每一个函数都写demo比较费时且无聊,你们又不必定跟着尝试,增长点乐趣吧,点的位置随机,矩阵的位置随机。

      1) 包含下头文件 <time.h> 和<stdlib.h>,using namespace std;

      2)  初始化指定下随机数种子

           

     //初始化种子
    srand((unsigned)time(NULL));

 

      3)写随机函数

Point GetRandomPoint(int xmax, int ymax) { Point t; t.X = rand() % xmax; t.Y = rand() % ymax; return t; } Rect GetRandomRect(int xmax, int ymax) { Rect t; Point t1 = GetRandomPoint(xmax, ymax); Point t2 = GetRandomPoint(xmax, ymax); if (t1.X < t2.X) { t.X = t1.X; t.Width = t2.X - t1.X; if (t1.Y<t2.Y) { t.Y = t1.Y; t.Height = t2.Y - t1.Y; } else { t.Y = t2.Y; t.Height = t1.Y - t2.Y; } } else { t.X = t2.X; t.Width = t1.X - t2.X; if (t1.Y < t2.Y) { t.Y = t1.Y; t.Height = t2.Y - t1.Y; } else { t.Y = t2.Y; t.Height = t1.Y - t2.Y; } } return t; }

 

      4)描点函数

   

//描点
void DrawEllipsePoint(HDC hdc, Point t) { Graphics graphics(hdc); SolidBrush redbursh(Color::Red); graphics.FillEllipse(&redbursh, t.X-5, t.Y-5, 10, 10); }

 

      5)开始做画

  

void DrawCurves(HDC hdc, int xmax, int ymax) { Graphics graphics(hdc); Point t[] = { GetRandomPoint(xmax, ymax), GetRandomPoint(xmax, ymax), GetRandomPoint(xmax, ymax), GetRandomPoint(xmax, ymax) }; int t_size = sizeof(t) / sizeof(t[0]); //画曲线
    Pen green(Color::Green, 3); graphics.DrawCurve(&green, t, t_size); //增长弯曲程度
    Pen blue(Color::Blue, 3); graphics.DrawCurve(&blue, t, t_size, 1.3f); //画闭合曲线
    Pen gray(Color::Gray, 3); graphics.DrawClosedCurve(&gray, t, t_size); //画贝塞尔曲线
    Pen orange(Color::Orange, 3); graphics.DrawBezier(&orange, t[0],t[1],t[2],t[3]); for (int i = 0; i < t_size;++i) { DrawEllipsePoint(hdc, t[i]); } }

 

     

   四、画圆弧与扇形

        DrawArc 、DrawPie

     

 
 

void DrawArcPie(HDC hdc,int xmax, int ymax)
{
Graphics graphics(hdc);
Rect t = GetRandomRect(xmax, ymax);
//先画矩形边框(增长对左边的认知)
Pen black(Color::Black); //默认一像素
graphics.DrawRectangle(&black, t);
//画弧线
Pen red(Color::Red, 3);
graphics.DrawArc(&red, t, 0/*起始位置*/, 90/*须要画的弧度大小*/);
//画扇形
Pen green(Color::Green, 1);
graphics.DrawPie(&green, t, 90/*起始位置*/, 90/*须要画的弧度大小*/);
}

 

 

  五、填充区域、画刷与颜色

      FillClosedCurve(填充封闭曲线)、FillEllipse(填充椭圆)、FillPath(填充路径)

      FillPie(填充扇形)、FillPolygon(填充多边形)、FillRectangle(填充矩形)

      FillRectangles(填充巨型集)、FillRegion(填充区域)。

      GDI+使用画刷来填充的:单色画刷、影线画刷、纹理画刷、线性渐变画刷与路径渐变画刷(画刷之后详细展开)

      咱们以前已经接触过颜色了,Color,由argb组成。a是alpha色彩的透明度、r是red红色、g是green绿色、b是blue蓝色。GDI+对透明度的支持是基于如下算法:output = foreground*alpha/255 + background*(255-alpha)/255。(之后详细展开)

      下面,咱们来填充一个正弦图形试试

      

//用半透明蓝色填充 填充sinx 与 x轴的区域
void FillSinRegion(HDC hdc, int xmax, int ymax) { const REAL Pi = 3.1415926; //从0 到 2pi //计算1弧度=多少像素,从50px ->xmax-50px 画
    REAL perX = (xmax - 100)*1.0 / (2 * Pi); //众项长度单位1=多少像素
    REAL perY = (ymax - 100)*1.0/2; //画点,理论上越多越精确,咱们取500+1点。你们能够试试更多
    const int counts = 500; Point t[counts*2]; t[0].X = 50; t[0].Y = ymax / 2; //步长
    REAL stepX = (2 * Pi) / counts; REAL step = stepX*perX ; //计算正弦值
    for (int i = 1; i < counts ; i++) { t[i].X = t[i - 1].X + step; REAL v = sin(i*stepX); t[i].Y = ymax / 2 - v*perY; } //画x轴
    t[counts].X = t[counts - 1].X; t[counts].Y = ymax / 2; for (int i = 1; i < counts; i++) { t[counts + i].X = t[counts + i - 1].X - step; t[counts + i].Y = ymax / 2; } Graphics graphics(hdc); SolidBrush blue(Color(255 / 2, 0, 0, 255)); Pen r(Color::Red); graphics.DrawPolygon(&r, t, counts*2); graphics.FillClosedCurve(&blue,t,counts*2); }

 

      

 六、输出问题

      DrawString

     

void  PrintText(HDC hdc, int xmax, int ymax) { TCHAR s[32]; _tcscpy_s(s, _T("Hello GDI+")); RectF t(10,10,200,50); Font f(_T("Arial"), 26); StringFormat Fmt; Fmt.SetAlignment(StringAlignmentCenter); Fmt.SetLineAlignment(StringAlignmentCenter); SolidBrush red(Color::Red); Graphics graphics(hdc); graphics.DrawString(s, _tcslen(s), &f, t, &Fmt, &red); Pen black(Color::Black); graphics.DrawRectangle(&black, t); }

 

 

  本文全部源码见:https://github.com/xuhuajie-NetEase/GDI-Study

相关文章
相关标签/搜索