Skip to content

Commit

Permalink
Allow to use HarfBuzz for text shaping
Browse files Browse the repository at this point in the history
  • Loading branch information
1bsyl committed Apr 5, 2019
1 parent 9fced21 commit c715964
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 2 deletions.
19 changes: 18 additions & 1 deletion Android.mk
@@ -1,13 +1,22 @@
# Save the local path
SDL_TTF_LOCAL_PATH := $(call my-dir)

# Enable this if you want to use HarfBuzz
SUPPORT_HARFBUZZ ?= false
HARFBUZZ_LIBRARY_PATH := external/harfbuzz-2.3.1

FREETYPE_LIBRARY_PATH := external/freetype-2.9.1

# Build freetype library
ifneq ($(FREETYPE_LIBRARY_PATH),)
include $(SDL_TTF_LOCAL_PATH)/$(FREETYPE_LIBRARY_PATH)/Android.mk
endif

# Build the library
ifeq ($(SUPPORT_HARFBUZZ),true)
include $(SDL_TTF_LOCAL_PATH)/$(HARFBUZZ_LIBRARY_PATH)/Android.mk
endif

# Restore local path
LOCAL_PATH := $(SDL_TTF_LOCAL_PATH)
include $(CLEAR_VARS)
Expand All @@ -16,13 +25,21 @@ LOCAL_MODULE := SDL2_ttf

LOCAL_C_INCLUDES := $(LOCAL_PATH)

LOCAL_SRC_FILES := SDL_ttf.c
LOCAL_SRC_FILES := SDL_ttf.c.neon

LOCAL_CFLAGS += -O2

ifneq ($(FREETYPE_LIBRARY_PATH),)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(FREETYPE_LIBRARY_PATH)/include
LOCAL_STATIC_LIBRARIES += freetype
endif

ifeq ($(SUPPORT_HARFBUZZ),true)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(HARFBUZZ_LIBRARY_PATH)/src
LOCAL_CFLAGS += -DTTF_USE_HARFBUZZ
LOCAL_STATIC_LIBRARIES += harfbuzz
endif

LOCAL_SHARED_LIBRARIES := SDL2

LOCAL_EXPORT_C_INCLUDES += $(LOCAL_C_INCLUDES)
Expand Down
114 changes: 113 additions & 1 deletion SDL_ttf.c
Expand Up @@ -36,6 +36,40 @@
#include "SDL_ttf.h"
#include "SDL_cpuinfo.h"

#ifndef TTF_USE_HARFBUZZ
# define TTF_USE_HARFBUZZ 0
#endif

#if TTF_USE_HARFBUZZ
#include <hb.h>
#include <hb-ft.h>

/* Default configuration */
static hb_direction_t g_hb_direction = HB_DIRECTION_LTR;
static hb_script_t g_hb_script = HB_SCRIPT_UNKNOWN;
#endif

/* Harfbuzz */
int TTF_SetDirection(int direction) /* hb_direction_t */
{
#if TTF_USE_HARFBUZZ
g_hb_direction = direction;
return 0;
#else
return -1;
#endif
}

int TTF_SetScript(int script) /* hb_script_t */
{
#if TTF_USE_HARFBUZZ
g_hb_script = script;
return 0;
#else
return -1;
#endif
}

/* Round glyph to 16 bytes width and use SSE2 instructions */
#if defined(__SSE2__)
# define HAVE_SSE2_INTRINSICS 1
Expand Down Expand Up @@ -202,6 +236,9 @@ struct _TTF_Font {
/* Hinting modes */
int ft_load_target;
int render_subpixel;
#if TTF_USE_HARFBUZZ
hb_font_t *hb_font;
#endif
};

/* Tell if SDL_ttf has to handle the style */
Expand Down Expand Up @@ -1459,6 +1496,20 @@ TTF_Font* TTF_OpenFontIndexRW(SDL_RWops *src, int freesrc, int ptsize, long inde
return NULL;
}

#if TTF_USE_HARFBUZZ
font->hb_font = hb_ft_font_create(face, NULL);
if (font->hb_font == NULL) {
TTF_SetError("Cannot create harfbuzz font");
TTF_CloseFont(font);
return NULL;
}

/* Default load-flags of hb_ft_font_create is no-hinting.
* So unless you call hb_ft_font_set_load_flags to match what flags you use for rendering,
* you will get mismatching advances and raster. */
hb_ft_font_set_load_flags(font->hb_font, FT_LOAD_DEFAULT | font->ft_load_target);
#endif

if (TTF_SetFontSize(font, ptsize) < 0) {
TTF_SetFTError("Couldn't set font size", error);
TTF_CloseFont(font);
Expand Down Expand Up @@ -1507,6 +1558,11 @@ int TTF_SetFontSize(TTF_Font *font, int ptsize)

Flush_Cache(font);

#if TTF_USE_HARFBUZZ
/* Call when size or variations settings on underlying FT_Face change. */
hb_ft_font_changed(font->hb_font);
#endif

return 0;
}

Expand Down Expand Up @@ -2120,6 +2176,9 @@ static SDL_INLINE int Find_GlyphMetrics(TTF_Font *font, Uint32 ch, c_glyph **out
void TTF_CloseFont(TTF_Font *font)
{
if (font) {
#if TTF_USE_HARFBUZZ
hb_font_destroy(font->hb_font);
#endif
Flush_Cache(font);
if (font->face) {
FT_Done_Face(font->face);
Expand Down Expand Up @@ -2405,10 +2464,18 @@ static int TTF_Size_Internal(TTF_Font *font,
int miny = 0, maxy = 0;
Uint8 *utf8_alloc = NULL;
c_glyph *glyph;
#if TTF_USE_HARFBUZZ
hb_buffer_t *hb_buffer = NULL;
unsigned int g;
unsigned int glyph_count;
hb_glyph_info_t *hb_glyph_info;
hb_glyph_position_t *hb_glyph_position;
#else
size_t textlen;
int skip_first = 1;
FT_UInt prev_index = 0;
FT_Pos prev_delta = 0;
#endif
int prev_advance = 0;

/* Measurement mode */
Expand Down Expand Up @@ -2444,6 +2511,34 @@ static int TTF_Size_Internal(TTF_Font *font,
/* Reset buffer */
font->pos_len = 0;

#if TTF_USE_HARFBUZZ
/* Create a buffer for harfbuzz to use */
hb_buffer = hb_buffer_create();
if (hb_buffer == NULL) {
TTF_SetError("Cannot create harfbuzz buffer");
goto failure;
}

/* Set global configuration */
hb_buffer_set_direction(hb_buffer, g_hb_direction);
hb_buffer_set_script(hb_buffer, g_hb_script);

/* Layout the text */
hb_buffer_add_utf8(hb_buffer, text, -1, 0, -1);
hb_shape(font->hb_font, hb_buffer, NULL, 0);

/* Get the result */
hb_glyph_info = hb_buffer_get_glyph_infos(hb_buffer, &glyph_count);
hb_glyph_position = hb_buffer_get_glyph_positions(hb_buffer, &glyph_count);

/* Load and render each character */
for (g = 0; g < glyph_count; g++)
{
FT_UInt idx = hb_glyph_info[g].codepoint;
int x_advance = hb_glyph_position[g].x_advance;
int x_offset = hb_glyph_position[g].x_offset;
int y_offset = hb_glyph_position[g].y_offset;
#else
/* Load each character and sum it's bounding box */
textlen = SDL_strlen(text);
while (textlen > 0) {
Expand All @@ -2456,6 +2551,7 @@ static int TTF_Size_Internal(TTF_Font *font,
if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) {
continue;
}
#endif
if (Find_GlyphByIndex(font, idx, 0, 0, 0, 0, &glyph, NULL) < 0) {
goto failure;
}
Expand All @@ -2472,6 +2568,12 @@ static int TTF_Size_Internal(TTF_Font *font,
}
}

#if TTF_USE_HARFBUZZ
/* Compute positions */
pos_x = x + x_offset;
pos_y = F26Dot6(font->ascent) - y_offset;
x += x_advance;
#else
/* Compute positions */
x += prev_advance;
prev_advance = glyph->advance;
Expand Down Expand Up @@ -2506,7 +2608,7 @@ static int TTF_Size_Internal(TTF_Font *font,
/* Compute positions where to copy the glyph bitmap */
pos_x = x;
pos_y = F26Dot6(font->ascent);

#endif
/* Store things for Render_Line() */
font->pos_buf[font->pos_len].x = pos_x;
font->pos_buf[font->pos_len].y = pos_y;
Expand Down Expand Up @@ -2565,9 +2667,15 @@ static int TTF_Size_Internal(TTF_Font *font,
}
}

#if TTF_USE_HARFBUZZ
if (hb_buffer) hb_buffer_destroy(hb_buffer);
#endif
if (utf8_alloc) SDL_stack_free(utf8_alloc);
return 0;
failure:
#if TTF_USE_HARFBUZZ
if (hb_buffer) hb_buffer_destroy(hb_buffer);
#endif
if (utf8_alloc) SDL_stack_free(utf8_alloc);
return -1;
}
Expand Down Expand Up @@ -3092,6 +3200,10 @@ void TTF_SetFontHinting(TTF_Font *font, int hinting)
}

font->render_subpixel = (hinting == TTF_HINTING_LIGHT_SUBPIXEL) ? 1 : 0;
#if TTF_USE_HARFBUZZ
/* update flag for HB */
hb_ft_font_set_load_flags(font->hb_font, FT_LOAD_DEFAULT | font->ft_load_target);
#endif

Flush_Cache(font);
}
Expand Down
9 changes: 9 additions & 0 deletions SDL_ttf.h
Expand Up @@ -305,6 +305,15 @@ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Blended(TTF_Font *font,
#define TTF_RenderUNICODE(font, text, fg, bg) \
TTF_RenderUNICODE_Shaded(font, text, fg, bg)

/* Set Direction and Script to be used for text shaping.
- direction is of type hb_direction_t
- script is of type hb_script_t
This functions returns always 0, or -1 if SDL_ttf is not compiled with HarfBuzz
*/
extern DECLSPEC int SDLCALL TTF_SetDirection(int direction); /* hb_direction_t */
extern DECLSPEC int SDLCALL TTF_SetScript(int script); /* hb_script_t */

/* Close an opened font file */
extern DECLSPEC void SDLCALL TTF_CloseFont(TTF_Font *font);

Expand Down
7 changes: 7 additions & 0 deletions external/freetype-2.9.1/Android.mk
Expand Up @@ -50,6 +50,13 @@ LOCAL_SRC_FILES := \


LOCAL_C_INCLUDES += $(LOCAL_PATH)/include

SUPPORT_HARFBUZZ ?= false
ifeq ($(SUPPORT_HARFBUZZ),true)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../harfbuzz-2.3.1/src/
LOCAL_CFLAGS += -DFT_CONFIG_OPTION_USE_HARFBUZZ
endif

LOCAL_CFLAGS += -DFT2_BUILD_LIBRARY -Os


Expand Down

0 comments on commit c715964

Please sign in to comment.