Advertisement
Pillager86

range violation on size 24

May 18th, 2020
2,929
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
D 5.68 KB | None | 0 0
  1. module misanthropyd.renderer.textrenderer;
  2.  
  3. import bindbc.freetype;
  4.  
  5. import misanthropyd.renderer.textures;
  6.  
  7. /// thrown when font loading fails
  8. class FontLoadException : Exception
  9. {
  10.     /// constructor
  11.     this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
  12.             pure nothrow @nogc @safe
  13.     {
  14.         super(msg, file, line, nextInChain);
  15.     }
  16. }
  17.  
  18. /// renders text as an array of unsigned ints
  19. class TextRenderer
  20. {
  21.     /// initializes freetype
  22.     static bool initialize()
  23.     {
  24.         /// load Freetype library
  25.         immutable ret = loadFreeType();
  26.         if(ret != ftSupport)
  27.         {
  28.             return false;
  29.         }
  30.  
  31.         if(FT_Init_FreeType(&library_))
  32.         {
  33.             return false;
  34.         }
  35.  
  36.         return true;
  37.     }
  38.  
  39.     /// cleanup
  40.     static void shutdown()
  41.     {
  42.         foreach(value ; fonts_)
  43.         {
  44.             destroy(value);
  45.         }
  46.         fonts_ = null;
  47.         FT_Done_FreeType(library_);
  48.     }
  49.  
  50.     /// load a font
  51.     static void loadFont(const string name, const string path, uint size)
  52.     {
  53.         fonts_[name] = new Font(path, size);
  54.     }
  55.  
  56.     /// render RGBA texture of text
  57.     static Texture2D renderText(const string fontName, const wstring text, ubyte r=0xff, ubyte g=0xff, ubyte b=0xff)
  58.     {
  59.         import std.stdio: writefln;
  60.  
  61.         Texture2D texture;
  62.         if(!cast(bool)(fontName in fonts_))
  63.             return null;
  64.         Font font = fonts_[fontName];
  65.         immutable useKerning = FT_HAS_KERNING(font.face_);
  66.         uint prevIndex = 0, glyphIndex = 0;
  67.         uint width, height; // @suppress(dscanner.suspicious.unmodified)
  68.  
  69.         getSize(font, text, width, height);
  70.  
  71.         uint[] bitmap = new uint[width * height];
  72.         bitmap[] = r | g<<8 | b<<16;
  73.         texture = Texture2D.create(width, height);
  74.  
  75.         int x = 0;
  76.         foreach(ch ; text)
  77.         {
  78.             glyphIndex = FT_Get_Char_Index(font.face_, ch);
  79.             if(useKerning && prevIndex && glyphIndex)
  80.             {
  81.                 FT_Vector delta;
  82.                 FT_Get_Kerning(font.face_, prevIndex, glyphIndex, FT_Kerning_Mode.FT_KERNING_DEFAULT, &delta);
  83.                 x += delta.x >> 6;
  84.             }
  85.             if(FT_Load_Char(font.face_, ch, FT_LOAD_RENDER))
  86.                 continue; // ignore unfound chars
  87.             auto glyphData = GlyphData(font.face_, font.face_.glyph);
  88.             // immutable Y_START = (font.face_.bbox.yMax - font.face_.glyph.metrics.horiBearingY) >> 6;
  89.             for(int row=0; row < font.face_.glyph.bitmap.rows; ++row)
  90.             {
  91.                 immutable YPOS = height - (row + glyphData.yoffset) - 1;
  92.                 // writefln("row=%s, yoffset=%s", row, glyphData.yoffset);
  93.                 for(int col=0; col < font.face_.glyph.bitmap.pitch; ++col)
  94.                 {
  95.                     immutable XPOS = x + col;
  96.                     // assert(glyphData.advance <= font.face_.glyph.bitmap.pitch);
  97.                     writefln("`%s`: yoffset=%s", ch, glyphData.yoffset);
  98.                     writefln(" `%s`: XPOS,YPOS=%s,%s", ch, XPOS, YPOS);
  99.                     bitmap[YPOS * width + XPOS] |=
  100.                         font.face_.glyph.bitmap.buffer[row * font.face_.glyph.bitmap.pitch + col] << 24;
  101.                 }
  102.             }
  103.             x += glyphData.advance;
  104.             prevIndex = glyphIndex;        
  105.         }
  106.         texture.setData(cast(ubyte[])bitmap);
  107.         return texture;
  108.     }
  109.  
  110.     private static getSize(Font font, const wstring text, ref uint width, ref uint height)
  111.     {
  112.         import std.algorithm: min, max;
  113.         import std.stdio: writefln;
  114.  
  115.         uint prevIndex = 0;
  116.         immutable useKerning = FT_HAS_KERNING(font.face_);
  117.         int x, minx, maxx, miny, maxy = font.face_.ascender >> 6;
  118.  
  119.         width = 0;
  120.         height = 0;
  121.  
  122.         foreach(ch ; text)
  123.         {
  124.             auto glyphIndex = FT_Get_Char_Index(font.face_, ch);
  125.             if(useKerning && prevIndex && glyphIndex)
  126.             {
  127.                 FT_Vector delta;
  128.                 FT_Get_Kerning(font.face_, prevIndex, glyphIndex, FT_Kerning_Mode.FT_KERNING_DEFAULT, &delta);
  129.                 x += delta.x >> 6;
  130.             }
  131.             if(FT_Load_Glyph(font.face_, ch, FT_LOAD_RENDER))
  132.                 continue;
  133.             auto glyphData = GlyphData(font.face_, font.face_.glyph);
  134.             minx = min(minx, x + glyphData.minx);
  135.             maxx = max(maxx, x + glyphData.maxx);
  136.             // for spaces
  137.             maxx = max(maxx, x + glyphData.advance);
  138.  
  139.             miny = min(miny, glyphData.yoffset);
  140.             maxy = max(maxy, glyphData.yoffset + glyphData.maxy - glyphData.miny);
  141.             writefln("maxy,miny of `%s`=%s,%s", ch, maxy,miny);
  142.             writefln(" yoffset of `%s`=%s", ch, glyphData.yoffset);
  143.             x += glyphData.advance - glyphData.minx;
  144.             prevIndex = glyphIndex;
  145.         }
  146.         width = maxx - minx;
  147.         height = maxy - miny; // - (font.face_.descender >> 6);
  148.         writefln("maxx,minx=%s,%s", maxx, minx);
  149.         writefln("width,height=%s,%s", width, height);
  150.         writefln("font.descender=%s", font.face_.descender >> 6);
  151.     }
  152.  
  153.     package static FT_Library library_;
  154.     private static Font[string] fonts_;
  155. }
  156.  
  157. private long FT_FLOOR(long x) { return ((x & -64) >> 6); }
  158. private long FT_CEIL(long x) { return (((x + 63) & -64) >> 6); }
  159.  
  160. private struct GlyphData
  161. {
  162.     this(const ref FT_Face face, const ref FT_GlyphSlot glyphSlot)
  163.     {
  164.         minx = cast(int)(FT_FLOOR(glyphSlot.metrics.horiBearingX));
  165.         maxx = cast(int)(FT_CEIL(glyphSlot.metrics.horiBearingX + glyphSlot.metrics.width));
  166.         maxy = cast(int)(FT_FLOOR(glyphSlot.metrics.horiBearingY));
  167.         miny = cast(int)(maxy - FT_CEIL(glyphSlot.metrics.height));
  168.         yoffset = cast(int)(FT_CEIL(face.ascender) - maxy);
  169.         // yoffset = cast(int)(FT_CEIL(face.ascender - glyphSlot.metrics.horiBearingY));
  170.         advance = cast(int)(FT_CEIL(glyphSlot.metrics.horiAdvance));
  171.     }
  172.  
  173.     int minx;
  174.     int maxx;
  175.     int miny;
  176.     int maxy;
  177.     int yoffset;
  178.     int advance;
  179. }
  180.  
  181. private class Font
  182. {
  183.     this(const string path, uint fontSize)
  184.     {
  185.         import std.string: toStringz;
  186.         import misanthropyd.core.logger : Logger;
  187.         Logger.logf(Logger.Severity.WARNING, "Creating font %s %s", path, fontSize);
  188.         immutable error = FT_New_Face(TextRenderer.library_, path.toStringz, 0, &face_);
  189.         if(error != 0)
  190.         {
  191.             throw new FontLoadException("Failed to load font `" ~ path ~ "`");
  192.         }
  193.         FT_Set_Pixel_Sizes(face_, 0, fontSize);
  194.     }
  195.  
  196.     ~this()
  197.     {
  198.         FT_Done_Face(face_);
  199.     }
  200.  
  201.     package FT_Face face_;
  202. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement