Changeset 80

Show
Ignore:
Timestamp:
07/20/08 15:45:47 (4 months ago)
Author:
JoeCoder
Message:

Added UTF-8 and better caching to Font.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/yage/gui/surface.d

    r79 r80  
    333333            // Todo: check style font properties for changes also. 
    334334            if (style.fontFamily && text != old_text) 
    335             {   Timer a = new Timer(); 
    336                 textImage = style.fontFamily.render(text, cast(int)style.fontSize, cast(int)style.fontSize, -1, -1, true); 
    337                 writefln(a, textImage.getWidth(), textImage.getHeight()); 
    338                 a.reset(); 
    339                 if (textTexture.texture) 
    340                    delete textTexture.texture
     335            {   textImage = style.fontFamily.render(text, cast(int)style.fontSize, cast(int)style.fontSize, -1, -1, true); 
     336                if (!textTexture.texture) 
     337                   textTexture = Texture(new GPUTexture(textImage, false, false), true, TEXTURE_FILTER_BILINEAR); 
     338                else 
     339                   textTexture.texture.upload(textImage, false, false); 
     340                old_text = text
    341341                 
    342                 writefln(a); 
    343                 a.reset(); 
    344                 textTexture = Texture(new GPUTexture(textImage, false, false), true, TEXTURE_FILTER_BILINEAR); 
    345                  
    346                 writefln(a); 
    347                  
    348                 old_text = text; 
    349342            } 
    350343             
  • trunk/yage/resource/font.d

    r79 r80  
    99import std.string; 
    1010import std.stdio; 
     11import std.utf; 
    1112import derelict.freetype.ft; 
    1213import yage.core.math; 
     14import yage.core.timer; 
     15import yage.core.types; 
     16import yage.core.parse; 
    1317import yage.resource.exception; 
    1418import yage.resource.resource; 
    1519import yage.resource.image; 
     20import yage.resource.texture; 
    1621 
     22//Stores a single rendered letter. 
     23private struct Letter 
     24{   Image image; 
     25    int top; 
     26    int left; 
     27    int advancex; 
     28    int advancey;    
     29} 
     30 
     31// Used as a key to lookup cached letters. 
     32private struct Key 
     33{   dchar letter; 
     34    short width; 
     35    short height; 
     36     
     37    // Hash recognizes that most letters are two bytes or less, most sizes are one byte or less 
     38    hash_t toHash() 
     39    {   return (cast(uint)letter<<16) + ((cast(uint)width)<<8) + (cast(uint)height);         
     40    } 
     41    int opEquals(Key s) 
     42    {   return letter==s.letter && width==s.width && height==s.height; 
     43    } 
     44    int opCmp(Key s) 
     45    {    return toHash() - s.toHash(); 
     46    } 
     47} 
     48 
     49/** 
     50 * An instance of a loaded Font. 
     51 * Fonts are typically used to render strings of text to an image. */ 
    1752class Font 
    1853{ 
    19     static FT_Library library; 
    20     static bool freetype_initialized = false; 
     54    protected static FT_Library library; 
     55    protected static bool freetype_initialized = false; 
    2156     
    22     char[] source; 
    23     FT_Face face; 
     57    protected FT_Face face; 
     58    protected char[] source; 
     59    protected Letter[Key] cache; // Using this cache of rendered character images increases performance by about 5x. 
    2460     
    25     /// 
     61    /** 
     62     * Construct and load the font file specified by filename. 
     63     * Params: 
     64     *     filename = Any font file supported by Freetype that exists in Resource.paths. */ 
    2665    this(char[] filename) 
    2766    { 
     
    2968        // TODO: Move this into Device? 
    3069        if (!freetype_initialized) 
    31         {   if (FT_Init_FreeType(&library)) 
    32                 throw new Exception("Freetype2 Failed"); 
    33         } 
     70            if (FT_Init_FreeType(&library)) 
     71                throw new Exception("Freetype2 Failed to load."); 
    3472         
    3573        // Load 
     
    4482    /// 
    4583    ~this() 
    46     {   /// TODO: cleanup. 
     84    {   // Do freetype libraries not require any type of cleanup? 
    4785    } 
    4886     
     
    5088    /** 
    5189     * Render an image from text. 
     90     * TODO: Fix text from Right-to-Left languages from being rendered backwards. 
    5291     * Params: 
    53      *     text =  
    54      *     width =  
    55      *     height =  
    56      *     line_width = Letters will wrap to the next line after this amount (breaking on spaces) 
    57      *     line_height = This much space will occur between each line, defaults to 1.5x height. 
    58      *     image_pow2 = If true, the image returned will always have its dimensions as powers of two. 
    59      * Returns: 
    60      */ 
    61     Image render(char[] text, int width, int height=0, int line_width=-1, int line_height=-1, bool image_pow2=false) 
    62     {    
     92     *     text = A string of text to render, can be utf-8 or unencoded unicode (dchar[]). 
     93     *     width = The horizontal pixel size of the font to render. 
     94     *     height = The vertical pixel size of the font to render, if 0 it will be the same as the width. 
     95     *     line_width = Letters will wrap to the next line after this amount (breaking on spaces), unsupported 
     96     *     line_height = This much space will occur between each line, defaults to 1.5x height, unsupported 
     97     *     image_pow2 = If true, the image returned will always have its dimensions as powers of two. */ 
     98    Image render(char[] utf8, int width, int height=0, int line_width=-1, int line_height=-1, bool image_pow2=false) 
     99    {   dchar[] unicode = toUTF32(utf8); 
     100        Image result = render(unicode, width, height, line_width, line_height, image_pow2); 
     101        delete unicode; 
     102        return result; 
     103    } 
     104     
     105    /// ditto 
     106    Image render(dchar[] text, int width, int height=0, int line_width=-1, int line_height=-1, bool image_pow2=false)  
     107    {        
    63108        // Calculate parameters 
    64109        if (line_height==-1) 
     
    70115        auto error = FT_Set_Pixel_Sizes(face, width, height);   // face, pixel width, pixel height 
    71116        if (error) 
    72             throw new Exception("Invalid font size."); 
     117            throw new Exception(formatString("Font '%s' does not support pixel sizes of %dx%d.", source, width, height));       
    73118         
    74         // Stores a single rendered letter. 
    75         struct Letter 
    76         {   Image image; 
    77             int advancex; 
    78             int advancey; // unnecessary? 
    79             int top; 
    80             int left; 
    81         } 
    82          
    83         Letter[] letters; // array of all rendered letters.      
     119        /* 
     120         * First, we render (or retrieve from cache) all letters into an array of Letter. 
     121         * This allows us to calculate dimensinal information like total width/height, number of lines etc. 
     122         * We then allocate an image of appropriate size, composite the letters onto it, and then return it. */ 
     123        Letter[] letters;  
    84124        int total_width = 0; 
    85125        int total_height = 0; 
     
    89129        // Create a glyph for each letter and store its parameters 
    90130        foreach (c; text) 
    91         { 
    92             error = FT_Load_Char(face, c, FT_LOAD_RENDER); // Load into slot  
    93             if (error) 
    94                 throw new Exception("Font Error."); 
     131        {    
     132            Key key = Key(c, width, height); 
     133            Letter letter; 
     134            if (key in cache) 
     135                letter = cache[key]; 
     136            else 
     137            {   // Render the character into the glyph slot. 
     138                error = FT_Load_Char(face, c, FT_LOAD_RENDER);   
     139                if (error) 
     140                    throw new Exception("Font '"~source~"' cannot render the character '"~toUTF8([c])~"'.");             
     141                 
     142                auto bitmap = face.glyph.bitmap; 
     143                ubyte[] data = (cast(ubyte*)bitmap.buffer)[0..(bitmap.width*bitmap.rows)];               
     144                 
     145                // Set the values of the letter.             
     146                letter.image = new Image(data.dup, 1, bitmap.width, bitmap.rows); 
     147                letter.top = face.glyph.bitmap_top; 
     148                letter.left = face.glyph.bitmap_left; 
     149                letter.advancex = face.glyph.advance.x>>6; 
     150                letter.advancey = face.glyph.advance.y>>6; 
     151                 
     152                cache[key] = letter; 
     153            } 
    95154             
    96             auto bitmap = face.glyph.bitmap; 
    97             ubyte[] data = (cast(ubyte*)bitmap.buffer)[0..(bitmap.width*bitmap.rows)]; 
    98              
    99             // Set the values of the 
    100             Letter letter; 
    101             letter.image = new Image(data.dup, 1, bitmap.width, bitmap.rows); 
    102             letter.top = face.glyph.bitmap_top; 
    103             letter.left = face.glyph.bitmap_left; 
    104             letter.advancex = total_width; 
    105             letter.advancey = total_height; 
    106155            letters ~= letter; 
    107              
    108             total_width+= face.glyph.advance.x>>6; 
    109             total_height+= face.glyph.advance.y>>6; 
     156            total_width+= letter.advancex; 
     157            total_height+= letter.advancey; 
    110158        } 
    111159         
     
    116164        int img_height = image_pow2 ? nextPow2(line_height*lines) : line_height*lines; 
    117165        Image result = new Image(1, img_width, img_height); 
     166         
     167        int advancex=0, advancey=0; 
    118168        for (int i=0; i<letters.length; i++) 
    119         {   result.overlay(letters[i].image, letters[i].advancex+letters[i].left, (letters[i].advancey-letters[i].top+height)); 
    120             delete letters[i].image; 
     169        {   result.overlay(letters[i].image, advancex+letters[i].left, (advancey-letters[i].top+height));            
     170            advancex+= letters[i].advancex; 
     171            advancey+= letters[i].advancey; 
    121172        } 
     173        delete letters; 
    122174     
    123175        return result; 
  • trunk/yage/resource/resource.d

    r79 r80  
    4141 
    4242    /// Get the array of path strings 
    43     static char[][] getPath() 
     43    static char[][] getPaths() 
    4444    {   return paths; 
    4545    } 
  • trunk/yage/resource/texture.d

    r79 r80  
    305305        // glu has resizing issues with non power of two source textures. 
    306306        if (mipmap) 
    307         {   //writefln(GL_TEXTURE_2D, " ", glinternalformat, " ", image.getWidth(), " ", image.getHeight(), " ", glformat, " ", GL_UNSIGNED_BYTE, " ", image.getData().length); 
    308             gluBuild2DMipmaps(GL_TEXTURE_2D, glinternalformat, image.getWidth(), image.getHeight(), glformat, GL_UNSIGNED_BYTE, image.getData().ptr); 
    309         } 
    310         else 
     307            gluBuild2DMipmaps(GL_TEXTURE_2D, glinternalformat, image.getWidth(), image.getHeight(), glformat, GL_UNSIGNED_BYTE, image.getData().ptr); 
     308        else 
    311309        { 
    312310            uint max = Device.getLimit(DEVICE_MAX_TEXTURE_SIZE); 
     
    325323            // Resize if necessary 
    326324            if (newwidth != width || newheight != height) 
    327                 image = image.resize(min(newwidth, max), min(newheight, max));          
     325                image = image.resize(min(newwidth, max), min(newheight, max)); 
    328326            glTexImage2D(GL_TEXTURE_2D, 0, glinternalformat, image.getWidth(), image.getHeight(), 0, glformat, GL_UNSIGNED_BYTE, image.getData().ptr); 
    329  
    330         } 
     327            } 
    331328        if(this.requested_width == 0) this.requested_width = this.getWidth(); 
    332329        if(this.requested_height == 0) this.requested_height = this.getHeight();