diff src/texture-font.c @ 0:785057719d9b

Import.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 05 Aug 2013 12:25:43 +0300
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/texture-font.c	Mon Aug 05 12:25:43 2013 +0300
@@ -0,0 +1,638 @@
+/* ===========================================================================
+ * Freetype GL - A C OpenGL Freetype engine
+ * Platform:    Any
+ * WWW:         http://code.google.com/p/freetype-gl/
+ * ----------------------------------------------------------------------------
+ * Copyright 2011,2012 Nicolas P. Rougier. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NICOLAS P. ROUGIER ''AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL NICOLAS P. ROUGIER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are
+ * those of the authors and should not be interpreted as representing official
+ * policies, either expressed or implied, of Nicolas P. Rougier.
+ * ============================================================================
+ */
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_STROKER_H
+// #include FT_ADVANCES_H
+#include FT_LCD_FILTER_H
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <math.h>
+#include <wchar.h>
+#include "platform.h"
+#include "texture-font.h"
+
+#undef __FTERRORS_H__
+#define FT_ERRORDEF( e, v, s )  { e, s },
+#define FT_ERROR_START_LIST     {
+#define FT_ERROR_END_LIST       { 0, 0 } };
+const struct {
+    int          code;
+    const char*  message;
+} FT_Errors[] =
+#include FT_ERRORS_H
+
+
+
+
+// ------------------------------------------------- texture_font_load_face ---
+int
+texture_font_load_face( FT_Library * library,
+                        const char * filename,
+                        const float size,
+                        FT_Face * face )
+{
+    size_t hres = 64;
+    FT_Error error;
+    FT_Matrix matrix = { (int)((1.0/hres) * 0x10000L),
+                         (int)((0.0)      * 0x10000L),
+                         (int)((0.0)      * 0x10000L),
+                         (int)((1.0)      * 0x10000L) };
+
+    assert( library );
+    assert( filename );
+    assert( size );
+
+    /* Initialize library */
+    error = FT_Init_FreeType( library );
+    if( error )
+    {
+        fprintf(stderr, "FT_Error (0x%02x) : %s\n",
+                FT_Errors[error].code, FT_Errors[error].message);
+        return 0;
+    }
+
+    /* Load face */
+    error = FT_New_Face( *library, filename, 0, face );
+    if( error )
+    {
+        fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
+                 __LINE__, FT_Errors[error].code, FT_Errors[error].message);
+        FT_Done_FreeType( *library );
+        return 0;
+    }
+
+    /* Select charmap */
+    error = FT_Select_Charmap( *face, FT_ENCODING_UNICODE );
+    if( error )
+    {
+        fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
+                 __LINE__, FT_Errors[error].code, FT_Errors[error].message );
+        FT_Done_Face( *face );
+        FT_Done_FreeType( *library );
+        return 0;
+    }
+
+    /* Set char size */
+    error = FT_Set_Char_Size( *face, (int)(size*64), 0, 72*hres, 72 );
+    if( error )
+    {
+        fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
+                 __LINE__, FT_Errors[error].code, FT_Errors[error].message );
+        FT_Done_Face( *face );
+        FT_Done_FreeType( *library );
+        return 0;
+    }
+
+    /* Set transform matrix */
+    FT_Set_Transform( *face, &matrix, NULL );
+
+    return 1;
+}
+
+
+// ------------------------------------------------------ texture_glyph_new ---
+texture_glyph_t *
+texture_glyph_new( void )
+{
+    texture_glyph_t *self = (texture_glyph_t *) malloc( sizeof(texture_glyph_t) );
+    if( self == NULL)
+    {
+        fprintf( stderr,
+                 "line %d: No more memory for allocating data\n", __LINE__ );
+        exit( EXIT_FAILURE );
+    }
+    self->id        = 0;
+    self->width     = 0;
+    self->height    = 0;
+    self->outline_type = 0;
+    self->outline_thickness = 0.0;
+    self->offset_x  = 0;
+    self->offset_y  = 0;
+    self->advance_x = 0.0;
+    self->advance_y = 0.0;
+    self->s0        = 0.0;
+    self->t0        = 0.0;
+    self->s1        = 0.0;
+    self->t1        = 0.0;
+    self->kerning   = vector_new( sizeof(kerning_t) );
+    return self;
+}
+
+
+// --------------------------------------------------- texture_glyph_delete ---
+void
+texture_glyph_delete( texture_glyph_t *self )
+{
+    assert( self );
+    vector_delete( self->kerning );
+    free( self );
+}
+
+// ---------------------------------------------- texture_glyph_get_kerning ---
+float 
+texture_glyph_get_kerning( const texture_glyph_t * self,
+                           const wchar_t charcode )
+{
+    size_t i;
+
+    assert( self );
+    for( i=0; i<vector_size(self->kerning); ++i )
+    {
+        kerning_t * kerning = (kerning_t *) vector_get( self->kerning, i );
+        if( kerning->charcode == charcode )
+        {
+            return kerning->kerning;
+        }
+    }
+    return 0;
+}
+
+
+// ------------------------------------------ texture_font_generate_kerning ---
+void
+texture_font_generate_kerning( texture_font_t *self )
+{
+    size_t i, j;
+    FT_Library library;
+    FT_Face face;
+    FT_UInt glyph_index, prev_index;
+    texture_glyph_t *glyph, *prev_glyph;
+    FT_Vector kerning;
+    
+    assert( self );
+
+    /* Load font */
+    if( !texture_font_load_face( &library, self->filename, self->size, &face ) )
+    {
+        return;
+    }
+
+    /* For each glyph couple combination, check if kerning is necessary */
+    /* Starts at index 1 since 0 is for the special backgroudn glyph */
+    for( i=1; i<self->glyphs->size; ++i )
+    {
+        glyph = *(texture_glyph_t **) vector_get( self->glyphs, i );
+        glyph_index = FT_Get_Char_Index( face, glyph->charcode );
+        vector_clear( glyph->kerning );
+
+        for( j=1; j<self->glyphs->size; ++j )
+        {
+            prev_glyph = *(texture_glyph_t **) vector_get( self->glyphs, j );
+            prev_index = FT_Get_Char_Index( face, prev_glyph->charcode );
+            FT_Get_Kerning( face, prev_index, glyph_index, FT_KERNING_UNFITTED, &kerning );
+            // printf("%c(%d)-%c(%d): %ld\n",
+            //       prev_glyph->charcode, prev_glyph->charcode,
+            //       glyph_index, glyph_index, kerning.x);
+            if( kerning.x )
+            {
+                // 64 * 64 because of 26.6 encoding AND the transform matrix used
+                // in texture_font_load_face (hres = 64)
+                kerning_t k = {prev_glyph->charcode, kerning.x / (float)(64.0f*64.0f)};
+                vector_push_back( glyph->kerning, &k );
+            }
+        }
+    }
+    FT_Done_Face( face );
+    FT_Done_FreeType( library );
+}
+
+
+// ------------------------------------------------------- texture_font_new ---
+texture_font_t *
+texture_font_new( texture_atlas_t * atlas,
+                  const char * filename,
+                  const float size)
+{
+    texture_font_t *self = (texture_font_t *) malloc( sizeof(texture_font_t) );
+    FT_Library library;
+    FT_Face face;
+    FT_Size_Metrics metrics;
+    
+    assert( filename );
+    assert( size );
+
+    if( self == NULL)
+    {
+        fprintf( stderr,
+                 "line %d: No more memory for allocating data\n", __LINE__ );
+        exit( EXIT_FAILURE );
+    }
+    self->glyphs = vector_new( sizeof(texture_glyph_t *) );
+    self->atlas = atlas;
+    self->height = 0;
+    self->ascender = 0;
+    self->descender = 0;
+    self->filename = strdup( filename );
+    self->size = size;
+    self->outline_type = 0;
+    self->outline_thickness = 0.0;
+    self->hinting = 1;
+    self->kerning = 1;
+    self->filtering = 1;
+    // FT_LCD_FILTER_LIGHT   is (0x00, 0x55, 0x56, 0x55, 0x00)
+    // FT_LCD_FILTER_DEFAULT is (0x10, 0x40, 0x70, 0x40, 0x10)
+    self->lcd_weights[0] = 0x10;
+    self->lcd_weights[1] = 0x40;
+    self->lcd_weights[2] = 0x70;
+    self->lcd_weights[3] = 0x40;
+    self->lcd_weights[4] = 0x10;
+
+    /* Get font metrics at high resolution */
+
+    if( !texture_font_load_face( &library, self->filename, self->size*100, &face ) )
+    {
+        return self;
+    }
+
+    // 64 * 64 because of 26.6 encoding AND the transform matrix used
+    // in texture_font_load_face (hres = 64)
+    self->underline_position = face->underline_position / (float)(64.0f*64.0f) * self->size;
+    self->underline_position = round( self->underline_position );
+    if( self->underline_position > -2 )
+    {
+        self->underline_position = -2.0;
+    }
+
+    self->underline_thickness = face->underline_thickness / (float)(64.0f*64.0f) * self->size;
+    self->underline_thickness = round( self->underline_thickness );
+    if( self->underline_thickness < 1 )
+    {
+        self->underline_thickness = 1.0;
+    }
+
+    metrics = face->size->metrics; 
+    self->ascender = (metrics.ascender >> 6) / 100.0;
+    self->descender = (metrics.descender >> 6) / 100.0;
+    self->height = (metrics.height >> 6) / 100.0;
+    self->linegap = self->height - self->ascender + self->descender;
+    FT_Done_Face( face );
+    FT_Done_FreeType( library );
+
+    /* -1 is a special glyph */
+    texture_font_get_glyph( self, -1 );
+
+    return self;
+}
+
+
+// ---------------------------------------------------- texture_font_delete ---
+void
+texture_font_delete( texture_font_t *self )
+{
+    size_t i;
+    texture_glyph_t *glyph;
+
+    assert( self );
+
+    if( self->filename )
+    {
+        free( self->filename );
+    }
+
+
+    for( i=0; i<vector_size( self->glyphs ); ++i)
+    {
+        glyph = *(texture_glyph_t **) vector_get( self->glyphs, i );
+        texture_glyph_delete( glyph);
+    }
+
+    vector_delete( self->glyphs );
+    free( self );
+}
+
+
+// ----------------------------------------------- texture_font_load_glyphs ---
+size_t
+texture_font_load_glyphs( texture_font_t * self,
+                          const wchar_t * charcodes )
+{
+    size_t i, x, y, width, height, depth, w, h;
+    FT_Library library;
+    FT_Error error;
+    FT_Face face;
+    FT_Glyph ft_glyph;
+    FT_GlyphSlot slot;
+    FT_Bitmap ft_bitmap;
+
+    FT_UInt glyph_index;
+    texture_glyph_t *glyph;
+    ivec4 region;
+    size_t missed = 0;
+
+    assert( self );
+    assert( charcodes );
+
+
+    width  = self->atlas->width;
+    height = self->atlas->height;
+    depth  = self->atlas->depth;
+
+    if( !texture_font_load_face( &library, self->filename, self->size, &face ) )
+    {
+        return wcslen(charcodes);
+    }
+
+    /* Load each glyph */
+    for( i=0; i<wcslen(charcodes); ++i )
+    {
+        FT_Int32 flags = 0;
+        int ft_bitmap_width = 0;
+        int ft_bitmap_rows = 0;
+        int ft_bitmap_pitch = 0;
+        int ft_glyph_top = 0;
+        int ft_glyph_left = 0;
+        glyph_index = FT_Get_Char_Index( face, charcodes[i] );
+        // WARNING: We use texture-atlas depth to guess if user wants
+        //          LCD subpixel rendering
+
+        if( self->outline_type > 0 )
+        {
+            flags |= FT_LOAD_NO_BITMAP;
+        }
+        else
+        {
+            flags |= FT_LOAD_RENDER;
+        }
+
+        if( !self->hinting )
+        {
+            flags |= FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT;
+        }
+        else
+        {
+            flags |= FT_LOAD_FORCE_AUTOHINT;
+        }
+
+
+        if( depth == 3 )
+        {
+            FT_Library_SetLcdFilter( library, FT_LCD_FILTER_LIGHT );
+            flags |= FT_LOAD_TARGET_LCD;
+            if( self->filtering )
+            {
+                FT_Library_SetLcdFilterWeights( library, self->lcd_weights );
+            }
+        }
+        error = FT_Load_Glyph( face, glyph_index, flags );
+        if( error )
+        {
+            fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
+                     __LINE__, FT_Errors[error].code, FT_Errors[error].message );
+            FT_Done_Face( face );
+            FT_Done_FreeType( library );
+            return wcslen(charcodes)-i;
+        }
+
+
+        if( self->outline_type == 0 )
+        {
+            slot            = face->glyph;
+            ft_bitmap       = slot->bitmap;
+            ft_bitmap_width = slot->bitmap.width;
+            ft_bitmap_rows  = slot->bitmap.rows;
+            ft_bitmap_pitch = slot->bitmap.pitch;
+            ft_glyph_top    = slot->bitmap_top;
+            ft_glyph_left   = slot->bitmap_left;
+        }
+        else
+        {
+            FT_Stroker stroker;
+            FT_BitmapGlyph ft_bitmap_glyph;
+            error = FT_Stroker_New( library, &stroker );
+            if( error )
+            {
+                fprintf(stderr, "FT_Error (0x%02x) : %s\n",
+                        FT_Errors[error].code, FT_Errors[error].message);
+                FT_Done_Face( face );
+                FT_Stroker_Done( stroker );
+                FT_Done_FreeType( library );
+                return 0;
+            }
+            FT_Stroker_Set( stroker,
+                            (int)(self->outline_thickness *64),
+                            FT_STROKER_LINECAP_ROUND,
+                            FT_STROKER_LINEJOIN_ROUND,
+                            0);
+            error = FT_Get_Glyph( face->glyph, &ft_glyph);
+            if( error )
+            {
+                fprintf(stderr, "FT_Error (0x%02x) : %s\n",
+                        FT_Errors[error].code, FT_Errors[error].message);
+                FT_Done_Face( face );
+                FT_Stroker_Done( stroker );
+                FT_Done_FreeType( library );
+                return 0;
+            }
+
+            if( self->outline_type == 1 )
+            {
+                error = FT_Glyph_Stroke( &ft_glyph, stroker, 1 );
+            }
+            else if ( self->outline_type == 2 )
+            {
+                error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 0, 1 );
+            }
+            else if ( self->outline_type == 3 )
+            {
+                error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 1, 1 );
+            }
+            if( error )
+            {
+                fprintf(stderr, "FT_Error (0x%02x) : %s\n",
+                        FT_Errors[error].code, FT_Errors[error].message);
+                FT_Done_Face( face );
+                FT_Stroker_Done( stroker );
+                FT_Done_FreeType( library );
+                return 0;
+            }
+          
+            if( depth == 1)
+            {
+                error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
+                if( error )
+                {
+                    fprintf(stderr, "FT_Error (0x%02x) : %s\n",
+                            FT_Errors[error].code, FT_Errors[error].message);
+                    FT_Done_Face( face );
+                    FT_Stroker_Done( stroker );
+                    FT_Done_FreeType( library );
+                    return 0;
+                }
+            }
+            else
+            {
+                error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_LCD, 0, 1);
+                if( error )
+                {
+                    fprintf(stderr, "FT_Error (0x%02x) : %s\n",
+                            FT_Errors[error].code, FT_Errors[error].message);
+                    FT_Done_Face( face );
+                    FT_Stroker_Done( stroker );
+                    FT_Done_FreeType( library );
+                    return 0;
+                }
+            }
+            ft_bitmap_glyph = (FT_BitmapGlyph) ft_glyph;
+            ft_bitmap       = ft_bitmap_glyph->bitmap;
+            ft_bitmap_width = ft_bitmap.width;
+            ft_bitmap_rows  = ft_bitmap.rows;
+            ft_bitmap_pitch = ft_bitmap.pitch;
+            ft_glyph_top    = ft_bitmap_glyph->top;
+            ft_glyph_left   = ft_bitmap_glyph->left;
+            FT_Stroker_Done(stroker);
+        }
+
+
+        // We want each glyph to be separated by at least one black pixel
+        // (for example for shader used in demo-subpixel.c)
+        w = ft_bitmap_width/depth + 1;
+        h = ft_bitmap_rows + 1;
+        region = texture_atlas_get_region( self->atlas, w, h );
+        if ( region.x < 0 )
+        {
+            missed++;
+            fprintf( stderr, "Texture atlas is full (line %d)\n",  __LINE__ );
+            continue;
+        }
+        w = w - 1;
+        h = h - 1;
+        x = region.x;
+        y = region.y;
+        texture_atlas_set_region( self->atlas, x, y, w, h,
+                                  ft_bitmap.buffer, ft_bitmap.pitch );
+
+        glyph = texture_glyph_new( );
+        glyph->charcode = charcodes[i];
+        glyph->width    = w;
+        glyph->height   = h;
+        glyph->outline_type = self->outline_type;
+        glyph->outline_thickness = self->outline_thickness;
+        glyph->offset_x = ft_glyph_left;
+        glyph->offset_y = ft_glyph_top;
+        glyph->s0       = x/(float)width;
+        glyph->t0       = y/(float)height;
+        glyph->s1       = (x + glyph->width)/(float)width;
+        glyph->t1       = (y + glyph->height)/(float)height;
+
+        // Discard hinting to get advance
+        FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_HINTING);
+        slot = face->glyph;
+        glyph->advance_x = slot->advance.x/64.0;
+        glyph->advance_y = slot->advance.y/64.0;
+
+        vector_push_back( self->glyphs, &glyph );
+
+        if( self->outline_type > 0 )
+        {
+            FT_Done_Glyph( ft_glyph );
+        }
+    }
+    FT_Done_Face( face );
+    FT_Done_FreeType( library );
+    texture_atlas_upload( self->atlas );
+    texture_font_generate_kerning( self );
+    return missed;
+}
+
+
+// ------------------------------------------------- texture_font_get_glyph ---
+texture_glyph_t *
+texture_font_get_glyph( texture_font_t * self,
+                        wchar_t charcode )
+{
+    size_t i;
+    wchar_t buffer[2] = {0,0};
+    texture_glyph_t *glyph;
+
+    assert( self );
+
+    assert( self );
+    assert( self->filename );
+    assert( self->atlas );
+
+    /* Check if charcode has been already loaded */
+    for( i=0; i<self->glyphs->size; ++i )
+    {
+        glyph = *(texture_glyph_t **) vector_get( self->glyphs, i );
+        // If charcode is -1, we don't care about outline type or thickness
+        if( (glyph->charcode == charcode) &&
+            ((charcode == (wchar_t)(-1) ) || 
+             ((glyph->outline_type == self->outline_type) &&
+              (glyph->outline_thickness == self->outline_thickness)) ))
+        {
+            return glyph;
+        }
+    }
+
+    /* charcode -1 is special : it is used for line drawing (overline,
+     * underline, strikethrough) and background.
+     */
+    if( charcode == (wchar_t)(-1) )
+    {
+        size_t width  = self->atlas->width;
+        size_t height = self->atlas->height;
+        ivec4 region = texture_atlas_get_region( self->atlas, 5, 5 );
+        texture_glyph_t * glyph = texture_glyph_new( );
+        static unsigned char data[4*4*3] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+                                            0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+                                            0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+                                            0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
+        if ( region.x < 0 )
+        {
+            fprintf( stderr, "Texture atlas is full (line %d)\n",  __LINE__ );
+            return NULL;
+        }
+        texture_atlas_set_region( self->atlas, region.x, region.y, 4, 4, data, 0 );
+        glyph->charcode = (wchar_t)(-1);
+        glyph->s0 = (region.x+2)/(float)width;
+        glyph->t0 = (region.y+2)/(float)height;
+        glyph->s1 = (region.x+3)/(float)width;
+        glyph->t1 = (region.y+3)/(float)height;
+        vector_push_back( self->glyphs, &glyph );
+        return glyph; //*(texture_glyph_t **) vector_back( self->glyphs );
+    }
+
+    /* Glyph has not been already loaded */
+    buffer[0] = charcode;
+    if( texture_font_load_glyphs( self, buffer ) == 0 )
+    {
+        return *(texture_glyph_t **) vector_back( self->glyphs );
+    }
+    return NULL;
+}
+
+