FreeTypeGL-0.0.1: src/texture-font.c
/* ===========================================================================
* 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 "texture-font.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>
#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
#undef FT_ERROR_START_LIST
#undef FT_ERROR_END_LIST
#undef FT_ERRORDEF
#define FT_CHECK_CALL(name, params, err_label) \
do { \
FT_Error ft_err = name params; \
if(0 != ft_err) { \
fprintf(stderr, "FT_ERROR (at %s:%d: " #name "): %d (%s)\n", \
__FILE__, __LINE__, \
FT_Errors[ft_err].code, \
FT_Errors[ft_err].message); \
goto err_label; \
} \
} while(0)
float texture_font_get_kerning(
texture_font_t *self,
FT_UInt glyph_index,
wchar_t prev_char)
{
FT_UInt prev_glyph_index = FT_Get_Char_Index(self->face, prev_char);
FT_Vector kerning;
FT_CHECK_CALL(
FT_Get_Kerning,
( self->face, prev_glyph_index, glyph_index, FT_KERNING_UNFITTED, &kerning ),
Error);
if(kerning.x) {
// 64 * 64 because of 26.6 encoding AND the transform matrix used
// in new_face (hres = 64)
return kerning.x / (float)(64.0f*64.0f);
}
Error: /* No kerning on error */
return 0;
}
static float hres = 64.0f;
static int new_face( FT_Library library,
const char *filename,
FT_Face *face )
{
FT_Matrix matrix = { (int)((1.0/hres) * 0x10000L),
(int)((0.0) * 0x10000L),
(int)((0.0) * 0x10000L),
(int)((1.0) * 0x10000L) };
/* Load face */
FT_CHECK_CALL(FT_New_Face, ( library, filename, 0, face ), Error);
/* Select charmap */
FT_CHECK_CALL(FT_Select_Charmap, ( *face, FT_ENCODING_UNICODE ), DoneFace_Error);
/* Set transform matrix */
FT_Set_Transform( *face, &matrix, NULL );
return 0;
DoneFace_Error:
FT_Done_Face (*face);
Error:
return -1;
}
// ------------------------------------------------------- texture_font_new ---
texture_font_t *texture_font_new(
bool is_lcd, /* atlas->depth == 3 */
const char * filename,
const float size)
{
assert(size > 0);
texture_font_t *self = (texture_font_t *) malloc( sizeof(texture_font_t) );
assert(self);
/* Initialize library */
FT_CHECK_CALL(FT_Init_FreeType, (&self->library), Free_Error);
#ifdef LCD_FEATURES_ENABLED
if(is_lcd) {
FT_CHECK_CALL(
FT_Library_SetLcdFilter, ( self->library, FT_LCD_FILTER_LIGHT ), DoneLibrary_Error);
if(self->filtering) {
FT_CHECK_CALL(
FT_Library_SetLcdFilterWeights, ( self->library, self->lcd_weights ),
DoneLibrary_Error);
}
}
#endif
self->height = 0;
self->ascender = 0;
self->descender = 0;
self->outline_type = TEXTURE_OUTLINE_NONE;
self->outline_thickness = 0.0;
self->hinting = 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;
int res = new_face(self->library, filename, &self->face);
if(0 != res) goto DoneLibrary_Error;
/* Get font metrics at high resolution: */
FT_CHECK_CALL(FT_Set_Char_Size, (self->face, (int)(size*64 * 100), 0, 72*hres, 72),
DoneFace_Error);
// 64 * 64 because of 26.6 encoding AND the transform matrix used
// in texture_font_new_face (hres = 64)
self->underline_position = self->face->underline_position / (float)(64.0f*64.0f) * size;
self->underline_position = floor( 0.5 + self->underline_position );
if( self->underline_position > -2 ) {
self->underline_position = -2.0;
}
self->underline_thickness = self->face->underline_thickness / (float)(64.0f*64.0f) * size;
self->underline_thickness = floor( 0.5 + self->underline_thickness );
if( self->underline_thickness < 1 ) {
self->underline_thickness = 1.0;
}
FT_Size_Metrics metrics = self->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_CHECK_CALL(FT_Set_Char_Size, (self->face, (int)(size*64), 0, 72*hres, 72),
DoneFace_Error);
return self;
DoneFace_Error:
FT_Done_Face(self->face);
DoneLibrary_Error:
FT_Done_FreeType(self->library);
Free_Error:
free(self);
return NULL;
}
// ---------------------------------------------------- texture_font_delete ---
void texture_font_delete( texture_font_t *self )
{
FT_Done_Face(self->face);
FT_Done_FreeType(self->library);
free(self);
}
/* 0 for success */
static int texture_font_get_advance_index(
texture_font_t *self, FT_UInt glyph_index, vec2 *out_advance)
{
// Discard hinting to get advance
FT_CHECK_CALL(FT_Load_Glyph, ( self->face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_HINTING),
Error);
*out_advance =
(vec2){{ self->face->glyph->advance.x/64.0,
self->face->glyph->advance.y/64.0 }};
return 0;
Error:
return -1;
}
/* 0 for success */
static int texture_font_get_advance(texture_font_t *self, wchar_t charcode, vec2 *out_advance)
{
FT_UInt glyph_index = FT_Get_Char_Index( self->face, charcode );
return texture_font_get_advance_index(self, glyph_index, out_advance);
}
/* 0 for success */
int texture_font_load_glyph(
texture_font_t *self, wchar_t charcode, int is_lcd,
texture_font_loaded_glyph_t *out)
{
FT_UInt glyph_index = FT_Get_Char_Index( self->face, charcode );
// WARNING: We use texture-atlas depth to guess if user wants
// LCD subpixel rendering
FT_Int32 flags = 0;
vec2 advance;
if(0 != texture_font_get_advance_index(self, glyph_index, &advance)) goto Error;
if( self->outline_type != TEXTURE_OUTLINE_NONE ) {
flags |= FT_LOAD_NO_BITMAP;
} else {
flags |= FT_LOAD_RENDER;
}
flags |=
self->hinting
? FT_LOAD_FORCE_AUTOHINT
: FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT;
if(is_lcd) flags |= FT_LOAD_TARGET_LCD;
FT_CHECK_CALL(FT_Load_Glyph, ( self->face, glyph_index, flags ), Error);
FT_GlyphSlot slot = self->face->glyph;
if( TEXTURE_OUTLINE_NONE == self->outline_type ) {
*out = (texture_font_loaded_glyph_t){
.ft_glyph = NULL,
.glyph_index = glyph_index,
.bearing = {{ slot->bitmap_left, slot->bitmap_top }},
.bitmap = &slot->bitmap,
.advance = advance,
};
return 0;
}
FT_Glyph ft_glyph;
/* Get_Glyph must be accompanied by Done_Glyph */
FT_CHECK_CALL(FT_Get_Glyph, (slot, &ft_glyph), Error);
FT_Stroker stroker;
FT_CHECK_CALL(FT_Stroker_New, (self->library, &stroker), DoneGlyph_Error);
FT_Stroker_Set( stroker,
(int)(self->outline_thickness * 64),
FT_STROKER_LINECAP_ROUND,
FT_STROKER_LINEJOIN_ROUND,
0);
switch(self->outline_type) {
case TEXTURE_OUTLINE_LINE:
FT_CHECK_CALL(FT_Glyph_Stroke, ( &ft_glyph, stroker, 1 ), StrokerDone_Error);
break;
case TEXTURE_OUTLINE_INNER:
FT_CHECK_CALL(FT_Glyph_StrokeBorder, ( &ft_glyph, stroker, 0, 1 ), StrokerDone_Error);
break;
case TEXTURE_OUTLINE_OUTER:
FT_CHECK_CALL(FT_Glyph_StrokeBorder, ( &ft_glyph, stroker, 1, 1 ), StrokerDone_Error);
break;
}
FT_Render_Mode render_mode = is_lcd ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_NORMAL;
FT_CHECK_CALL(FT_Glyph_To_Bitmap, ( &ft_glyph, render_mode, 0, 1), StrokerDone_Error);
FT_BitmapGlyph ft_bitmap_glyph = (FT_BitmapGlyph) ft_glyph;
*out = (texture_font_loaded_glyph_t){
.ft_glyph = ft_glyph,
.glyph_index = glyph_index,
.bearing = (ivec2){{ ft_bitmap_glyph->left, ft_bitmap_glyph->top }},
.bitmap = &ft_bitmap_glyph->bitmap,
.advance = advance,
};
FT_Stroker_Done(stroker);
return 0;
StrokerDone_Error:
FT_Stroker_Done(stroker);
DoneGlyph_Error:
FT_Done_Glyph( ft_glyph );
Error:
return -1;
}
void texture_font_done_glyph(texture_font_t *self, texture_font_loaded_glyph_t *loaded)
{
if(self->outline_type != TEXTURE_OUTLINE_NONE) {
FT_Done_Glyph( loaded->ft_glyph );
}
}
/* 0 for success */
int texture_font_get_text_size(texture_font_t *self, wchar_t *text, vec2 *out_size)
{
float maxwidth = 0;
float width = 0;
unsigned lines = 1;
size_t i;
for(i=0; text[i]; ++i) {
if (text[i] == L'\n') {
if(width > maxwidth) maxwidth = width;
width = 0;
lines++;
continue;
}
vec2 advance;
if(0 != texture_font_get_advance(self, text[i], &advance)) return -1;
width += advance.x;
}
if(width > maxwidth) maxwidth = width;
*out_size = (vec2){{ maxwidth, lines * self->height }};
return 0;
}