Skip to content

Commit 1e3b4f8

Browse files
committed
去掉所有 gdi 的调用
1 parent 7bbbf5c commit 1e3b4f8

27 files changed

Lines changed: 236 additions & 415 deletions

Workbench/Wmf/SkiaWmfRenderer/samples/sample/Program.cs

Lines changed: 0 additions & 306 deletions
Original file line numberDiff line numberDiff line change
@@ -16,310 +16,4 @@
1616

1717
SkiaWmfRenderHelper.TryConvertToPng(new FileInfo(file), new FileInfo("foo.png"));
1818

19-
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
20-
21-
var image = Image.FromFile(file);
22-
var imageWidth = image.Width;
23-
var imageHeight = image.Height;
24-
var imagePhysicalDimension = image.PhysicalDimension;
25-
26-
using var fileStream = File.OpenRead(file);
27-
var wmfDocument = new WmfDocument();
28-
wmfDocument.Load(fileStream);
29-
30-
31-
var format = wmfDocument.Format;
32-
Console.WriteLine(format.Dump());
33-
34-
// Left: 61528
35-
// Top: 62158
36-
// Right: 4008
37-
// Bottom: 3378
38-
// Unit: 1000
39-
// Checksum: 21749
40-
41-
var x = Math.Min(format.Left, format.Right);
42-
var y = Math.Min(format.Top, format.Bottom);
43-
44-
var width = Math.Abs(format.Right - format.Left);
45-
var height = Math.Abs(format.Bottom - format.Top);
46-
47-
var inchUnit = format.Unit;
48-
var pixelWidth = (double)width / inchUnit * 96;
49-
var pixelHeight = (double)height / inchUnit * 96;
50-
51-
var skBitmap = new SKBitmap(width, height, SKColorType.Bgra8888, SKAlphaType.Premul);
52-
SKCanvas canvas = new SKCanvas(skBitmap);
53-
54-
var currentPenColor = SKColors.Empty;
55-
var currentPenThickness = 0;
56-
57-
float currentX = x;
58-
float currentY = y;
59-
60-
var currentTextColor = SKColors.Black;
61-
62-
using var paint = new SKPaint();
63-
paint.IsAntialias = true;
64-
65-
using var skFont = new SKFont();
66-
float currentFontSize = 0;
67-
68-
string? currentFontName = null;
69-
CharacterSet currentCharacterSet = CharacterSet.ANSI_CHARSET;
70-
Encoding currentEncoding = Encoding.GetEncoding(1252);
71-
72-
Encoding CharacterSetToEncoding(CharacterSet characterSet)
73-
{
74-
var codePageId = characterSet switch
75-
{
76-
CharacterSet.ANSI_CHARSET
77-
// DEFAULT_CHARSET: Specifies a character set based on the current system locale; for example, when the system locale is United States English, the default character set is ANSI_CHARSET.
78-
or CharacterSet.DEFAULT_CHARSET => 1252,
79-
CharacterSet.OEM_CHARSET => 437,
80-
CharacterSet.SHIFTJIS_CHARSET => 932,
81-
CharacterSet.HANGUL_CHARSET => 949,
82-
CharacterSet.JOHAB_CHARSET => 1361,
83-
CharacterSet.GB2312_CHARSET => 936,
84-
CharacterSet.CHINESEBIG5_CHARSET => 950,
85-
CharacterSet.HEBREW_CHARSET => 1255,
86-
CharacterSet.ARABIC_CHARSET => 1256,
87-
CharacterSet.GREEK_CHARSET => 1253,
88-
CharacterSet.TURKISH_CHARSET => 1254,
89-
CharacterSet.BALTIC_CHARSET => 1257,
90-
CharacterSet.EASTEUROPE_CHARSET => 1250,
91-
CharacterSet.RUSSIAN_CHARSET => 1251,
92-
CharacterSet.THAI_CHARSET => 874,
93-
CharacterSet.VIETNAMESE_CHARSET => 1258,
94-
CharacterSet.SYMBOL_CHARSET => 42, // Symbol font is not a code page, but 42 is often used for Symbol font
95-
_ => 1252,
96-
};
97-
98-
return Encoding.GetEncoding(codePageId);
99-
}
100-
101-
float lastDxOffset = 0;
102-
103-
bool isItalic = false;
104-
int fontWeight = 400;
105-
106-
for (var i = 0; i < wmfDocument.Records.Count; i++)
107-
{
108-
var wmfDocumentRecord = wmfDocument.Records[i];
109-
switch (wmfDocumentRecord)
110-
{
111-
// The META_SETBKMODE Record defines the background raster operation mix mode in the playback device context. The background mix mode is the mode for combining pens, text, hatched brushes, and interiors of filled objects with background colors on the output surface.
112-
case WmfSetBkModeRecord setBkModeRecord:
113-
{
114-
// RecordFunction (2 bytes): A 16-bit unsigned integer that defines this WMF record type. The lower byte MUST match the lower byte of the RecordType Enumeration (section 2.1.1.1) table value META_SETBKMODE.
115-
// BkMode (2 bytes): A 16-bit unsigned integer that defines background mix mode. This MUST be one of the values in the MixMode Enumeration (section 2.1.1.20).
116-
117-
break;
118-
}
119-
case WmfSetTextAlignRecord setTextAlignRecord:
120-
{
121-
// RecordFunction (2 bytes): A 16-bit unsigned integer that defines this WMF record type. The lower byte MUST match the lower byte of the RecordType Enumeration (section 2.1.1.1) table value META_SETTEXTALIGN.
122-
// TextAlignmentMode (2 bytes): A 16-bit unsigned integer that defines text alignment. This value MUST be a combination of one or more TextAlignmentMode Flags (section 2.1.2.3) for text with a horizontal baseline, and VerticalTextAlignmentMode Flags (section 2.1.2.4) for text with a vertical baseline.
123-
124-
break;
125-
}
126-
case WmfCreatePenIndirectRecord createPenIndirectRecord:
127-
{
128-
Color color = createPenIndirectRecord.Color;
129-
currentPenColor = new SKColor(color.R, color.G, color.B, color.A);
130-
currentPenThickness = Math.Max(createPenIndirectRecord.Width.X, createPenIndirectRecord.Width.Y);
131-
break;
132-
}
133-
// - [11] {== WmfMoveToRecord ==
134-
// RecordSize: 5 words = 10 bytes
135-
// RecordType: 0x0214 (RecordType.META_MOVETO)
136-
// X: 1372Y: 865} Oxage.Wmf.IBinaryRecord {Oxage.Wmf.Records.WmfMoveToRecord}
137-
//
138-
case WmfMoveToRecord moveToRecord:
139-
{
140-
currentX = moveToRecord.X;
141-
currentY = moveToRecord.Y;
142-
lastDxOffset = 0;
143-
break;
144-
}
145-
// - [12] {== WmfLineToRecord ==
146-
// RecordSize: 5 words = 10 bytes
147-
// RecordType: 0x0213 (RecordType.META_LINETO)
148-
// X: 1840Y: 865} Oxage.Wmf.IBinaryRecord {Oxage.Wmf.Records.WmfLineToRecord}
149-
//
150-
case WmfLineToRecord lineToRecord:
151-
{
152-
paint.IsStroke = true;
153-
paint.Color = currentPenColor;
154-
paint.StrokeWidth = currentPenThickness;
155-
156-
canvas.DrawLine(currentX, currentY, lineToRecord.X, lineToRecord.Y, paint);
157-
158-
break;
159-
}
160-
// - [13] {== WmfSetTextColorRecord ==
161-
// RecordSize: 5 words = 10 bytes
162-
// RecordType: 0x0209 (RecordType.META_SETTEXTCOLOR)
163-
// ColorRef: Color [A=255, R=0, G=0, B=0]
164-
// } Oxage.Wmf.IBinaryRecord {Oxage.Wmf.Records.WmfSetTextColorRecord}
165-
//
166-
case WmfSetTextColorRecord setTextColorRecord:
167-
{
168-
currentTextColor = ToSKColor(setTextColorRecord.Color);
169-
170-
break;
171-
}
172-
// - [15] {== WmfCreateFontIndirectRecord ==
173-
// RecordSize: 28 words = 56 bytes
174-
// RecordType: 0x02fb (RecordType.META_CREATEFONTINDIRECT)
175-
// Height: -636
176-
// Width: 0
177-
// Escapement: 0
178-
// Orientation: 0
179-
// Weight: 400
180-
// Italic: False
181-
// Underline: False
182-
// StrikeOut: False
183-
// CharSet: 0x0000 (CharacterSet.ANSI_CHARSET)
184-
// OutPrecision: 0x00 (OutPrecision.OUT_DEFAULT_PRECIS)
185-
// ClipPrecision: 0x02 (ClipPrecision.CLIP_STROKE_PRECIS)
186-
// Quality: 0x00 (FontQuality.DEFAULT_QUALITY)
187-
// Pitch: 0x00 (PitchFont.DEFAULT_PITCH)
188-
// Family: 0x00 (FamilyFont.FF_DONTCARE)
189-
// Facename: Times New Roman Oxage.Wmf.IBinaryRecord {Oxage.Wmf.Records.WmfCreateFontIndirectRecord}
190-
//
191-
case WmfCreateFontIndirectRecord createFontIndirectRecord:
192-
{
193-
// "Times New Roman\0è±ñwñ±ñw @ów³\u0011fÁ"
194-
currentFontName = createFontIndirectRecord.FaceName.ToString();
195-
currentCharacterSet = createFontIndirectRecord.CharSet;
196-
currentEncoding = CharacterSetToEncoding(currentCharacterSet);
197-
198-
// Width (2 bytes): A 16-bit signed integer that defines the average width, in logical units, of characters in the font. If Width is 0x0000, the aspect ratio of the device SHOULD be matched against the digitization aspect ratio of the available fonts to find the closest match, determined by the absolute value of the difference.
199-
var fontWidth = createFontIndirectRecord.Width;
200-
201-
// Height (2 bytes): A 16-bit signed integer that specifies the height, in logical units, of the font's character cell. The character height is computed as the character cell height minus the internal leading. The font mapper SHOULD interpret the height as follows.
202-
// value < 0x0000 The font mapper SHOULD transform this value into device units and match its absolute value against the character height of available fonts.
203-
// 0x0000 A default height value MUST be used when creating a physical font.
204-
// 0x0000 < value The font mapper SHOULD transform this value into device units and match it against the cell height of available fonts.
205-
// For all height comparisons, the font mapper SHOULD find the largest physical font that does not exceed the requested size.<40>
206-
// <40> Section 2.2.1.2: All Windows versions: mapping the logical font size to the available physical fonts occurs the first time the logical font needs to be used in a drawing operation.
207-
// For the MM_TEXT mapping mode, the following formula can be used to compute the height of a font with a specified point size.
208-
// Height = -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
209-
//
210-
var fontHeight = createFontIndirectRecord.Height;
211-
212-
fontWidth = Math.Abs(fontWidth);
213-
fontHeight = Math.Abs(fontHeight);
214-
215-
currentFontSize = Math.Max(fontWidth, fontHeight);
216-
217-
isItalic = createFontIndirectRecord.Italic;
218-
fontWeight = createFontIndirectRecord.Weight;
219-
220-
break;
221-
}
222-
case WmfUnknownRecord unknownRecord:
223-
{
224-
switch (unknownRecord.RecordType)
225-
{
226-
case RecordType.META_EXTTEXTOUT:
227-
{
228-
// 关于字间距的规则:
229-
// 1. 如果两个 META_EXTTEXTOUT 相邻,中间没有 MoveTo 之类
230-
// 则第二个 META_EXTTEXTOUT 将需要使用前一个 META_EXTTEXTOUT 的 dx 末项
231-
// 2. 可选的 dx 是存放在字符串末尾的可选项,从文档 2.3.3.5 上可见 dx 是顶格写的,这就意味着这个值是一定对齐整数倍的。由于 dx 是放在数据末尾,可通过减法算出 dx 长度,即数据总长度减去所有已知字段的长度加上字符串长度,剩余的就是 dx 长度。如果计算返回的 dx 长度是奇数,则首个 byte 是需要跳过的,如此就能确保在 16bit 下的 wmf 格式里面,读取的 dx 是从整数倍开始读取
232-
// 参考 https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/nf-wingdi-exttextoutw
233-
234-
// 测试 17 项
235-
var memoryStream = new MemoryStream(unknownRecord.Data);
236-
var binaryReader = new BinaryReader(memoryStream);
237-
var ty = binaryReader.ReadUInt16();
238-
var tx = binaryReader.ReadUInt16();
239-
var stringLength = binaryReader.ReadUInt16();
240-
var fwOpts = (ExtTextOutOptions)binaryReader.ReadUInt16();
241-
// Rectangle (8 bytes): An optional 8-byte Rect Object (section 2.2.2.18).) When either ETO_CLIPPED, ETO_OPAQUE, or both are specified, the rectangle defines the dimensions, in logical coordinates, used for clipping, opaquing, or both. When neither ETO_CLIPPED nor ETO_OPAQUE is specified, the coordinates in Rectangle are ignored.
242-
var st = 8; /*2byte ofr ty tx stringLength fwOpts*/
243-
if (fwOpts is ExtTextOutOptions.ETO_CLIPPED or ExtTextOutOptions.ETO_OPAQUE)
244-
{
245-
// 此时才有 Rectangle 的值
246-
binaryReader.ReadBytes(8);
247-
st += 8;
248-
}
249-
250-
st += stringLength;
251-
252-
// String (variable): A variable-length string that specifies the text to be drawn. The string does not need to be null-terminated, because StringLength specifies the length of the string. If the length is odd, an extra byte is placed after it so that the following member (optional Dx) is aligned on a 16-bit boundary. The string will be decoded based on the font object currently selected into the playback device context. If a font matching the font object’s specification is not found, the decoding is undefined. If a matching font is found that matches the charset specified in the font object, the string should be decoded with the codepages in the following table.
253-
var stringBuffer = binaryReader.ReadBytes(stringLength);
254-
var text = currentEncoding.GetString(stringBuffer);
255-
256-
// Dx (variable): An optional array of 16-bit signed integers that indicate the distance between origins of adjacent character cells. For example, Dx[i] logical units separate the origins of character cell i and character cell i + 1. If this field is present, there MUST be the same number of values as there are characters in the string.
257-
//Debug.Assert(st == unknownRecord.RecordSize);
258-
var dxLength = unknownRecord.Data.Length - st;
259-
260-
skFont.Size = currentFontSize;
261-
skFont.Typeface = SKTypeface.FromFamilyName(currentFontName, (SKFontStyleWeight)fontWeight,
262-
SKFontStyleWidth.Normal, isItalic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright);
263-
264-
paint.Style = SKPaintStyle.Fill;
265-
paint.Color = currentTextColor;
266-
267-
var currentXOffset = currentX + tx + lastDxOffset;
268-
269-
if (dxLength == 0)
270-
{
271-
canvas.DrawText(text, currentXOffset, currentY + ty, skFont, paint);
272-
}
273-
else
274-
{
275-
// 如果这里计算出来不是偶数,则首个需要跳过。这是经过测试验证的。~~但没有相关说明内容。且跳过的 byte 是有内容的~~ String (variable): If the length is odd, an extra byte is placed after it so that the following member (optional Dx) is aligned on a 16-bit boundary.
276-
// 如果字符串的长度是奇数,则在字符串后面放置一个额外的字节,以便下一个成员(可选的 Dx)对齐到 16 位边界。
277-
if (dxLength > ((dxLength / sizeof(UInt16)) * sizeof(UInt16)))
278-
{
279-
// 读取掉这个额外的字节,以便 Dx 对齐到 16 位边界
280-
var r = binaryReader.ReadByte();
281-
_ = r;
282-
}
283-
284-
UInt16[] dxArray = new UInt16[dxLength / sizeof(UInt16)];
285-
for (var t = 0; t < dxArray.Length; t++)
286-
{
287-
dxArray[t] = binaryReader.ReadUInt16();
288-
}
289-
290-
if (dxArray.Length != text.Length)
291-
{
292-
continue;
293-
}
294-
295-
for (var textIndex = 0; textIndex < text.Length; textIndex++)
296-
{
297-
canvas.DrawText(text[textIndex].ToString(), currentXOffset, currentY + ty, skFont, paint);
298-
299-
currentXOffset += dxArray[textIndex];
300-
}
301-
302-
lastDxOffset = dxArray[^1];
303-
}
304-
305-
break;
306-
}
307-
}
308-
309-
break;
310-
}
311-
}
312-
}
313-
314-
var outputFile = "1.png";
315-
using (var outputStream = File.OpenWrite(outputFile))
316-
{
317-
skBitmap.Encode(outputStream, SKEncodedImageFormat.Png, 100);
318-
}
319-
32019
Console.WriteLine("Hello, World!");
321-
322-
SKColor ToSKColor(Color color)
323-
{
324-
return new SKColor(color.R, color.G, color.B, color.A);
325-
}

Workbench/Wmf/SkiaWmfRenderer/src/SkiaWmfRenderer/Rendering/WmfRenderer.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Diagnostics.CodeAnalysis;
22
using System.Drawing;
33
using Oxage.Wmf;
4+
using Oxage.Wmf.Primitive;
45
using Oxage.Wmf.Records;
56
using SkiaSharp;
67

@@ -271,7 +272,7 @@ private bool TryRenderInner(SKCanvas canvas, WmfRenderStatus renderStatus)
271272
return true;
272273
}
273274

274-
private static SKColor ToSKColor(Color color)
275+
private static SKColor ToSKColor(WmfColor color)
275276
{
276277
return new SKColor(color.R, color.G, color.B, color.A);
277278
}

Workbench/Wmf/SkiaWmfRenderer/src/SkiaWmfRenderer/SkiaWmfRenderer.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net9.0</TargetFramework>
4+
<TargetFrameworks>net6.0;net9.0</TargetFrameworks>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
7+
<LangVersion>latest</LangVersion>
78
</PropertyGroup>
89

910
<ItemGroup>
1011
<ProjectReference Include="..\wieslawsoltes-wmf\src\library\Library.csproj" />
1112
</ItemGroup>
1213

1314
<ItemGroup>
15+
<PackageReference Include="dotnetCampus.LatestCSharpFeatures" Version="12.0.1" />
1416
<PackageReference Include="SkiaSharp" Version="3.119.0" />
1517
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.7" />
1618
</ItemGroup>

Workbench/Wmf/SkiaWmfRenderer/src/wieslawsoltes-wmf/src/library/Library.csproj

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netstandard2.0;net6.0;net7.0;net461</TargetFrameworks>
4+
<TargetFrameworks>net6.0;net9.0;</TargetFrameworks>
55
<OutputType>Library</OutputType>
66
<RootNamespace>Oxage.Wmf</RootNamespace>
77
<AssemblyName>Oxage.Wmf</AssemblyName>
8+
<LangVersion>latest</LangVersion>
89
</PropertyGroup>
910

1011
<PropertyGroup>
@@ -20,12 +21,8 @@
2021
<PackageTags>wmf;emf;windows;metafile;reader;writer;gdi;drawing</PackageTags>
2122
</PropertyGroup>
2223

23-
<ItemGroup Condition="$(TargetFramework.StartsWith('net4'))">
24-
<Reference Include="System.Drawing" />
25-
</ItemGroup>
26-
2724
<ItemGroup>
28-
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
25+
<PackageReference Include="dotnetCampus.LatestCSharpFeatures" Version="12.0.1" />
2926
</ItemGroup>
3027

3128
</Project>

0 commit comments

Comments
 (0)