Added support for color emoji fonts

parent 1a764f7e
...@@ -18,7 +18,7 @@ AM_CPPFLAGS = $(EXAMPLE_CFLAGS) @FTINC@ -DSOURCE_PATH_SIZE=@SOURCE_PATH_SIZE@ ...@@ -18,7 +18,7 @@ AM_CPPFLAGS = $(EXAMPLE_CFLAGS) @FTINC@ -DSOURCE_PATH_SIZE=@SOURCE_PATH_SIZE@
lib_LTLIBRARIES = libfreetype-gl.la lib_LTLIBRARIES = libfreetype-gl.la
libfreetype_gl_la_SOURCES = texture-atlas.c texture-font.c vector.c utf8-utils.c distance-field.c edtaa3func.c freetype-gl-err.c libfreetype_gl_la_SOURCES = texture-atlas.c texture-font.c vector.c utf8-utils.c distance-field.c edtaa3func.c freetype-gl-err.c
include_HEADERS = freetype-gl.h vec234.h vector.h texture-atlas.h texture-font.h opengl.h distance-field.h edtaa3func.h freetype-gl-err.h freetype-gl-errdef.h include_HEADERS = freetype-gl.h vec234.h vector.h texture-atlas.h texture-font.h opengl.h distance-field.h edtaa3func.h freetype-gl-err.h freetype-gl-errdef.h
libfreetype_gl_la_LIBADD = -lm -l@LIBGL@ -lfreetype -lz @LIBADD@ libfreetype_gl_la_LIBADD = -lm -lfreetype -lz @LIBADD@
distclean-am: clean-am distclean-compile distclean-hdr distclean-libtool distclean-tags distclean-am: clean-am distclean-compile distclean-hdr distclean-libtool distclean-tags
......
## ExampleLib Example: an example of using Automake to link with a library ## ExampleLib Example: an example of using Automake to link with a library
AC_INIT([freetype-gl], [1.3], [bernd@net2o.de], [freetype-gl for Linux/Android], AC_INIT([freetype-gl], [1.4], [bernd@net2o.de], [freetype-gl for Linux/Android],
[http://lonesock.net/soil.html]) [https://github.com/forthy42/freetype-gl])
AC_PREREQ([2.59]) AC_PREREQ([2.59])
AM_INIT_AUTOMAKE([1.10 -Wall no-define]) AM_INIT_AUTOMAKE([1.10 -Wall no-define])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
...@@ -13,10 +13,6 @@ LT_INIT ...@@ -13,10 +13,6 @@ LT_INIT
case $CC in case $CC in
*android*) *android*)
LIBADD="-llog" LIBADD="-llog"
LIBGL=GLESv2
;;
*)
LIBGL=GL
;; ;;
esac esac
...@@ -24,11 +20,10 @@ esac ...@@ -24,11 +20,10 @@ esac
FTINC=`eval $ac_compiler -E -Wp,-v conftest.$ac_ext 2>&1 | grep '^ ' | while read i; do test -f $i/freetype2/ft2build.h && echo -I$i/freetype2; done` FTINC=`eval $ac_compiler -E -Wp,-v conftest.$ac_ext 2>&1 | grep '^ ' | while read i; do test -f $i/freetype2/ft2build.h && echo -I$i/freetype2; done`
rm conftest.$ac_ext rm conftest.$ac_ext
SOURCE_PATH_SIZE=$(echo $PWD | wc -c) SOURCE_PATH_SIZE=0 #$(echo $PWD | wc -c)
AC_SUBST(FTINC) AC_SUBST(FTINC)
AC_SUBST(LIBADD) AC_SUBST(LIBADD)
AC_SUBST(LIBGL)
AC_SUBST(SOURCE_PATH_SIZE) AC_SUBST(SOURCE_PATH_SIZE)
PRECC=${CC%gcc*} PRECC=${CC%gcc*}
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include FT_STROKER_H #include FT_STROKER_H
// #include FT_ADVANCES_H // #include FT_ADVANCES_H
#include FT_LCD_FILTER_H #include FT_LCD_FILTER_H
#include FT_TRUETYPE_TABLES_H
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
...@@ -200,6 +201,7 @@ texture_font_init(texture_font_t *self) ...@@ -200,6 +201,7 @@ texture_font_init(texture_font_t *self)
self->kerning = 1; self->kerning = 1;
self->filtering = 1; self->filtering = 1;
self->scaletex = 1; self->scaletex = 1;
self->scale = 1.0;
// FT_LCD_FILTER_LIGHT is (0x00, 0x55, 0x56, 0x55, 0x00) // FT_LCD_FILTER_LIGHT is (0x00, 0x55, 0x56, 0x55, 0x00)
// FT_LCD_FILTER_DEFAULT is (0x10, 0x40, 0x70, 0x40, 0x10) // FT_LCD_FILTER_DEFAULT is (0x10, 0x40, 0x70, 0x40, 0x10)
...@@ -249,6 +251,16 @@ texture_library_new() ...@@ -249,6 +251,16 @@ texture_library_new()
return self; return self;
} }
// -------------------------------------------------- texture_is_color_font ---
int
texture_is_color_font( texture_font_t *self) {
static const uint32_t tag = FT_MAKE_TAG('C', 'B', 'D', 'T');
unsigned long length = 0;
FT_Load_Sfnt_Table(self->face, tag, 0, NULL, &length);
return length != 0;
}
// --------------------------------------------- texture_font_new_from_file --- // --------------------------------------------- texture_font_new_from_file ---
texture_font_t * texture_font_t *
texture_font_new_from_file(texture_atlas_t *atlas, const float pt_size, texture_font_new_from_file(texture_atlas_t *atlas, const float pt_size,
...@@ -355,7 +367,7 @@ texture_font_load_face( texture_font_t *self, float size ) ...@@ -355,7 +367,7 @@ texture_font_load_face( texture_font_t *self, float size )
if(error) { if(error) {
freetype_error(error, "FT_Error (0x%02x) : %s\n", freetype_error(error, "FT_Error (0x%02x) : %s\n",
FT_Errors[error].code, FT_Errors[error].message); FT_Errors[error].code, FT_Errors[error].message);
goto cleanup; goto cleanup;
} }
} }
...@@ -381,28 +393,54 @@ texture_font_load_face( texture_font_t *self, float size ) ...@@ -381,28 +393,54 @@ texture_font_load_face( texture_font_t *self, float size )
} }
break; break;
} }
}
/* Select charmap */ /* Select charmap */
error = FT_Select_Charmap(self->face, FT_ENCODING_UNICODE); error = FT_Select_Charmap(self->face, FT_ENCODING_UNICODE);
if(error) { if(error) {
freetype_error( error, "FT_Error (%s:%d, code 0x%02x) : %s\n", freetype_error( error, "FT_Error (%s:%d, code 0x%02x) : %s\n",
__FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message); __FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message);
goto cleanup_face; goto cleanup_face;
} }
/* Set char size */ if( texture_is_color_font( self ) ) {
error = FT_Set_Char_Size(self->face, (int)(size * HRES), 0, DPI * HRES, DPI); /* Select best size */
if (self->face->num_fixed_sizes == 0) {
if(error) { freetype_error( error, "FT_Error (%s:%d) : no fixed size in color font\n",
freetype_error( error, "FT_Error (%s:%d, code 0x%02x) : %s\n", __FILENAME__, __LINE__);
__FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message); goto cleanup_face;
goto cleanup_face; }
int best_match = 0;
int diff = abs((int)size - self->face->available_sizes[0].width);
for (int i = 1; i < self->face->num_fixed_sizes; ++i) {
int ndiff = abs((int)size - self->face->available_sizes[i].width);
if (ndiff < diff) {
best_match = i;
diff = ndiff;
}
}
error = FT_Select_Size(self->face, best_match);
self->scale = self->size / self->face->available_sizes[best_match].width;
if(error) {
freetype_error( error, "FT_Error (%s:%d, code 0x%02x) : %s\n",
__FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message);
goto cleanup_face;
}
} else {
/* Set char size */
error = FT_Set_Char_Size(self->face, (int)(size * HRES), 0, DPI * HRES, DPI);
if(error) {
freetype_error( error, "FT_Error (%s:%d, code 0x%02x) : %s\n",
__FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message);
goto cleanup_face;
}
}
/* Set transform matrix */
FT_Set_Transform(self->face, &matrix, NULL);
} }
/* Set transform matrix */
FT_Set_Transform(self->face, &matrix, NULL);
return 1; return 1;
cleanup_face: cleanup_face:
...@@ -572,6 +610,11 @@ texture_font_load_glyph( texture_font_t * self, ...@@ -572,6 +610,11 @@ texture_font_load_glyph( texture_font_t * self,
} }
} }
if( self->atlas->depth == 4 )
{
flags |= FT_LOAD_COLOR;
}
error = FT_Load_Glyph( self->face, glyph_index, flags ); error = FT_Load_Glyph( self->face, glyph_index, flags );
if( error ) if( error )
{ {
...@@ -631,10 +674,17 @@ texture_font_load_glyph( texture_font_t * self, ...@@ -631,10 +674,17 @@ texture_font_load_glyph( texture_font_t * self,
goto cleanup_stroker; goto cleanup_stroker;
} }
if( self->atlas->depth == 1 ) switch( self->atlas->depth ) {
error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1); case 1:
else error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_LCD, 0, 1); break;
case 3:
error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_LCD, 0, 1);
break;
case 4:
error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
break;
}
if( error ) if( error )
{ {
...@@ -671,7 +721,7 @@ cleanup_stroker: ...@@ -671,7 +721,7 @@ cleanup_stroker:
padding.left = 1; padding.left = 1;
} }
size_t src_w = ft_bitmap.width/self->atlas->depth; size_t src_w = ft_bitmap.width;
size_t src_h = ft_bitmap.rows; size_t src_h = ft_bitmap.rows;
size_t tgt_w = src_w + padding.left + padding.right; size_t tgt_w = src_w + padding.left + padding.right;
...@@ -692,14 +742,15 @@ cleanup_stroker: ...@@ -692,14 +742,15 @@ cleanup_stroker:
x = region.x; x = region.x;
y = region.y; y = region.y;
unsigned char *buffer = calloc( tgt_w * tgt_h * self->atlas->depth, sizeof(unsigned char) ); unsigned char *buffer = calloc( tgt_w * tgt_h, self->atlas->depth * sizeof(unsigned char) );
unsigned char *dst_ptr = buffer + (padding.top * tgt_w + padding.left) * self->atlas->depth; unsigned char *dst_ptr = buffer + (padding.top * tgt_w + padding.left) * self->atlas->depth;
unsigned char *src_ptr = ft_bitmap.buffer; unsigned char *src_ptr = ft_bitmap.buffer;
assert( self->atlas->depth == (ft_bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 1) );
for( i = 0; i < src_h; i++ ) for( i = 0; i < src_h; i++ )
{ {
//difference between width and pitch: https://www.freetype.org/freetype2/docs/reference/ft2-basic_types.html#FT_Bitmap //difference between width and pitch: https://www.freetype.org/freetype2/docs/reference/ft2-basic_types.html#FT_Bitmap
memcpy( dst_ptr, src_ptr, ft_bitmap.width); memcpy( dst_ptr, src_ptr, ft_bitmap.width * self->atlas->depth);
dst_ptr += tgt_w * self->atlas->depth; dst_ptr += tgt_w * self->atlas->depth;
src_ptr += ft_bitmap.pitch; src_ptr += ft_bitmap.pitch;
} }
...@@ -718,12 +769,12 @@ cleanup_stroker: ...@@ -718,12 +769,12 @@ cleanup_stroker:
glyph = texture_glyph_new( ); glyph = texture_glyph_new( );
glyph->codepoint = glyph_index ? utf8_to_utf32( codepoint ) : 0; glyph->codepoint = glyph_index ? utf8_to_utf32( codepoint ) : 0;
; ;
glyph->width = tgt_w; glyph->width = tgt_w * self->scale;
glyph->height = tgt_h; glyph->height = tgt_h * self->scale;
glyph->rendermode = self->rendermode; glyph->rendermode = self->rendermode;
glyph->outline_thickness = self->outline_thickness; glyph->outline_thickness = self->outline_thickness;
glyph->offset_x = ft_glyph_left; glyph->offset_x = ft_glyph_left * self->scale;
glyph->offset_y = ft_glyph_top; glyph->offset_y = ft_glyph_top * self->scale;
if(self->scaletex) { if(self->scaletex) {
glyph->s0 = x/(float)self->atlas->width; glyph->s0 = x/(float)self->atlas->width;
glyph->t0 = y/(float)self->atlas->height; glyph->t0 = y/(float)self->atlas->height;
...@@ -736,14 +787,22 @@ cleanup_stroker: ...@@ -736,14 +787,22 @@ cleanup_stroker:
// half a pixel each to get crisp rendering // half a pixel each to get crisp rendering
glyph->s0 = x - 0.5; glyph->s0 = x - 0.5;
glyph->t0 = y - 0.5; glyph->t0 = y - 0.5;
glyph->s1 = x + glyph->width - 0.5; glyph->s1 = x + tgt_w - 0.5;
glyph->t1 = y + glyph->height - 0.5; glyph->t1 = y + tgt_h - 0.5;
} }
// Discard hinting to get advance // Discard hinting to get advance
FT_Load_Glyph( self->face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_HINTING); FT_Load_Glyph( self->face, glyph_index,
((self->atlas->depth == 4) ? FT_LOAD_COLOR : 0) |
FT_LOAD_RENDER | FT_LOAD_NO_HINTING);
slot = self->face->glyph; slot = self->face->glyph;
glyph->advance_x = slot->advance.x / HRESf; if( self->atlas->depth == 4 ) {
glyph->advance_y = slot->advance.y / HRESf; // color fonts use actual pixels, not subpixels
glyph->advance_x = slot->advance.x * self->scale;
glyph->advance_y = slot->advance.y * self->scale;
} else {
glyph->advance_x = slot->advance.x / HRESf;
glyph->advance_y = slot->advance.y / HRESf;
}
texture_font_index_glyph(self, glyph, ucodepoint); texture_font_index_glyph(self, glyph, ucodepoint);
if(!glyph_index) { if(!glyph_index) {
......
...@@ -376,6 +376,11 @@ typedef struct texture_font_t ...@@ -376,6 +376,11 @@ typedef struct texture_font_t
* Whether to scale texture coordinates * Whether to scale texture coordinates
*/ */
int scaletex; int scaletex;
/**
* factor to scale font coordinates
*/
float scale;
} texture_font_t; } texture_font_t;
/** /**
...@@ -397,7 +402,7 @@ typedef struct texture_font_t ...@@ -397,7 +402,7 @@ typedef struct texture_font_t
* texture atlas is used to store glyph on demand. Note the depth of the atlas * texture atlas is used to store glyph on demand. Note the depth of the atlas
* will determine if the font is rendered as alpha channel only (depth = 1) or * will determine if the font is rendered as alpha channel only (depth = 1) or
* RGB (depth = 3) that correspond to subpixel rendering (if available on your * RGB (depth = 3) that correspond to subpixel rendering (if available on your
* freetype implementation). * freetype implementation), or RGBA (depth = 4) for color fonts.
* *
* @param atlas A texture atlas * @param atlas A texture atlas
* @param pt_size Size of font to be created (in points) * @param pt_size Size of font to be created (in points)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment