| | 1 | | using System; |
| | 2 | | using System.Collections.Generic; |
| | 3 | | using System.Drawing; |
| | 4 | | using System.Linq; |
| | 5 | | using SixLabors.Fonts; |
| | 6 | | using SixLabors.ImageSharp; |
| | 7 | | using SixLabors.ImageSharp.PixelFormats; |
| | 8 | | using SixLabors.ImageSharp.Processing; |
| | 9 | | using SixLabors.Shapes.Temp; |
| | 10 | |
|
| | 11 | | using SPointF = SixLabors.Primitives.PointF; |
| | 12 | |
|
| | 13 | | namespace Imagini.Fonts |
| | 14 | | { |
| | 15 | | /// <summary> |
| | 16 | | /// Represents a sprite font page with the texture atlas and glyph bounds data. |
| | 17 | | /// </summary> |
| | 18 | | public class SpriteFontPage |
| | 19 | | { |
| | 20 | | /// <summary> |
| | 21 | | /// Returns the starting code point of this page. |
| | 22 | | /// </summary> |
| 701 | 23 | | public char Start { get; private set; } |
| | 24 | | /// <summary> |
| | 25 | | /// Returns the ending code point of this page. |
| | 26 | | /// </summary> |
| 676 | 27 | | public char End { get; private set; } |
| | 28 | |
|
| 18 | 29 | | private Dictionary<char, Rectangle> _glyphMap = |
| 0 | 30 | | new Dictionary<char, Rectangle>(); |
| | 31 | |
|
| | 32 | | /// <summary> |
| | 33 | | /// Returns the Image containing the glyphs of this page. |
| | 34 | | /// </summary> |
| | 35 | | /// <remarks> |
| | 36 | | /// Intended for use by text renderers. |
| | 37 | | /// Can be disposed and nullified by the renderer if it |
| | 38 | | /// uses other surface type (GPU texture, for example). |
| | 39 | | /// </remarks> |
| 672 | 40 | | public Image<Rgba32> Texture { get; set; } |
| | 41 | |
|
| 0 | 42 | | internal SpriteFontPage(Font font, |
| 0 | 43 | | ref ISet<char> requestedSymbols, int textureSize, int padding) |
| | 44 | | { |
| 18 | 45 | | Texture = new Image<Rgba32>(textureSize, textureSize); |
| 36 | 46 | | Texture.Mutate(x => x.Fill(Rgba32.Transparent)); |
| 18 | 47 | | RenderFontToTexture(font, ref requestedSymbols, textureSize, padding); |
| 18 | 48 | | } |
| | 49 | |
|
| | 50 | | private void RenderFontToTexture(Font font, |
| | 51 | | ref ISet<char> requestedSymbols, int textureSize, int padding) |
| | 52 | | { |
| 18 | 53 | | var options = new RendererOptions(font); |
| 54 | 54 | | int x = 0, y = 0, maxHeight = 0; |
| | 55 | | do |
| | 56 | | { |
| 606 | 57 | | var glyphStr = $"{requestedSymbols.First()}"; |
| 606 | 58 | | var glyph = glyphStr[0]; |
| 606 | 59 | | var sizeF = TextMeasurer.Measure(glyphStr, options); |
| 606 | 60 | | var size = new Size((int)Math.Ceiling(sizeF.Width), (int)Math.Ceiling(sizeF.Height)); |
| 606 | 61 | | maxHeight = Math.Max(maxHeight, size.Height); |
| | 62 | |
|
| 606 | 63 | | var dstRect = new Rectangle(x, y, size.Width, size.Height); |
| 606 | 64 | | if (dstRect.Right >= textureSize) |
| | 65 | | { |
| | 66 | | // jump onto the next line |
| 52 | 67 | | x = 0; |
| 52 | 68 | | y += maxHeight + padding; |
| 52 | 69 | | maxHeight = 0; |
| 52 | 70 | | dstRect.X = x; |
| 52 | 71 | | dstRect.Y = y; |
| | 72 | | } |
| 606 | 73 | | if (dstRect.Bottom >= textureSize) |
| | 74 | | { |
| | 75 | | // we can't fit letters anymore |
| | 76 | | break; |
| | 77 | | } |
| | 78 | |
|
| 597 | 79 | | var shapes = TextBuilder.GenerateGlyphs(glyphStr, new SPointF(x, y), options); |
| 1194 | 80 | | Texture.Mutate(m => m.Fill(Rgba32.White, shapes)); |
| 597 | 81 | | _glyphMap.Add(glyph, dstRect); |
| 597 | 82 | | requestedSymbols.Remove(glyph); |
| 614 | 83 | | if (Start == default(char)) Start = glyph; |
| 597 | 84 | | End = glyph; |
| | 85 | |
|
| 597 | 86 | | x += size.Width + padding; |
| 597 | 87 | | } while (requestedSymbols.Any()); |
| 18 | 88 | | } |
| | 89 | |
|
| | 90 | | /// <summary> |
| | 91 | | /// Checks if this page contains the specified symbol. |
| | 92 | | /// </summary> |
| 157 | 93 | | public bool HasGlyph(char symbol) => _glyphMap.ContainsKey(symbol); |
| | 94 | |
|
| | 95 | | /// <summary> |
| | 96 | | /// Returns the rectangle defining pixel region where the specified |
| | 97 | | /// glyph is located. Returns empty rectangle if the glyph is not present. |
| | 98 | | /// </summary> |
| | 99 | | public Rectangle GetGlyph(char symbol) |
| | 100 | | { |
| 168 | 101 | | if (!HasGlyph(symbol)) return Rectangle.Empty; |
| 130 | 102 | | return _glyphMap[symbol]; |
| | 103 | | } |
| | 104 | | } |
| | 105 | | } |