Skip to content

Commit

Permalink
Set LCD parameters, per TTF_Font
Browse files Browse the repository at this point in the history
  • Loading branch information
1bsyl committed Aug 8, 2023
1 parent 71627c9 commit feaf420
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 33 deletions.
13 changes: 8 additions & 5 deletions include/SDL3_ttf/SDL_ttf.h
Original file line number Diff line number Diff line change
Expand Up @@ -2373,21 +2373,23 @@ extern DECLSPEC TTF_SubpixelMode SDLCALL TTF_GetSubpixelMode(void);
* See FreeType function FT_Library_SetLcdGeometry for more details
* Remark: FT_Library_SetLcdGeometry's y coordinates are in reverse from the usual SDL notation (larger = upper as opposed to larger = lower).
*
* \param font the font to configure
* \param pixel_coordinates default positions of color subpixels
* \returns 0 on success, or -1 on error.
*
* \since This function is available since SDL_ttf 3.0.0
*
* \sa TTF_GetSubpixelMode
*/
extern DECLSPEC int SDLCALL TTF_SetLcdGeometry(const int pixel_coordinates[6]);
extern DECLSPEC int SDLCALL TTF_SetLcdGeometry(TTF_Font *font, const int pixel_coordinates[6]);

/**
* Set a filter for ClearType-style LCD rendering.
*
* See FreeType function FT_Library_SetLcdFilter for more details
*
* \params filter filter to use
* \param font the font to configure
* \param filter filter to use
* \returns 0 on success, or -1 on error.
*
* \since This function is available since SDL_ttf 3.0.0
Expand All @@ -2396,22 +2398,23 @@ extern DECLSPEC int SDLCALL TTF_SetLcdGeometry(const int pixel_coordinates[6]);
* \sa TTF_LcdFilter
* \sa TTF_SetLcdFilterWeights
*/
extern DECLSPEC int SDLCALL TTF_SetLcdFilter(TTF_LcdFilter filter);
extern DECLSPEC int SDLCALL TTF_SetLcdFilter(TTF_Font *font, TTF_LcdFilter filter);

/**
* Set a custom filter for ClearType-style LCD rendering.
*
* See FreeType function FT_Library_SetLcdFilterWeights for more details
*
* \params weights A pointer to an array; the function copies the first five bytes and uses them to specify the filter weights in 1/256 units.
* \param font the font to configure
* \param weights A pointer to an array; the function copies the first five bytes and uses them to specify the filter weights in 1/256 units.
* \returns 0 on success, or -1 on error.
*
* \since This function is available since SDL_ttf 3.0.0
*
* \sa TTF_GetSubpixelMode
* \sa TTF_SetLcdFilter
*/
extern DECLSPEC int SDLCALL TTF_SetLcdFilterWeights(unsigned char *weights);
extern DECLSPEC int SDLCALL TTF_SetLcdFilterWeights(TTF_Font *font, unsigned char *weights);

/* Ends C function definitions when using C++ */
#ifdef __cplusplus
Expand Down
159 changes: 131 additions & 28 deletions src/SDL_ttf.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,13 @@ typedef struct PosBuf {
int y;
} PosBuf_t;

typedef enum {
MODE_LCD_DEFAULT = 0, /* no lcd customization */
MODE_LCD_HARMONY = 1,
MODE_LCD_CT = 2,
MODE_LCD_CT_WEIGHTS = 3,
} mode_lcd_t;

/* The structure used to hold internal font information */
struct _TTF_Font {
/* Freetype2 maintains all sorts of useful info itself */
Expand Down Expand Up @@ -298,8 +305,22 @@ struct _TTF_Font {

/* Extra layout setting for wrapped text */
int horizontal_align;

/* LCD filter, per font */
FT_LcdFilter ft_filter; /* ClearType-style */
FT_Vector sub[3]; /* Harmony */
unsigned char weights[5]; /* ClearType-style with weights*/
mode_lcd_t mode_lcd;
};

static SDL_bool g_using_LCD_settings = SDL_FALSE;

/* LCD filter, for the library */
static FT_LcdFilter g_ft_filter;
static FT_Vector g_sub[3];
static unsigned char g_weights[5];
static mode_lcd_t g_mode_lcd;

/* Tell if SDL_ttf has to handle the style */
#define TTF_HANDLE_STYLE_BOLD(font) ((font)->style & TTF_STYLE_BOLD)
#define TTF_HANDLE_STYLE_ITALIC(font) ((font)->style & TTF_STYLE_ITALIC)
Expand Down Expand Up @@ -360,6 +381,8 @@ static SDL_INLINE int Find_GlyphByIndex(TTF_Font *font, FT_UInt idx,

static void Flush_Cache(TTF_Font *font);

static int reapply_lcd_setting(TTF_Font *font);

#if defined(USE_DUFFS_LOOP)

/* 4-times unrolled loop */
Expand Down Expand Up @@ -3465,9 +3488,12 @@ static SDL_Surface* TTF_Render_Internal(TTF_Font *font, const char *text, const
TTF_CHECK_POINTER(font, NULL);
TTF_CHECK_POINTER(text, NULL);

if (render_mode == RENDER_LCD && !FT_IS_SCALABLE(font->face)) {
TTF_SetError("LCD rendering is not available for non-scalable font");
goto failure;
if (render_mode == RENDER_LCD) {
if (!FT_IS_SCALABLE(font->face)) {
TTF_SetError("LCD rendering is not available for non-scalable font");
goto failure;
}
reapply_lcd_setting(font);
}

/* Convert input string to default encoding UTF-8 */
Expand Down Expand Up @@ -3698,9 +3724,12 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text
TTF_CHECK_POINTER(font, NULL);
TTF_CHECK_POINTER(text, NULL);

if (render_mode == RENDER_LCD && !FT_IS_SCALABLE(font->face)) {
TTF_SetError("LCD rendering is not available for non-scalable font");
goto failure;
if (render_mode == RENDER_LCD) {
if (!FT_IS_SCALABLE(font->face)) {
TTF_SetError("LCD rendering is not available for non-scalable font");
goto failure;
}
reapply_lcd_setting(font);
}

/* Convert input string to default encoding UTF-8 */
Expand Down Expand Up @@ -4320,12 +4349,12 @@ TTF_SubpixelMode TTF_GetSubpixelMode(void) {
return TTF_SUBPIXEL_MODE_UNKNOWN;
}

int TTF_SetLcdFilter(TTF_LcdFilter filter) {
int TTF_SetLcdFilter(TTF_Font *font, TTF_LcdFilter filter) {
FT_LcdFilter ft_filter;
FT_Error error;
TTF_SubpixelMode mode;

TTF_CHECK_INITIALIZED(-1);
TTF_CHECK_POINTER(font, -1);

mode = TTF_GetSubpixelMode();
if (mode != TTF_SUBPIXEL_MODE_CLEARTYPE_STYLE) {
Expand All @@ -4347,19 +4376,17 @@ int TTF_SetLcdFilter(TTF_LcdFilter filter) {
return SDL_InvalidParamError("filter");
}

error = FT_Library_SetLcdFilter(library, ft_filter);
if (error) {
TTF_SetFTError("FT_Library_SetLcdFilter", error);
return -1;
}
return 0;
g_using_LCD_settings = SDL_TRUE;
font->mode_lcd = MODE_LCD_CT;
font->ft_filter = ft_filter;
return reapply_lcd_setting(font);
}

int TTF_SetLcdFilterWeights(unsigned char *weights) {
FT_Error error;
int TTF_SetLcdFilterWeights(TTF_Font *font, unsigned char *weights) {
TTF_SubpixelMode mode;

TTF_CHECK_INITIALIZED(-1);
TTF_CHECK_POINTER(font, -1);

mode = TTF_GetSubpixelMode();
if (mode != TTF_SUBPIXEL_MODE_CLEARTYPE_STYLE) {
Expand All @@ -4371,20 +4398,18 @@ int TTF_SetLcdFilterWeights(unsigned char *weights) {
return SDL_InvalidParamError("weights");
}

error = FT_Library_SetLcdFilterWeights(library, weights);
if (error) {
TTF_SetFTError("FT_Library_SetLcdFilterWeights", error);
return -1;
}
return 0;
g_using_LCD_settings = SDL_TRUE;
font->mode_lcd = MODE_LCD_CT_WEIGHTS;
SDL_memcpy(font->weights, weights, 5);
return reapply_lcd_setting(font);
}

int TTF_SetLcdGeometry(const int pixel_coordinates[6]) {
int TTF_SetLcdGeometry(TTF_Font *font, const int pixel_coordinates[6]) {
FT_Vector sub[3];
FT_Error error;
TTF_SubpixelMode mode;

TTF_CHECK_INITIALIZED(-1);
TTF_CHECK_POINTER(font, -1);

mode = TTF_GetSubpixelMode();
if (mode != TTF_SUBPIXEL_MODE_HARMONY) {
Expand All @@ -4399,11 +4424,89 @@ int TTF_SetLcdGeometry(const int pixel_coordinates[6]) {
sub[2].x = pixel_coordinates[4];
sub[2].y = pixel_coordinates[5];

error = FT_Library_SetLcdGeometry(library, sub);
if (error) {
TTF_SetFTError("FT_Library_SetLcdGeometry", error);
return -1;
g_using_LCD_settings = SDL_TRUE;
font->mode_lcd = MODE_LCD_HARMONY;
SDL_memcpy(font->sub, sub, sizeof(sub));
return reapply_lcd_setting(font);
}

static int reapply_lcd_setting(TTF_Font *font) {
FT_Error error;

if (g_using_LCD_settings == SDL_FALSE) {
/* Nothing to do */
return 0;
}

if (g_mode_lcd != font->mode_lcd) {
/* re-apply */
} else {
if (g_mode_lcd == MODE_LCD_CT) {
if (font->ft_filter == g_ft_filter) {
return 0; /* Same mode, nothing to do */
}
}
if (g_mode_lcd == MODE_LCD_CT_WEIGHTS) {
if (SDL_memcmp(font->weights, g_weights, 5) == 0) {
return 0; /* Same mode, nothing to do */
}
}
if (g_mode_lcd == MODE_LCD_HARMONY) {
if (SDL_memcpy(font->sub, g_sub, sizeof(g_sub)) == 0) {
return 0; /* Same mode, nothing to do */
}
}
}

if (font->mode_lcd == MODE_LCD_DEFAULT) {
/* This font was never configure for LCD, switch back to default */
TTF_SubpixelMode mode;
mode = TTF_GetSubpixelMode();
if (mode == TTF_SUBPIXEL_MODE_CLEARTYPE_STYLE) {
font->ft_filter = FT_LCD_FILTER_DEFAULT;
font->mode_lcd = MODE_LCD_CT;
} else if (mode == TTF_SUBPIXEL_MODE_HARMONY) {
FT_Vector sub[3];
sub[0].x = -21;
sub[0].y = 0;
sub[1].x = 0;
sub[1].y = 0;
sub[2].x = 21;
sub[2].y = 0;
SDL_memcpy(font->sub, sub, sizeof(sub));
font->mode_lcd = MODE_LCD_HARMONY;
}
}

/* Need to apply the settings */
if (font->mode_lcd == MODE_LCD_CT) {
error = FT_Library_SetLcdFilter(library, font->ft_filter);
if (error) {
TTF_SetFTError("FT_Library_SetLcdFilter", error);
return -1;
}
g_ft_filter = font->ft_filter;
} else if (font->mode_lcd == MODE_LCD_CT_WEIGHTS) {
error = FT_Library_SetLcdFilterWeights(library, font->weights);
if (error) {
TTF_SetFTError("FT_Library_SetLcdFilterWeights", error);
return -1;
}
SDL_memcpy(g_weights, font->weights, 5);
} else if (font->mode_lcd == MODE_LCD_HARMONY) {
error = FT_Library_SetLcdGeometry(library, font->sub);
if (error) {
TTF_SetFTError("FT_Library_SetLcdGeometry", error);
return -1;
}
SDL_memcpy(g_sub, font->sub, sizeof(g_sub));
}

g_mode_lcd = font->mode_lcd;

/* Since the setting has changed, flush the cache */
Flush_Cache(font);

return 0;
}

Expand Down

0 comments on commit feaf420

Please sign in to comment.