在C#中使⽤GDI的简单总结
在C#中使⽤GDI的简单总结
在C#默认⽀持的是GDI+库,使⽤GDI+库,有很丰富的函数和排版⼿段,可以满⾜⼤部分的要求.除⾮,你需要使⽤bitmap字体,GDI+对于字体的⽀持有很⼤限制,⽀持truetype字体,⽽对于点阵字体(栅格字体)则不再⽀持,但是很多字体都是这种点阵字体,这样就带来⼀个问题,使⽤不了了.⽽很多公司则会⾃⼰制作某些⽤途的字体,⽐如说在LED显⽰屏上使⽤,这个时候使⽤GDI+则不再是⼀个明智的选择了.此外GDI+虽然强⼤,但是经过测试发现效率却是低下,速度⽐GDI慢了不少.
但是CS的界⾯开发环境和编码习惯⼜很适合,所以⽐较折衷的解决⽅法是使⽤C#调⽤系统的gdi api函数实现编程.
⾸先C#环境中的color结构是AARRGGBB,⽽在win32的颜⾊结构却是AABBGGRR,所以如果不注意则会使到颜⾊出现偏差或者逆转.在环境的类库当中,在system.drawing的命名空间中有ColorTranslator.ToWin32()这个函数,来对color结构转换成win32的api使⽤的颜⾊结构.
GDI中⽐较重要的函数,gdi函数基本都是来之⼀个dll. Gdi32.dll
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("GDI32.dll")]
public static extern bool DeleteObject(IntPtr objectHandle);
[DllImport("gdi32.dll")]
public static extern bool FillRgn(IntPtr hdc, IntPtr hrgn, IntPtr hbr);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect,
int nBottomRect);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateSolidBrush(Int32 crColor);
[DllImport("gdi32.dll")]
public static extern int SetBkMode(IntPtr hdc, int iBkMode);
public const int TRANSPARENT = 1;
public const int OPAQUE = 2;
[DllImport("gdi32.dll")]
static extern uint SetBkColor(IntPtr hdc, int crColor);
[DllImport("gdi32.dll")]
static extern uint SetTextColor(IntPtr hdc, int crColor);
[DllImport("gdi32", EntryPoint = "CreateFontW", CharSet = CharSet.Auto)]
static extern IntPtr CreateFontW(
[In] Int32 nHeight,
[In] Int32 nWidth,
[In] Int32 nEscapement,
[In] Int32 nOrientation,
[In] FontWeight fnWeight,
[In] Boolean fdwItalic,
[In] Boolean fdwUnderline,
[In] Boolean fdwStrikeOut,
[In] FontCharSet fdwCharSet,
[In] FontPrecision fdwOutputPrecision,
[In] FontClipPrecision fdwClipPrecision,
[In] FontQuality fdwQuality,
[In] FontPitchAndFamily fdwPitchAndFamily,
[In] String lpszFace);
[DllImport("gdi32.dll")]
public static extern int GetTextFace(IntPtr hdc, int nCount,
[Out] StringBuilder lpFaceName);
public const Int32 LF_FACESIZE = 32;
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern bool BitBlt(
IntPtr hdcDest, // ⽬标设备的句柄
int nXDest, // ⽬标对象的左上⾓的X坐标
int nYDest, // ⽬标对象的左上⾓的Y坐标
int nWidth, // ⽬标对象的矩形的宽度
int nHeight, // ⽬标对象的矩形的长度
IntPtr hdcSrc, // 源设备的句柄
int nXSrc, // 源对象的左上⾓的X坐标
int nYSrc, // 源对象的左上⾓的X坐标
TernaryRasterOperations dwRop // 光栅的操作值
);
[DllImport("gdi32.dll")]
public static extern bool StretchBlt(IntPtr hdcDest, int nXOriginDest, int nYOriginDest,
int nWidthDest, int nHeightDest,
IntPtr hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc,
TernaryRasterOperations dwRop);
[DllImport("gdi32.dll", CharSet = CharSet.Auto)]
public static extern bool GetTextExtentPoint(IntPtr hdc, string lpString,
int cbString, ref Size lpSize);
[DllImport("Gdi32.dll", CharSet = CharSet.Auto)]
public static extern bool GetTextMetrics(IntPtr hdc, out TEXTMETRIC lptm);
[DllImport("gdi32.dll")]
public static extern bool GetCharABCWidthsFloatW(IntPtr hdc, uint iFirstChar, uint iLastChar, [Out] ABCFloat[] lpABCF);
[DllImport("gdi32.dll", CharSet = CharSet.Auto)]
public static extern bool TextOutW(IntPtr hdc, int nXStart, int nYStart,
string lpString, int cbString);
[DllImport("gdi32.dll", CharSet = CharSet.Auto)]
public static extern bool GetCharWidth32(IntPtr hdc, uint iFirstChar, uint iLastChar,
[Out] int[] lpBuffer);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int DrawText(IntPtr hdc, string lpStr, int nCount, ref Rect lpRect, dwDTFormat wFormat);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
static extern bool DeleteDC(IntPtr hdc);
这些都是在GDI中⽐较常⽤的函数,其中SelectObject,和DeleteObject更是关键,在GDI的使⽤中最容易出现的问题就是内存泄漏,很多时候是因为没有正确释放资源引起的.所以需要特别⼩⼼,在GDI中释放资源使⽤DeleteObject这个函数来释放.
fontweight几百正常下⾯来实现⼀些具体的函数
///
/// 填充特定DC的⼀个区域的特定颜⾊
///
/// 给定DC
/// 给定区域
/// 给定颜⾊
public static void FillRect(IntPtr hdc, Rectangle Rect, Color FillColor)
{
IntPtr fillBrush = CreateSolidBrush(ColorTranslator.ToWin32(FillColor));
IntPtr rectR = CreateRectRgn(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
FillRgn(hdc, rectR, fillBrush);
DeleteObject(rectR);
DeleteObject(fillBrush);
}
这个函数实现对⼀个区域填充⼀个颜⾊, 当中创建了画笔,创建了区域,然后最后,当然还要记得释放两者占⽤的资源.创建字体函数
public static IntPtr CreatFont(String FontName, Int32 Height, FontStyle Style)
{
IntPtr Result;// = IntPtr.Zero;
FontWeight boldWeight = FontWeight.FW_NORMAL;
Boolean Italic = false;
Boolean Underline = false;
Boolean Bold = false;
if ((Style & FontStyle.Bold) != 0)
{
Bold = true;
}
if ((Style & FontStyle.Italic) != 0)
{
Italic = true;
}
if ((Style & FontStyle.Underline) != 0)
{
Underline = true;
}
if (Bold)
{
boldWeight = FontWeight.FW_BOLD;
}
Result = CreateFontW(Height, 0, 0, 0, boldWeight, Italic, Underline, false,
FontCharSet.DEFAULT_CHARSET, FontPrecision.OUT_DEFAULT_PRECIS,
FontClipPrecision.CLIP_DEFAULT_PRECIS, FontQuality.DRAFT_QUALITY,
FontPitchAndFamily.DEFAULT_PITCH, FontName);
return Result;
}
在中,默认的字体类,不⽀持点阵字体,所以要使⽤CreateFontW这个函数来创建⾃⼰的字体资源,其中⼤部分的选项都试⽤默认值即可.
然后就是设置画板字体,在GDI环境中,DC是带着字体的,⽽不像GDI+那样⼦是分离的,所以经常需要设置不同的字体
public static IntPtr SetCanvasFont(IntPtr hdc, ApiFont NewFont)
{
IntPtr FontPtr = CreatFont(NewFont.Name, NewFont.Size, NewFont.Style);
IntPtr OldPtr = SelectObject(hdc, FontPtr);
DeleteObject(OldPtr);
return OldPtr;
}
这个函数,将DC原来的字体资源释放掉,这样的实现会带来⼀个新的问题,因为⼀般来说都需要将DC原来的字体资源再通过selectobject函数放回去,然后将新的字体资源释放掉.所以这个函数是要⼩⼼使⽤的.
所以就有了第⼆个版本
public static IntPtr SetCanvasFontNotDelete( IntPtr hdc, ApiFont NewFont )
{
IntPtr FontPtr = CreatFont(NewFont.Name, NewFont.Size, NewFont.Style);
IntPtr OldPtr = SelectObject(hdc, FontPtr);
return OldPtr;
}
这样⼦就可以⼿动的释放资源,但是需要特别注意,的是⼀定要记得释放掉字体资源.
⼀般的使⽤画图的步骤
IntPtr pTarget = e.Graphics.GetHdc();
IntPtr pSource = CreateCompatibleDC(pTarget);
IntPtr pOrig = SelectObject(pSource, drawBmp.GetHbitmap());
GDIApi.StretchBlt(pTarget, 0, 0, this.Width, this.Height, pSource, 0, 0, drawWidth, drawHeight, TernaryRasterOperations.SRCCOPY);
IntPtr pNew = SelectObject(pSource, pOrig);
DeleteObject(pNew);
DeleteDC(pSource);
e.Graphics.ReleaseHdc(pTarget);
这样⼦就可以将drawBmp画到e.graphics上⾯了.最重要的是后⾯释放掉资源,否则内存的泄漏速度是很厉害的.我的软件每次重画就有7M左右的泄漏.⼀下⼦从⼗⼏M的内存上升到⼏百M的内存占⽤
[DllImport("gdi32.dll")]
public static extern int SetBkMode( IntPtr hdc, int iBkMode );
public const int TRANSPARENT = 1;
public const int OPAQUE = 2;
这个函数是设置透明度的,参数2如果为1则是透明,2则是不透明.
不透明的话,将字符串画上去的时候,会有⽩⾊的背景.⼀般来说设为透明.
获取字符或者字体的信息函数
[DllImport("gdi32.dll", CharSet = CharSet.Auto)]
public static extern bool GetTextExtentPoint( IntPtr hdc, string lpString,
int cbString, ref Size lpSize );
[DllImport("Gdi32.dll", CharSet = CharSet.Auto)]
public static extern bool GetTextMetrics( IntPtr hdc, out TEXTMETRIC lptm );
[DllImport("gdi32.dll")]
public static extern bool GetCharABCWidthsFloatW( IntPtr hdc, uint iFirstChar, uint iLastChar, [Out] ABCFloat[] lpABCF );
[DllImport("gdi32.dll", CharSet = CharSet.Auto)]
public static extern bool GetCharWidth32( IntPtr hdc, uint iFirstChar, uint iLastChar,
[Out] int[] lpBuffer );
这⼏个函数都可以获取字体或者字符串占⽤的空间⼤⼩,⽽⼜各有区别.
GetTextExtentPoint可以⽅便的获取⼀个字符串,或者字符串的部分的长度,因为可以通过cbString这个长度来控制获取的范围. GetTextMetrics则是获取⼀个字体的各种⾼度信息,包括height,ascent,descent,还包括字体能够表现的字符范围等等信息. GetCharABCWidthsFloatW则是获取某段连续字符串的abcwidth信息,abcwidth信息在某些情况下,需要特别注意,否则斜体会排版得很难看. GetCharWidth32获取⼀个连续的字符段的宽度信息, 但是根据实践,居然和GetTextExtentPoint获取的信息不⼤⼀致,暂时是少于实际占⽤的空间.使到计算出来的占⽤宽度实际上不⾜以容纳字符串的排版.
字符串的描绘
这是在gdi操作中⾮常重要的⼀部分,使⽤Gdi是因为需要使⽤特殊字体,⽽字体当然是针对字符串来使⽤的的.所以,这根本就是使⽤gdi的⽬的.
常⽤的字符串输出有来个函数
[DllImport("gdi32.dll", CharSet = CharSet.Auto)]
public static extern bool TextOutW( IntPtr hdc, int nXStart, int nYStart,
string lpString, int cbString );
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int DrawText( IntPtr hdc, string lpStr, int nCount, ref Rect lpRect, dwDTFormat wFormat );
TextOutW是⼀个⽐较简单的函数,适合⼀般的场合,只需要设置X和Y的坐标即可
DrawText,则会控制输出的空间⼤⼩,排版规则.⽐较适合需要精确控制的场所,⼜或者⽐如说输出阿
拉伯⽂字的时候,要设置为右对齐.
获取系统所有的字体
由于C#不⽀持点阵字体,所以⾃然地,使⽤提供的函数,获取的安装字体列表⾃然是不包含点阵字体的.所以并不符合我的要求.所以还得使⽤系统的api函数,来获取安装字体列表.
private Int32 EnumFontCallBack(ref ENUMLOGFONTEX lpelfe, IntPtr lpntme, int FontType, int lParam)
{
//Debug.WriteLine(lpelfe.elfFullName);
if (lpelfe.elfFullName.Substring(0, 1) != "@")
{
if (!sysFontList.Contains(lpelfe.elfFullName))
{

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。