|
在C#默认支持的是GDI+库,使用GDI+库,有很丰富的函数和排版手段,可以满足大部分的要求.除非,你需要使用bitmap字体,GDI+对于字体的支持有很大限制,支持truetype字体,而对于点阵字体(栅格字体)则不再支持,但是很多字体都是这种点阵字体,这样就带来一个问题,使用不了了.而很多公司则会自己制作某些用途的字体,比如说在LED显示屏上使用,这个时候使用GDI+则不再是一个明智的选择了.此外GDI+虽然强大,但是经过测试发现效率却是低下,速度比GDI慢了不少.
但是CS的界面开发环境和编码习惯又很适合,所以比较折衷的解决方法是使用C#调用系统的gdi api函数实现编程.
首先C#环境中的color结构是AARRGGBB,而在win32的颜色结构却是AABBGGRR,所以如果不注意则会使到颜色出现偏差或者逆转.在.net环境的类库当中,在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这个函数来释放.
下面来实现一些具体的函数
/// <summary>
/// 填充特定DC的一个区域的特定颜色
/// </summary>
/// <param name="hdc">给定DC</param>
/// <param name="Rect">给定区域</param>
/// <param name="FillColor">给定颜色</param>
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;
}
在.net中,默认的字体类,不支持点阵字体,所以要使用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#不支持点阵字体,所以自然地,使用.net提供的函数,获取的安装字体列表自然是不包含点阵字体的.所以并不符合我的要求.所以还得使用系统的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))
{
sysFontList.Add(lpelfe.elfFullName);
}
}
return 1;
}
LOGFONT logfont = new LOGFONT();
logfont.lfCharSet = FontCharSet.DEFAULT_CHARSET;
Bitmap bmp = new Bitmap(10, 10);
Graphics g = Graphics.FromImage(bmp);
IntPtr hDC = g.GetHdc();
EnumFontFamilies.EnumFontFamiliesEx(hDC, logfont, EnumFontCallBack, IntPtr.Zero, 0);
g.ReleaseHdc(hDC);
g.Dispose();
bmp.Dispose();
public class EnumFontFamilies
{
public const int LF_FACESIZE = 32;
public delegate int EnumFontExDelegate( ref ENUMLOGFONTEX lpelfe, IntPtr lpntme, int FontType, int lParam );
[DllImport("gdi32.dll", EntryPoint = "EnumFontFamiliesEx", CharSet = CharSet.Unicode)]
public static extern int EnumFontFamiliesEx( IntPtr hDC, [In] LOGFONT logFont, EnumFontExDelegate enumFontExCallback,
IntPtr lParam, uint dwFlags );
}
其中EnumFontCallBack为回调函数,通过这个回调函数,可以获取到系统所有的字体,包括点阵的字体.
附录A:
GDI使用的结构和常量
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct ENUMLOGFONTEX
{
public LOGFONT elfLogFont;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string elfFullName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string elfStyle;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string elfScript;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class LOGFONT
{
public int lfHeight;
public int lfWidth;
public int lfEscapement;
public int lfOrientation;
public FontWeight lfWeight;
[MarshalAs(UnmanagedType.U1)]
public bool lfItalic;
[MarshalAs(UnmanagedType.U1)]
public bool lfUnderline;
[MarshalAs(UnmanagedType.U1)]
public bool lfStrikeOut;
public FontCharSet lfCharSet;
public FontPrecision lfOutPrecision;
public FontClipPrecision lfClipPrecision;
public FontQuality lfQuality;
public FontPitchAndFamily lfPitchAndFamily;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string lfFaceName;
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("LOGFONT\n");
sb.AppendFormat(" lfHeight: {0}\n", lfHeight);
sb.AppendFormat(" lfWidth: {0}\n", lfWidth);
sb.AppendFormat(" lfEscapement: {0}\n", lfEscapement);
sb.AppendFormat(" lfOrientation: {0}\n", lfOrientation);
sb.AppendFormat(" lfWeight: {0}\n", lfWeight);
sb.AppendFormat(" lfItalic: {0}\n", lfItalic);
sb.AppendFormat(" lfUnderline: {0}\n", lfUnderline);
sb.AppendFormat(" lfStrikeOut: {0}\n", lfStrikeOut);
sb.AppendFormat(" lfCharSet: {0}\n", lfCharSet);
sb.AppendFormat(" lfOutPrecision: {0}\n", lfOutPrecision);
sb.AppendFormat(" lfClipPrecision: {0}\n", lfClipPrecision);
sb.AppendFormat(" lfQuality: {0}\n", lfQuality);
sb.AppendFormat(" lfPitchAndFamily: {0}\n", lfPitchAndFamily);
sb.AppendFormat(" lfFaceName: {0}\n", lfFaceName);
return sb.ToString();
}
}
public enum FontWeight : int
{
FW_DONTCARE = 0,
FW_THIN = 100,
FW_EXTRALIGHT = 200,
FW_LIGHT = 300,
FW_NORMAL = 400,
FW_MEDIUM = 500,
FW_SEMIBOLD = 600,
FW_BOLD = 700,
FW_EXTRABOLD = 800,
FW_HEAVY = 900,
}
public enum FontCharSet : byte
{
ANSI_CHARSET = 0,
DEFAULT_CHARSET = 1,
SYMBOL_CHARSET = 2,
SHIFTJIS_CHARSET = 128,
HANGEUL_CHARSET = 129,
HANGUL_CHARSET = 129,
GB2312_CHARSET = 134,
CHINESEBIG5_CHARSET = 136,
OEM_CHARSET = 255,
JOHAB_CHARSET = 130,
HEBREW_CHARSET = 177,
ARABIC_CHARSET = 178,
GREEK_CHARSET = 161,
TURKISH_CHARSET = 162,
VIETNAMESE_CHARSET = 163,
THAI_CHARSET = 222,
EASTEUROPE_CHARSET = 238,
RUSSIAN_CHARSET = 204,
MAC_CHARSET = 77,
BALTIC_CHARSET = 186,
}
public enum FontPrecision : byte
{
OUT_DEFAULT_PRECIS = 0,
OUT_STRING_PRECIS = 1,
OUT_CHARACTER_PRECIS = 2,
OUT_STROKE_PRECIS = 3,
OUT_TT_PRECIS = 4,
OUT_DEVICE_PRECIS = 5,
OUT_RASTER_PRECIS = 6,
OUT_TT_ONLY_PRECIS = 7,
OUT_OUTLINE_PRECIS = 8,
OUT_SCREEN_OUTLINE_PRECIS = 9,
OUT_PS_ONLY_PRECIS = 10,
}
public enum FontClipPrecision : byte
{
CLIP_DEFAULT_PRECIS = 0,
CLIP_CHARACTER_PRECIS = 1,
CLIP_STROKE_PRECIS = 2,
CLIP_MASK = 0xf,
CLIP_LH_ANGLES = (1 << 4),
CLIP_TT_ALWAYS = (2 << 4),
CLIP_DFA_DISABLE = (4 << 4),
CLIP_EMBEDDED = (8 << 4),
}
public enum FontQuality : byte
{
DEFAULT_QUALITY = 0,
DRAFT_QUALITY = 1,
PROOF_QUALITY = 2,
NONANTIALIASED_QUALITY = 3,
ANTIALIASED_QUALITY = 4,
CLEARTYPE_QUALITY = 5,
CLEARTYPE_NATURAL_QUALITY = 6,
}
[Flags]
public enum FontPitchAndFamily : byte
{
DEFAULT_PITCH = 0,
FIXED_PITCH = 1,
VARIABLE_PITCH = 2,
FF_DONTCARE = (0 << 4),
FF_ROMAN = (1 << 4),
FF_SWISS = (2 << 4),
FF_MODERN = (3 << 4),
FF_SCRIPT = (4 << 4),
FF_DECORATIVE = (5 << 4),
}
/// <summary>
/// Enumeration for the raster operations used in BitBlt.
/// In C++ these are actually #define. But to use these
/// constants with C#, a new enumeration type is defined.
/// </summary>
public enum TernaryRasterOperations
{
SRCCOPY = 0x00CC0020, /* dest = source */
SRCPAINT = 0x00EE0086, /* dest = source OR dest */
SRCAND = 0x008800C6, /* dest = source AND dest */
SRCINVERT = 0x00660046, /* dest = source XOR dest */
SRCERASE = 0x00440328, /* dest = source AND (NOT dest ) */
NOTSRCCOPY = 0x00330008, /* dest = (NOT source) */
NOTSRCERASE = 0x001100A6, /* dest = (NOT src) AND (NOT dest) */
MERGECOPY = 0x00C000CA, /* dest = (source AND pattern) */
MERGEPAINT = 0x00BB0226, /* dest = (NOT source) OR dest */
PATCOPY = 0x00F00021, /* dest = pattern */
PATPAINT = 0x00FB0A09, /* dest = DPSnoo */
PATINVERT = 0x005A0049, /* dest = pattern XOR dest */
DSTINVERT = 0x00550009, /* dest = (NOT dest) */
BLACKNESS = 0x00000042, /* dest = BLACK */
WHITENESS = 0x00FF0062, /* dest = WHITE */
};
[Flags]
public enum dwDTFormat : int
{
DT_TOP = 0, DT_LEFT = 0x00000000, DT_CENTER = 0x00000001, DT_RIGHT = 0x00000002,
DT_VCENTER = 0x00000004, DT_BOTTOM = 0x00000008, DT_WORDBREAK = 0x00000010, DT_SINGLELINE = 0x00000020,
DT_EXPANDTABS = 0x00000040, DT_TABSTOP = 0x00000080, DT_NOCLIP = 0x00000100, DT_EXTERNALLEADING = 0x00000200,
DT_CALCRECT = 0x00000400, DT_NOPREFIX = 0x00000800, DT_INTERNAL = 0x00001000
};
public struct Rect
{
public int Left, Top, Right, Bottom;
public Rect(Rectangle r)
{
this.Left = r.Left;
this.Top = r.Top;
this.Bottom = r.Bottom;
this.Right = r.Right;
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct TEXTMETRIC
{
public Int32 tmHeight;
public Int32 tmAscent;
public Int32 tmDescent;
public Int32 tmInternalLeading;
public Int32 tmExternalLeading;
public Int32 tmAveCharWidth;
public Int32 tmMaxCharWidth;
public Int32 tmWeight;
public Int32 tmOverhang;
public Int32 tmDigitizedAspectX;
public Int32 tmDigitizedAspectY;
public char tmFirstChar;
public char tmLastChar;
public char tmDefaultChar;
public char tmBreakChar;
public byte tmItalic;
public byte tmUnderlined;
public byte tmStruckOut;
public byte tmPitchAndFamily;
public byte tmCharSet;
} |
阿莫论坛20周年了!感谢大家的支持与爱护!!
月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
|