1 module ppc.backend.ft;
2 import bindbc.freetype;
3 import std..string;
4 import ppc.backend.cfile;
5 import ppc.backend;
6 
7 /++
8     The format of a Glyph
9     Usually the format is Bitmap.
10 +/
11 enum GlyphFormat {
12     Bitmap = FT_GLYPH_FORMAT_BITMAP,
13     Composite = FT_GLYPH_FORMAT_COMPOSITE,
14     None = FT_GLYPH_FORMAT_NONE,
15     Outline = FT_GLYPH_FORMAT_OUTLINE,
16     Plotter = FT_GLYPH_FORMAT_PLOTTER
17 }
18 
19 /++
20     The type of pixels the glyph contains
21 +/
22 enum GlyphPixels {
23     None = FT_PIXEL_MODE_NONE,
24     Mono = FT_PIXEL_MODE_MONO,
25     Gray = FT_PIXEL_MODE_GRAY,
26     Gray2 = FT_PIXEL_MODE_GRAY2,
27     Gray4 = FT_PIXEL_MODE_GRAY4,
28     LCD = FT_PIXEL_MODE_LCD,
29     LCDV = FT_PIXEL_MODE_LCD_V,
30     Max = FT_PIXEL_MODE_MAX
31 }
32 
33 alias FTVector = PVector;
34 
35 /++
36     A glyph is a single character
37 +/
38 struct Glyph {
39 private:
40     dchar represents;
41     ubyte[] bitmap;
42 
43     FTVector size;
44     FTVector bufferSize;
45 
46     uint grays;
47 
48     FTVector advance;
49     FTVector bearing;
50 
51     GlyphPixels pixels;
52     GlyphFormat fmt;
53 
54 public:
55 
56     /++
57         Constructs a new glyph
58     +/
59     this(FT_GlyphSlot slot, dchar rep) {
60         this.represents = rep;
61 
62         size = FTVector(slot.bitmap.width, slot.bitmap.rows);
63         bufferSize = FTVector(slot.bitmap.pitch, slot.bitmap.rows);
64         grays = slot.bitmap.num_grays;
65         pixels = cast(GlyphPixels)slot.bitmap.pixel_mode;
66         fmt = cast(GlyphFormat)slot.format;
67 
68         advance = FTVector(slot.advance.x, slot.advance.y);
69         bearing = FTVector(slot.bitmap_left, slot.bitmap_top);
70 
71 
72         // Copy pixels in to the bitmap this glyph represents
73         size_t toAllocate = slot.bitmap.pitch*slot.bitmap.rows;
74         bitmap = new ubyte[](toAllocate);
75         bitmap[] = slot.bitmap.buffer[0..toAllocate];
76     }
77 
78     /++
79         Gets advance (how much to offset position after rendering this character)
80     +/
81     FTVector getAdvance() {
82         return advance;
83     }
84 
85     /++
86         Gets the bearing (offset from origin)
87     +/
88     FTVector getBearing() {
89         return bearing;
90     }
91 
92     /++
93         Gets the size of the glyph in pixels
94     +/
95     PVector getSize() {
96         return size;
97     }
98 
99     /++
100         Gets the size of the glyph in bytes on each axis
101     +/
102     PVector getDataSize() {
103         return bufferSize;
104     }
105 
106     /++
107         Get number of gray levels used
108     +/
109     uint getGrays() {
110         return grays;
111     }
112 
113     /++
114         Gets the pixel mode of the glyph.
115     +/
116     GlyphPixels getPixelMode() {
117         return pixels;
118     }
119 
120     ubyte[] getPixels() {
121         return bitmap;
122     }
123 
124     /++
125         Gets the stored buffer of the glyph
126     +/
127     ubyte* getBuffer() {
128         return bitmap.ptr;
129     }
130 
131     /++
132         Gets the size in byte of the stored glyph pixel buffer
133     +/
134     size_t getBufferSize() {
135         return bitmap.length;
136     }
137 }
138 
139 /++
140     Load options for glpyhs
141 +/
142 enum FTLoadOption : uint {
143     Default = FT_LOAD_DEFAULT,
144 
145     ComputeMetrics = FT_LOAD_COMPUTE_METRICS,
146     LinearDesign = FT_LOAD_LINEAR_DESIGN,
147     CropBitmap = FT_LOAD_CROP_BITMAP,
148     ForceAutohint = FT_LOAD_FORCE_AUTOHINT,
149 
150     IgnoreGlobalAdvanceWidth = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH,
151     IgnoreTransform = FT_LOAD_IGNORE_TRANSFORM,
152 
153     Color = FT_LOAD_COLOR,
154     Monochrome = FT_LOAD_MONOCHROME,
155     Render = FT_LOAD_RENDER
156 }
157 
158 /++
159     A font face
160 +/
161 class FontFace {
162 private:
163     FreeType parent;
164     FT_Face face;
165 
166 public:
167     ~this() {
168         // HACK: This should really be cleaned up, but it causes crashes rn.
169         // if (face !is null) {
170         //     FT_Done_Face(face);
171         // }
172     }
173 
174     this(FreeType ft, string fontName, uint faceIndex = 0) {
175         this.parent = ft;
176         FT_New_Face(parent.lib, fontName.toStringz, faceIndex, &face);
177         if (face is null) throw new Exception("Unable to find font "~fontName~"! (Make sure to use full paths)");
178 
179         FT_Select_Charmap(face, FT_ENCODING_UNICODE);
180     }
181 
182     this(FreeType ft, ubyte[] fontData, uint faceIndex = 0) {
183         this.parent = ft;
184         FT_New_Memory_Face(parent.lib, fontData.ptr, cast(int)fontData.length, cast(int)faceIndex, &face);
185         
186         FT_Select_Charmap(face, FT_ENCODING_UNICODE);
187     }
188 
189     void setPixelSizes(size_t width, size_t height) {
190         FT_Set_Pixel_Sizes(face, cast(uint)width, cast(uint)height);
191     }
192 
193     Glyph* getChar(dchar c, FTLoadOption options = FTLoadOption.Render) {
194         uint index = FT_Get_Char_Index(face, c);
195         FT_Load_Glyph(face, index, FT_LOAD_RENDER);
196         //FT_Load_Char(face, ds[0], options);
197         // TODO: Allow conversion
198         //FT_Render_Glyph(face.glyph, FT_RENDER_MODE_NORMAL);
199         if (face.glyph is null) return null;
200         return new Glyph(face.glyph, c);
201     }
202 }
203 
204 /++
205     FreeType library
206 +/
207 class FreeType {
208 private:
209     FT_Library lib;
210     FontFace[] faces;
211 
212 public:
213     ~this() {
214         destroy(faces);
215         FT_Done_Library(lib);
216     }
217 
218     this() {
219         FT_Init_FreeType(&lib);
220     }
221 
222     /++
223         Opens a font from a file
224     +/
225     FontFace open(string file, uint faceIndex = 0) {
226         faces ~= new FontFace(this, file, faceIndex);
227         return faces[$-1];
228     }
229 
230     /++
231         Opens a font from a memory buffer
232     +/
233     FontFace open(ubyte[] file, uint faceIndex = 0) {
234         faces ~= new FontFace(this, file, faceIndex);
235         return faces[$-1];
236     }
237 
238     /++
239         Opens a font from a memfile buffer
240     +/
241     FontFace open(MemFile file, uint faceIndex = 0) {
242         faces ~= new FontFace(this, file.arrayptr[0..file.length], faceIndex);
243         return faces[$-1];
244     }
245 }
246 
247 void initFT() {
248     auto ret = loadFreeType();
249     switch(ret) {
250         case (FTSupport.noLibrary):
251             throw new Exception("FreeType was not found, please install freetype.");
252         case (FTSupport.badLibrary):
253             throw new Exception("Failed to load some symbols, the application might crash!");
254         default: break;
255     }
256 }