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 }