GDI映射模式

最近在学Win32的编程,看的是《Windows程序设计第5版》一书,这本书是台湾人翻译的,有些译法和大陆不一样,书中还有一些错误的地方,很多时候需要中英文对照阅读,下载请点击

TextOut(hdc,100,100,TEXT(“Love China”),10)

这句GDI函数的作用是在坐标点(100,100)的位置输出一个“Love
China”字符串:

图片 1

GDI函数作为硬件设备(显示器,打印机)的接口封装中间层,其传入的参数坐标值(100,100)要在设备上哪个位置显示,是通过GDI映射方式定义的!也就是说:

(100,100)
—————-通过方式A映射之后,可以变成—————-》(1,1)

(100,100)
—————-通过方式B映射之后,可以变成—————-》(10,1)

(100,100)
—————-通过方式C映射之后,可以变成—————-》(1,3)

(100,100)
—————-通过方式C映射之后,可以变成—————-》(5,1)

左边这一列的坐标(100,100)就是一个 style=”color: #9b00d3; font-size: large;”>逻辑坐标(所有GDI函数都是)

右边这一列的坐标(1,1)就是一个 style=”color: #9b00d3; font-size: large;”>设备坐标

style=”color: #ff0000;”>所谓映射,就是将一个值通过一个数学公式变成另外一个值

图片 2

1、概述

以下是WINDOWS定义的各种映射模式

GDI 映射模式

        GDI在全称是Graphics Device
Interface,即图形设备接口。是图形显示与实际物理设备之间的桥梁。GDI接口是基于函数,虽然使程序员省力不少,但是编程方式依然显得麻烦。例如显示一张位图,我们需要进行“创建位图,读取位图文件信息,启用场景设备,调色板变化“等一系列操作。然而有了GDI+,繁琐的步骤再次被简化。顾名思义,GDI+就是GDI的增强版,它是微软在Windows
2000以后操作系统中提供的新接口。

好了,下面开始正文:

style=”color: #666666;”>“在默认情况下,WINDOWS的坐标原点在(0,0)点,但是可以通过调用SetWindowOrgEx来改变逻辑坐标点的原点,通过SetViewportOrgEx来改变设备坐标的原点;如果传入的参数有负号,还能够改变对应轴的正方向;X轴的正方向指向屏幕的右侧,不同的映射模式Y轴正方向不一样”

MM_TEXT                      一个逻辑单位映射成一个像素     
Y:指向屏幕下侧

MM_LOMETRIC            
一个逻辑单位映射成0.1mm        Y:(以下都指向上侧)

MM_HIMETRIC             
一个逻辑单位映射成0.01mm              

MM_LOENGHLISH        
一个逻辑单位映射成0.01英寸

MM_HIENGLISH           
一个逻辑单位映射成0.001英寸

MM_TWIPS                  
一个逻辑单位映射成1/1440英寸

style=”background-color: #000000; color: #ffffff;”>MM_ISOTROPIC            
一个逻辑单位映射成自定义的设备单位

style=”background-color: #000000; color: #ffffff;”>MM_ANISOTROPIC       
一个逻辑单位映射成自定义的设备单位

style=”background-color: #ffffff; color: #000000;”>“黑色背景的映射方式能改变逻辑坐标(也叫窗口)或者设备坐标(也叫视口)的范围

 
不同之处:

style=”background-color: #000000; color: #ffffff;”> style=”background-color: #ffffff; color: #000000;”>MM_ISOTROPIC在设置范围时WINDOWS将自动调整使得, style=”color: #ff0000;”>逻辑坐标的X与设备坐标的X之比=逻辑坐标的Y与设备坐标的Y之比

style=”background-color: #000000; color: #ffffff;”> style=”background-color: #ffffff; color: #000000;”>MM_ANISOTROPIC
不会调整 style=”background-color: #ffffff; color: #000000;”>”

MM_TEXT  1逻辑单位 = 1像素

2、GDI+主要功能         GDI+主要提供以下三种功能:         (1)
二维矢量图形:GDI+提供了存储图形基元自身信息的类(或结构体)、存储图形基元绘制方式信息的类以及实际进行绘制的类;

在看到GDI(GDI Graphic Device
Interface图形设备接口)
映射方式这一节的时候,书中又是逻辑坐标,又是设备坐标,又是视口,窗口,又是视埠什么的,搞得人头都大了。虽然我现在还没有完全读懂,但是我感觉我已经抓住了理解这些东西的主线,下面的东西就当作我的笔记吧:

图片 3

MM_LOMETRIC  1逻辑单位 = 0.1mm

        (2)
图像处理:大多数图片都难以划定为直线和曲线的集合,无法使用二维矢量图形方式进行处理。因此,GDI+为我们提供了Bitmap、Image等类,它们可用于显示、操作和保存BMP、JPG、GIF等图像格式。

1.逻辑坐标和设备坐标

要在距离窗口左边距100像素,上边距100像素的位置写出一个I love you,
China可以使用如下提供的各种

Setmapmode(dc,MM_TEXT);
SetViewportorgEx(dc,100,100,nil);
逻辑坐标(0,0)对应设备坐标(100,100)

textout(dc,0,0,’I love you, China’,17);

图片 4

 

Setmapmode(dc,MM_TEXT);
SetWindowOrgEx(dc,100,100,nil);

逻辑坐标(100,100)对应设备坐标(0,0)
textout(dc,200,200,’I love you, China’,17);

 

Setmapmode(dc,MM_LOMETRIC);
textout(dc,356,-356,’I love you, China’,17);

 

Setmapmode(dc,MM_HIMETRIC);
textout(dc,3560,-3560,’I love you, China’,17);

Setmapmode(dc,MM_LOENGLISH);
textout(dc,140,-140,’I love you, China’,17);

Setmapmode(dc,MM_ISOTROPIC);
SetwindowExtEx(dc,getclientRect.Right,getclientrect.Bottom,nil);
SetViewportExtEx(dc,getclientRect.Right,-getclientrect.Bottom,nil);
textout(dc,100,100,’I love you, China’,17);

 

Setmapmode(dc,MM_ANISOTROPIC);
SetwindowExtEx(dc,getclientRect.Right,getclientrect.Bottom,nil);
SetViewportExtEx(dc,getclientRect.Right,getclientrect.Bottom,nil);
textout(dc,100,100,’I love you, China’,17);

MM_HIMETRIC  1逻辑单位 = 0.01mm

        (3) 文字显示:GDI+支持使用各种字体、字号和样式来显示文本。
        相比于GDI,GDI+是基于C++类的对象化的应用程序接口,因此用起来更为简单。GDI的核心是设备上下文,GDI函数都依赖于设备上下文句柄,其编程方式是基于句柄的;GDI+无需时刻依赖于句柄或设备上下文,用户只需创建一个Graphics
对象,就可以用面向对象的方式调用其成员函数进行图形操作,编程方式是基于对象的。

 
 首先,逻辑坐标这个名词就让很多人望而却步,确实,不能“望文生义”地理解的翻译就不是好翻译 
 ——鲁迅。哈哈,开个玩笑,我们要理解这两个东西,首先要想到如果你要用Win32要绘制一个东西,该怎么做呢?比如绘制一个矩形,假设我们调用的是Rectangle(hdc,30,20,50,80),(这个函数的用法是Rectangle(hdc,left,top,right,bottom),我叫雷锋,不用谢我)。可以看到,跟很多GDI函数一样,这个函数里面使用了很多数字,坐标。让我们回忆一下小学知识,绘制一个东西,不仅应当搞清楚他的长度,还应该搞清楚他的单位,那么这里的30,20,50,80的单位是什么呢?很多人会说,是像素!这个答案是对的,但是又不全对。事实上,Windows默认的映射方式(Mapping
Mode,简称就是MM)是MM_TEXT,在MM_TEXT映射方式(TEXT实际上跟文字没有多大关系,是这种映射方式下的坐标方向,从左到右,从上到下,跟文字阅读方式一样)下,这个单位确实是像素。实际上,逻辑坐标和设备坐标的区别就在于他们的单位不一样!

MM_LOENGLISH  1逻辑单位 = 0.01 in =
0.254mm

3、GDI绘制实例
        GDI在使用设备上下文绘制线条之前,必须先调用SelectObject
以使笔对象和设备上下文关联。其后,在设备上下文中绘制的所有线条均使用该笔,直到选择另一支不同的笔为止。
        使用GDI画线代码如下

下面我们拿出一个公式

MM_HIENGLISH  1逻辑单位 = 0.001 in =
0.0254mm

// TODO: Add your command handler code here
   CClientDC clientDC;  //目标DC     
   CPen pen (PS_SOLID, 1, RGB(0, 0, 255));
   clientDC.SelectObject(pen.GetSafeHandle());
    //开始绘制
   clientDC.MoveTo(0, 0)
   clientDC.LineTo(rect.right, 0);
   clientDC.SelectObject(oldObject);

         
 图片 5

MM_TWIPS  1逻辑单位 = 1/1440 in =
0.0176mm

        从上述代码可以看出:在GDI编程中,几乎所有的操作都围绕设备上下文dc展开。的确,这正是GDI编程的特点!设备上下文是
Windows
使用的一种结构,所有GDI操作前都需取得特定设备的上下文,函数中的CClientDC
dc (this) 语句完成这一功能。
利用GDI进行图形、图像处理的一般操作步骤为:1. 取得指定窗口的DC。2.
确定使用的坐标系及映射方式。3. 进行图形、图像或文字处理。4.
释放所使用的DC。但是,在GDI+中,只需将Pen对象直接作为参数传递给Graphics类的DrawLine等方法即可,而不必使Pen对象与
Graphics对象关联。 4、GDI+绘制实例         使用GDI+画线代码如下  

要讲上面的公式,就要先说一下视口(Viewport 台湾译作视埠)和窗口(Window
台湾译作视窗)

默认映射模式:MM_TEXT

// TODO: Add your command handler code here 
 CClientDC clientDC (this); 
 //创建Graphics对象
 Graphics graphics(clientDC);
 //创建pen
 Pen myPen;
 myPen.SetWidth(1);
 //画X轴
 myPen.SetColor(Color::Blue);
 graphics.DrawLine(&myPen, 0, 0, rect.right, 0);

首先,不要被这两个名字迷惑了,这两个坐标是跟映射有关的,跟屏幕坐标系,窗口坐标系,客户端坐标系是相对独立的两个知识。

图片 6

        (1)创建 Graphics 对象:Graphics
对象表示GDI+绘图表面,是用于创建图形图像的对象。         (2)使用
Graphics 对象绘制线条和形状、呈现文本或显示与操作图像。
        GDI+的相对与GDI而言,新增了一系列功能:渐变的画刷(Gradient
Brushes)、基数样条函数(Cardinal Splines)、持久的路径对象(Persistent Path
Objects)、变形和矩阵对象(Transformations &Matrix
Object)、可伸缩区域(Scalable Regions)、Alpha混合(Alpha
Blending)和丰富的图像格式支持等。下面,我们来逐个用实际代码实现GDI+的新增功能。
    4.1渐变的画刷
        (GDI+提供了用于填充图形、路径和区域的线性渐变画刷和路径渐变画刷。线性渐变画刷使用渐变颜色来填充图形。当用路径渐变画刷填充图形时,可指定从图形的一部分移至另一部分时画刷颜色的变化方式。例如,我们可以只指定图形的中心颜色和边缘颜色,当画刷从图形中间向外边缘移动时,画刷会逐渐从中心颜色变化到边缘颜色。
)  

其实公式拿出来,学数学的小伙伴是不是就懂了大半了,这个公式非常重要,理解了这个公式,后面的很多东西就能理解,首先,公式中的Window,WinOrg,WinExt,就是带了Win的东西,就是使用的逻辑坐标的值,就跟GDI函数中的一样,逻辑坐标的单位可能是像素(MM_TEXT映射)、毫米(单位是0.1mm,在MM_LOMETRIC映射下)等等等等(看下图).

MM_LOMETRIC

// TODO: Add your command handler code here
CClientDC clientDC (this);
CRect rect;
GetClientRect(&rect);
//创建Graphics对象
Graphics graphics(clientDC);
//创建渐变画刷
LinearGradientBrush lgb(Point(0, 0), Point(rect.right, rect.bottom), Color::Blue, Color::Green);
//填充
graphics.FillRectangle(&lgb, 0, 0, rect.right, rect.bottom);

图片 7

MM_HIMETRIC

    4.2基数样条函数
        (基数样条指的是一连串单独的曲线,这些曲线连接起来形成一条较大的曲线。样条由点(Point结构体)的数组指定,并通过该数组中的每一个点。基数样条平滑地穿过数组中的每一个点(不出现尖角),因此比用直线连接创建的路径精确。)
 

就是说我们在调用Win32函数绘图的时候,要知道自己使用的单位(根据映射模式确定的)。因为绘图函数里的数值,使用的就是这些单位,虽然默认的MM_TEXT映射模式使用的单位就是像素,但是很多时候其他单位也很有用,比如你要做一个屏幕尺子的时候,你要用尺子量一下物体有几厘米。尺子上的刻度就可以用其他的映射模式来画。但是屏幕在显示的时候却不能只知道逻辑坐标几厘米啊,屏幕得知道具体的像素位置才行啊!那这个时候,就需要用到上面的公式转换了。讲到这里,公式里的ViewExt/WinExt是什么意思就很明显了。那就是在当前逻辑坐标系下(比如几厘米,打比方哈),实际上是上面映射模式表格里的单位)对应的设备坐标应该是多少个像素!这样转换过后,得到实际的Viewport,就是该逻辑点在屏幕上的位置。

MM_LOENGLISH

   // TODO: Add your command handler code here
 CClientDC clientDC (this);
 //创建Graphics对象
 Graphics graphics(clientDC);
 Point points[] =
 {
  Point(0, 0), Point(100, 200), Point(200, 0), Point(300, 200), Point(400, 00)
 };
 //直接画线
 for (int i = 0; i < 4; i++)
 {
  graphics.DrawLine(&Pen(Color::Blue, 3), points[i], points[i + 1]);
 }
 //利用基数样条画线
 graphics.DrawCurve(&Pen(Color::Red, 3), points, 5);

所以说视口和窗口实际上是表示的同一块区域,只不过是因为单位和原点的不同,需要进行映射,逻辑单位就是窗口,就是Window,就是像素,毫米,英寸,就是给人用的单位,就是设备无关的单位,设备单位就是视口,就是Viewport,就只能是像素,就是给设备用的单位,确定的一厘米,在不同的设备上的像素数可能会有区别,所以是设备相关的单位。

MM_HIENGLISH

    4.3变形和矩阵对象
        (GDI+提供了Matrix对象,它是一种可以使变形(旋转、平移、缩放等)
简易灵活的强大工具,Matrix对象需与要被变形的对象联合使用。对于GraphicsPath类,我们可以使用其成员函数Transform接收
Matrix参数用于变形。)  

 

MM_TWIPS

// TODO: Add your command handler code here
 CClientDC clientDC (this);
 //创建Graphics对象
 Graphics graphics(clientDC);
 GraphicsPath path;
 path.AddRectangle(Rect(250, 20, 70, 70));
 graphics.DrawPath(&Pen(Color::Black, 1), &path); // 在应用变形矩阵之前绘制矩形
 // 路径变形
 Matrix matrix1, matrix2;

 matrix1.Rotate(45.0f); //旋转顺时针45度
 path.Transform(&matrix1); //应用变形
 graphics.DrawPath(&Pen(Color::Red, 3), &path);

 matrix2.Scale(1.0f, 0.5f); //转化成为平行四边形法则
 path.Transform(&matrix2); //应用变形
 graphics.DrawPath(&Pen(Color::Blue, 3), &path);

要注意的是在上述的5映射模式下,ViewExt/WinExt的比例都是已经确定了,不能更改的,如果要更改两个值,只能在MM_ISOTROPIC和MM_ANISOTROPIC映射模式下使用SetViewportExtEx()和SetWindowExtEx()更改。而且这两个函数在上面5种映射方式下无效。

除MM_TEXT外的默认坐标轴

    4.4丰富的图像格式支持         (GDI +提供了Image、Bitmap 和Metafile
类,方便用户进行图像格式的加载、操作和保存。GDI+支持的图像格式有BMP、GIF、JPEG、EXIF、PNG、TIFF、ICON、WMF、
EMF等,几乎涵盖了所有的常用图像格式。)  

最后,这些映射模式,视口原点(ViewOrg),窗口原点(WinOrg)等,都是设备内容(DC
Device
Context,又译作装置内容、设备上下文等)的属性,设备内容其实就是你绘制的区域。有三种BeginPaint(在WM_PAINT时绘制无效区域),GetDC是客户端区域(在更新时绘制整个客户区域),GetWindowsDC是整个窗口区域(可以绘制包括窗口标题栏,菜单栏区域)。这些知识细讲的话又是另一篇博客了

 

 

 

图片 8

from:

 

1 case WM_PAINT:
2         hdc = BeginPaint(hWnd, &ps);
3         // TODO:  在此添加任意绘图代码...
4         SetMapMode(hdc, MM_LOMETRIC);
5         Rectangle(hdc, 100, -100, 200, -200);
6         SetMapMode(hdc, MM_TEXT);  //使用完后改回默认映射模式
7         EndPaint(hWnd, &ps);
8         break;

所以只要牢记开头的公式,得到正确的对应的参数,就可以画出需要的图形。

 

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注