private static extern uint GetGlyphOutline(IntPtr hdc, uint ch, uint format, out GLYPHMETRICS gm, uint cb, IntPtr buffer, ref MAT2 mat2);
public GlyphDescriptorCollection CollectGlyphs(IAtlasDescriptor descriptor, Action<float> progress, CancellationTokenSource cts) { ValidateSettings(descriptor); GlyphDescriptorCollection glyphs = new GlyphDescriptorCollection(); HashSet<char> chars = null; if (descriptor.UseFonts) { chars = new HashSet<char>(); // build set of characters foreach (ICharSet charSet in descriptor.CharSets) { foreach (char ch in charSet.Characters) { chars.Add(ch); } } } int count = 0; count += (descriptor.UseImages) ? descriptor.Images.Count : 0; count += (chars != null) ? chars.Count : 0; int current = 0; if (descriptor.UseImages) { int ch = descriptor.StartCode; foreach (ImageInfo ii in descriptor.Images) { int width = ii.Image.Width; int height = ii.Image.Height; Vec2i offset; Image image = CropImage(ii.Image, out offset); ABC abc = new ABC(); abc.abcA = -(int)ii.Offset.X; abc.abcB = (uint)(width + ii.Offset.X); abc.abcC = 0; ii.SortCode = ii.HasCustomCode ? ii.Code : UniqueCode(ref ch, descriptor.Images); var gi = new GlyphDescriptor((char)ii.SortCode, abc, image, descriptor.Spacing * 2); gi.Offset = offset - (Vec2i)ii.Offset; gi.ImageInfo = ii; glyphs.Add(gi); if (progress != null) { progress((float)(++current) / count); } if (cts != null) { cts.Token.ThrowIfCancellationRequested(); } } } if (descriptor.UseFonts) { // build image for each glyph var gm = new GLYPHMETRICS(); foreach (char ch in chars) { Image image = null; try { if (ch == 159) { } image = TextHelper.GetGlyphOutlineImage(descriptor.Font, ch, out gm); if (image == null) { continue; } } catch { if (image != null) { image.Dispose(); } // skip glyph continue; } // flip Y offset ABC abc = new ABC(); TextHelper.GetCharABCWidths(descriptor.Font, ch, out abc); GlyphDescriptor glyph = new GlyphDescriptor(ch, abc, image, descriptor.Spacing * 2); glyph.Offset = new Vec2i(gm.gmptGlyphOrigin.X, -gm.gmptGlyphOrigin.Y); glyphs.Add(glyph); if (progress != null) { progress((float)(++current) / count); } if (cts != null) { cts.Token.ThrowIfCancellationRequested(); } } if (descriptor.ForceSpace && glyphs.Find(32) == null) { ABC abc = new ABC(); TextHelper.GetCharABCWidths(descriptor.Font, (char)32, out abc); abc.abcB = (uint)(abc.abcA + abc.abcB + abc.abcC); abc.abcC = 0; glyphs.Add(new GlyphDescriptor((char)32, abc)); } } return glyphs; }
public static Image GetGlyphOutlineImage(Font font, char ch, out GLYPHMETRICS gm) { byte[] alpha; int size; using (FontDC dc = new FontDC(font)) { MAT2 mat = new MAT2(); mat.eM11.value = 1; mat.eM22.value = 1; size = (int)GetGlyphOutline(dc.handle, ch, (uint)GlyphOutline.GGO_GRAY8_BITMAP, out gm, 0, IntPtr.Zero, ref mat); if (size <= 0) { return null; } IntPtr bufptr = Marshal.AllocHGlobal(size); try { alpha = new byte[size]; GetGlyphOutline(dc.handle, ch, (uint)GlyphOutline.GGO_GRAY8_BITMAP, out gm, (uint)size, bufptr, ref mat); Marshal.Copy(bufptr, alpha, 0, size); } finally { Marshal.FreeHGlobal(bufptr); } } int stride = (int)(gm.gmBlackBoxX + 3) & ~3; // dword alinged Bitmap bmp = new Bitmap(stride, (int)gm.gmBlackBoxY, PixelFormat.Format32bppArgb); BitmapData bd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); int bmpSize = bd.Stride * bd.Height; byte[] bmpValues = new byte[bmpSize]; Marshal.Copy(bd.Scan0, bmpValues, 0, bmpSize); const int bias = 0x40; for (int i = 0; i < size; i++) { byte v = (byte)((int)alpha[i] * 255 / bias); bmpValues[i * 4 + 0] = 0xff; bmpValues[i * 4 + 1] = 0xff; bmpValues[i * 4 + 2] = 0xff; bmpValues[i * 4 + 3] = v; } Marshal.Copy(bmpValues, 0, bd.Scan0, bmpSize); bmp.UnlockBits(bd); return bmp; }