using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Text; using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; namespace FontPreview; public class Worker { public class CardData { public Bitmap Bitmap; public int Width; public string Name; public string DisplayName; public string Path; public bool Failed; } public delegate void CompletedHandler(bool cancelled, int done, int failed, int remain, List cards); private MainForm form; private List items; private int target; private string text; private int size; private Color fg; private Color bg; private Dictionary cache; private Thread thread; private volatile bool stop; private List results = new List(); public event CompletedHandler Completed; public Worker(MainForm f, List items, int target, string text, int size, Color fg, Color bg, Dictionary cache) { form = f; this.items = items; this.target = target; this.text = text; this.size = size; this.fg = fg; this.bg = bg; this.cache = cache; } public bool IsRunning() { return thread != null && thread.IsAlive; } public void Stop() { stop = true; } public void Start() { stop = false; thread = new Thread(Run); thread.IsBackground = true; thread.SetApartmentState(ApartmentState.STA); thread.Start(); } private void Run() { int total = target; int next = 0; int done = 0; int failed = 0; // 并行渲染:拉满 CPU 线程,批量并行 int workers = Math.Max(2, Environment.ProcessorCount); object renderLock = new object(); ParameterizedThreadStart work = delegate (object _) { while (true) { if (stop) break; int i = Interlocked.Increment(ref next) - 1; if (i >= total) break; FontItem fontItem = items[i]; Bitmap value = null; bool failed2 = false; string key = fontItem.Cat + "|" + fontItem.Path; lock (renderLock) { cache.TryGetValue(key, out value); } if (value == null) { value = RenderFont(fontItem.Path, fontItem.Name, text, size, fg, bg, out failed2); if (value != null) { lock (renderLock) { cache[key] = value; } } } CardData cardData = new CardData(); cardData.Bitmap = value; cardData.Width = 340; cardData.Name = fontItem.Name; cardData.DisplayName = fontItem.DisplayName; cardData.Path = fontItem.Path; cardData.Failed = failed2; lock (results) { results.Add(cardData); } int d = Interlocked.Increment(ref done); if (value == null) Interlocked.Increment(ref failed); // 进度更新节流:每 10 个或最后一个才唤醒 UI,避免频繁打断 if (d % 10 == 0 || d >= total) { int pct = (int)((double)d / (double)total * 100.0); ((Control)form).BeginInvoke((Delegate)(Action)delegate { form.SetProgress(pct, d, total); }); } } }; Thread[] ts = new Thread[workers]; for (int w = 0; w < workers; w++) { ts[w] = new Thread(work); ts[w].IsBackground = true; ts[w].Start(); } foreach (Thread wt in ts) wt.Join(); int remain = total - done; List snapshot; lock (results) { snapshot = new List(results); } ((Control)form).Invoke((Delegate)(Action)delegate { if (this.Completed != null) { this.Completed(stop, done, failed, remain, snapshot); } }); } internal static Bitmap RenderFont(string path, string name, string text, int size, Color fg, Color bg, out bool failed) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Expected O, but got Unknown //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Expected O, but got Unknown failed = false; Bitmap val = null; PrivateFontCollection val2 = null; Font val3 = null; try { // 直接按文件路径加载,避免 AddMemoryFont 在快速连续渲染多字体时按内存地址缓存导致串字体 val2 = new PrivateFontCollection(); val2.AddFontFile(path); if (((FontCollection)val2).Families.Length == 0) { failed = true; return null; } FontFamily val4 = ((FontCollection)val2).Families[0]; val3 = new Font(val4, (float)size, (FontStyle)0, (GraphicsUnit)2); int num = 14; Graphics val5 = Graphics.FromHwnd(IntPtr.Zero); try { StringFormat val6 = new StringFormat((StringFormatFlags)4096); try { val6.Alignment = (StringAlignment)0; val6.LineAlignment = (StringAlignment)1; SizeF sizeF = val5.MeasureString(text, val3, 20000, val6); int num2 = (int)Math.Ceiling(sizeF.Width) + num * 2; int num3 = (int)Math.Ceiling(sizeF.Height) + 14; val = new Bitmap(num2, Math.Max(num3, 30)); Graphics val7 = Graphics.FromImage((Image)(object)val); try { val7.SmoothingMode = (SmoothingMode)4; val7.TextRenderingHint = (TextRenderingHint)4; val7.Clear(bg); Brush val8 = (Brush)new SolidBrush(fg); try { val7.DrawString(text, val3, val8, new RectangleF(num, 7f, sizeF.Width, num3 - 14), val6); } finally { ((IDisposable)val8)?.Dispose(); } } finally { ((IDisposable)val7)?.Dispose(); } } finally { ((IDisposable)val6)?.Dispose(); } } finally { ((IDisposable)val5)?.Dispose(); } return val; } catch (Exception) { failed = true; if (val != null) { ((Image)val).Dispose(); } return null; } finally { if (val3 != null) { val3.Dispose(); } if (val2 != null) { ((FontCollection)val2).Dispose(); } } } }