From 9d77a2faac41dd39caeadaa13889d286a3023649 Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Sun, 20 Nov 2016 18:00:01 +0100 Subject: [PATCH 1/2] Implement FT_Face_Option() [1/2] FT_Face_Option() can override certain library or module-wide options on a face-by-face basis. These options are stored in the 'internal' structure of a FT_Face. They are "unset" by default. First up are custom LCD filter weights. * Implement FT_Face_Option() and declare the option tags. * Implement the code to honors the face-level LCD filter option. * Make the default and light filter weights publicly accessible as FT_LCD_FILTER_DEFAULT_WEIGHTS and FT_LCD_FILTER_LIGHT_WEIGHTS. * Modify the signature of FT_Bitmap_LcdFilterFunc to take FT_Byte* instead of FT_Library as the last argument, change _ft_lcd_filter_fir and _ft_lcd_filter_legacy accordingly. * Rename _ft_lcd_filter_fir to ft_lcd_filter_fir and make it accessible from within the FreeType source. --- include/freetype/freetype.h | 80 ++++++++++++++++++++++++++++++++++++++ include/freetype/ftlcdfil.h | 10 +++++ include/freetype/internal/ftobjs.h | 25 +++++++++++- src/base/ftlcdfil.c | 41 ++++++++++--------- src/base/ftobjs.c | 65 +++++++++++++++++++++++++++++++ src/smooth/ftsmooth.c | 62 +++++++++++++++++++++++------ 6 files changed, 250 insertions(+), 33 deletions(-) diff --git a/include/freetype/freetype.h b/include/freetype/freetype.h index f3287b7..0ae2598 100644 --- a/include/freetype/freetype.h +++ b/include/freetype/freetype.h @@ -3578,6 +3578,86 @@ FT_BEGIN_HEADER /*************************************************************************/ + /* + * + * FT_Face_Option + * + * + * This function can be used to set or override certain (library or + * module-wide) options on a face-by-face basis. Useful for finer grained + * control and avoiding locks on shared structures (threads can modify + * their own faces as they see fit). + * + * + * face :: A handle to the source face object. + * num_params :: The number of parameters that follow. + * parameters :: A handle to the num_params FT_Parameters. + * + * + * FreeType error code. 0~means success. + * + * + * The accepted parameters are: + * + * 1) FT_FACE_OPTION_ENABLE_STEM_DARKENING: + * Toggles stem darkening. Overrides the module setting. + * + * Currently implies forcing the native CFF engine for OpenType/CFF + * fonts and the (LIGHT) autohinter for all other outline formats, as + * only those engines have full control over how an outline is + * rendered. + * + * 2) FT_FACE_OPTION_SET_LCD_FILTER_WEIGHTS: + * Sets the weights for the 5-tap in-place FIR filter used by default + * (the legacy intra-pixel filter is not supported). Overrides the + * library setting. See @lcd_filtering. + * + * Pass NULL as data for a tag to reset the option and use the library or + * module default again. + * + * Example: + * { + * // Define FT_CONFIG_OPTION_SUBPIXEL_RENDERING for the LCD filter + * // examples to work. + * + * FT_Parameter parameter1; + * FT_Bool darken_stems = 1; + * parameter1.tag = FT_FACE_OPTION_ENABLE_STEM_DARKENING; + * parameter1.data = &darken_stems; + * + * FT_Parameter parameter2; + * FT_LcdFiveTapFilter custom_weight = { 0x10, 0x40, 0x70, 0x40, 0x10 }; + * parameter2.tag = FT_FACE_OPTION_SET_LCD_FILTER_WEIGHTS; + * parameter2.data = custom_weight; + * + * FT_Parameter parameters[2] = { parameter1, parameter2 }; + * + * FT_Face_Option( face, 2, parameters ); + * + * // Switch options around. + * darken_stems = 0; + * ft_memcpy(parameters[1].data, FT_LCD_FILTER_LIGHT_WEIGHTS, FT_LCD_FILTER_FIVE_TAPS); + * + * FT_Face_Option( face, 2, parameters ); + * + * // Example for a single option. + * FT_Parameter parameter3; + * parameter3.tag = FT_FACE_OPTION_SET_LCD_FILTER_WEIGHTS; + * parameter3.data = NULL; // Reset option to driver/module default + * + * FT_Face_Option( face, 1, ¶meter3 ); + * } + */ + FT_EXPORT( FT_Error ) + FT_Face_Option( FT_Face face, + FT_UInt num_params, + FT_Parameter* parameters); + +#define FT_FACE_OPTION_ENABLE_STEM_DARKENING FT_MAKE_TAG( 'd', 'a', 'r', 'k' ) +#define FT_FACE_OPTION_SET_LCD_FILTER_WEIGHTS FT_MAKE_TAG( 'l', 'c', 'd', 'f' ) + + + /*************************************************************************/ /* */ /* */ /* FT_Get_Name_Index */ diff --git a/include/freetype/ftlcdfil.h b/include/freetype/ftlcdfil.h index e06a895..cc3338f 100644 --- a/include/freetype/ftlcdfil.h +++ b/include/freetype/ftlcdfil.h @@ -197,6 +197,16 @@ FT_BEGIN_HEADER } FT_LcdFilter; + #define FT_LCD_FILTER_FIVE_TAPS 5 + + typedef FT_Byte FT_LcdFiveTapFilter[FT_LCD_FILTER_FIVE_TAPS]; + + static const FT_LcdFiveTapFilter FT_LCD_FILTER_DEFAULT_WEIGHTS = + { 0x08, 0x4d, 0x56, 0x4d, 0x08 }; + static const FT_LcdFiveTapFilter FT_LCD_FILTER_LIGHT_WEIGHTS = + { 0x00, 0x55, 0x56, 0x55, 0x00 }; + + /************************************************************************** * diff --git a/include/freetype/internal/ftobjs.h b/include/freetype/internal/ftobjs.h index 15936f2..d54e4ec 100644 --- a/include/freetype/internal/ftobjs.h +++ b/include/freetype/internal/ftobjs.h @@ -347,6 +347,16 @@ FT_BEGIN_HEADER /* created. @FT_Reference_Face increments this counter, and */ /* @FT_Done_Face only destroys a face if the counter is~1, */ /* otherwise it simply decrements it. */ + /* + * no_stem_darkening :: + * Overrides the module-level default, see e.g. @stem-darkening[cff]. + * FALSE and TRUE toggle stem darkening on and off, respectively, -1 + * means to use the module/driver default. + * + * lcd_weights :: + * Overrides the library default with custom weights for the 5-tap FIR + * filter. {0, 0, 0, 0, 0} means to use the library default. + */ /* */ typedef struct FT_Face_InternalRec_ { @@ -362,6 +372,11 @@ FT_BEGIN_HEADER FT_Int refcount; + FT_Char no_stem_darkening; + +#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING + FT_LcdFiveTapFilter lcd_weights; /* Preset or custom filter weights. */ +#endif } FT_Face_InternalRec; @@ -779,7 +794,13 @@ FT_BEGIN_HEADER typedef void (*FT_Bitmap_LcdFilterFunc)( FT_Bitmap* bitmap, FT_Render_Mode render_mode, - FT_Library library ); + FT_Byte* weights ); + + /* This is the default LCD filter, an in-place, 5-tap FIR filter. */ + FT_BASE( void ) + ft_lcd_filter_fir( FT_Bitmap* bitmap, + FT_Render_Mode mode, + FT_LcdFiveTapFilter weights ); /*************************************************************************/ @@ -875,7 +896,7 @@ FT_BEGIN_HEADER #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING FT_LcdFilter lcd_filter; FT_Int lcd_extra; /* number of extra pixels */ - FT_Byte lcd_weights[5]; /* filter weights, if any */ + FT_LcdFiveTapFilter lcd_weights; /* filter weights, if any */ FT_Bitmap_LcdFilterFunc lcd_filter_func; /* filtering callback */ #endif diff --git a/src/base/ftlcdfil.c b/src/base/ftlcdfil.c index 8bcbed7..2c1a72e 100644 --- a/src/base/ftlcdfil.c +++ b/src/base/ftlcdfil.c @@ -30,12 +30,11 @@ #define USE_LEGACY /* FIR filter used by the default and light filters */ - static void - _ft_lcd_filter_fir( FT_Bitmap* bitmap, - FT_Render_Mode mode, - FT_Library library ) + FT_BASE( void ) + ft_lcd_filter_fir( FT_Bitmap* bitmap, + FT_Render_Mode mode, + FT_LcdFiveTapFilter weights ) { - FT_Byte* weights = library->lcd_weights; FT_UInt width = (FT_UInt)bitmap->width; FT_UInt height = (FT_UInt)bitmap->rows; @@ -176,7 +175,7 @@ static void _ft_lcd_filter_legacy( FT_Bitmap* bitmap, FT_Render_Mode mode, - FT_Library library ) + FT_Byte* weights ) { FT_UInt width = (FT_UInt)bitmap->width; FT_UInt height = (FT_UInt)bitmap->rows; @@ -189,7 +188,7 @@ { 65538 * 1/13, 65538 * 1/6, 65538 * 9/13 } }; - FT_UNUSED( library ); + FT_UNUSED( weights ); /* horizontal in-place intra-pixel filter */ @@ -295,8 +294,8 @@ if ( !weights ) return FT_THROW( Invalid_Argument ); - ft_memcpy( library->lcd_weights, weights, 5 ); - library->lcd_filter_func = _ft_lcd_filter_fir; + ft_memcpy( library->lcd_weights, weights, FT_LCD_FILTER_FIVE_TAPS ); + library->lcd_filter_func = ft_lcd_filter_fir; library->lcd_extra = 2; return FT_Err_Ok; @@ -307,12 +306,6 @@ FT_Library_SetLcdFilter( FT_Library library, FT_LcdFilter filter ) { - static const FT_Byte default_filter[5] = - { 0x08, 0x4d, 0x56, 0x4d, 0x08 }; - static const FT_Byte light_filter[5] = - { 0x00, 0x55, 0x56, 0x55, 0x00 }; - - if ( !library ) return FT_THROW( Invalid_Library_Handle ); @@ -324,14 +317,14 @@ break; case FT_LCD_FILTER_DEFAULT: - ft_memcpy( library->lcd_weights, default_filter, 5 ); - library->lcd_filter_func = _ft_lcd_filter_fir; + ft_memcpy( library->lcd_weights, FT_LCD_FILTER_DEFAULT_WEIGHTS, FT_LCD_FILTER_FIVE_TAPS ); + library->lcd_filter_func = ft_lcd_filter_fir; library->lcd_extra = 2; break; case FT_LCD_FILTER_LIGHT: - ft_memcpy( library->lcd_weights, light_filter, 5 ); - library->lcd_filter_func = _ft_lcd_filter_fir; + ft_memcpy( library->lcd_weights, FT_LCD_FILTER_LIGHT_WEIGHTS, FT_LCD_FILTER_FIVE_TAPS ); + library->lcd_filter_func = ft_lcd_filter_fir; library->lcd_extra = 2; break; @@ -356,6 +349,16 @@ #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ + FT_BASE( void ) + ft_lcd_filter_fir( FT_Bitmap* bitmap, + FT_Render_Mode mode, + FT_LcdFiveTapFilter weights ) + { + FT_UNUSED( bitmap ); + FT_UNUSED( mode ); + FT_UNUSED( weights ); + } + FT_EXPORT_DEF( FT_Error ) FT_Library_SetLcdFilterWeights( FT_Library library, unsigned char *weights ) diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c index c701ebc..cd78424 100644 --- a/src/base/ftobjs.c +++ b/src/base/ftobjs.c @@ -2352,6 +2352,11 @@ internal->transform_delta.y = 0; internal->refcount = 1; + + internal->no_stem_darkening = -1; +#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING + ft_memset( internal->lcd_weights, 0, FT_LCD_FILTER_FIVE_TAPS ); +#endif } if ( aface ) @@ -3506,6 +3511,66 @@ /* documentation is in freetype.h */ + FT_EXPORT_DEF( FT_Error ) + FT_Face_Option( FT_Face face, + FT_UInt num_params, + FT_Parameter* parameters) + { + FT_Error error = FT_Err_Ok; + + + if ( num_params > 0 && !parameters ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + for ( ; num_params > 0; num_params-- ) + { + if ( parameters->tag == FT_FACE_OPTION_ENABLE_STEM_DARKENING ) + { + if ( parameters->data ) + { + if ( *((FT_Bool*)parameters->data) == TRUE ) + face->internal->no_stem_darkening = FALSE; + else + face->internal->no_stem_darkening = TRUE; + } + else /* Use module default. */ + face->internal->no_stem_darkening = -1; + } + else if ( parameters->tag == FT_FACE_OPTION_SET_LCD_FILTER_WEIGHTS ) + { +#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING + if ( parameters->data ) + ft_memcpy( face->internal->lcd_weights, parameters->data, FT_LCD_FILTER_FIVE_TAPS ); + else /* NULL == no custom weights, use library default. Signaled by + filling the weight field with zeros. */ + ft_memset( face->internal->lcd_weights, 0, FT_LCD_FILTER_FIVE_TAPS ); +#else + error = FT_THROW( Unimplemented_Feature ); + goto Exit; +#endif + } + else + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + if ( error ) + break; + + parameters++; + } + + Exit: + return error; + } + + + /* documentation is in freetype.h */ + FT_EXPORT_DEF( FT_UInt ) FT_Face_GetCharVariantIndex( FT_Face face, FT_ULong charcode, diff --git a/src/smooth/ftsmooth.c b/src/smooth/ftsmooth.c index 1371792..f9a50d5 100644 --- a/src/smooth/ftsmooth.c +++ b/src/smooth/ftsmooth.c @@ -122,6 +122,47 @@ FT_Bool have_outline_shifted = FALSE; FT_Bool have_buffer = FALSE; +#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING + FT_Int lcd_extra = 0; + FT_LcdFiveTapFilter lcd_weights = { 0 }; + FT_Bool have_custom_weight = FALSE; + FT_Bitmap_LcdFilterFunc lcd_filter_func = NULL; + + if ( slot->face ) + { + FT_Char weight_i = 0; + + for ( ; weight_i < FT_LCD_FILTER_FIVE_TAPS; weight_i++ ) + if ( slot->face->internal->lcd_weights[weight_i] != 0 ) + { + have_custom_weight = TRUE; + break; + } + } + + /* The LCD filter can be set library-wide and per-face. Face overrides + * library. If the face filter weights are all-zero (the default), it means + * that the library default should be used. */ + if ( have_custom_weight ) + { + /* A per-font filter is set. It will always use the default 5-tap + * in-place FIR filter that needs 2 extra pixels. */ + lcd_filter_func = ft_lcd_filter_fir; + lcd_extra = 2; + ft_memcpy( lcd_weights, slot->face->internal->lcd_weights, FT_LCD_FILTER_FIVE_TAPS ); + } + else + { + /* The face's lcd_weights is {0, 0, 0, 0, 0}, use library default. If the + * library is set to use no LCD filtering (lcd_filter_func == NULL), + * lcd_filter_func here will also be set to NULL and the tests further + * below will pass over the filtering process. */ + lcd_filter_func = slot->library->lcd_filter_func; + lcd_extra = slot->library->lcd_extra; + ft_memcpy( lcd_weights, slot->library->lcd_weights, FT_LCD_FILTER_FIVE_TAPS ); + } +#endif + /* check glyph image format */ if ( slot->format != render->glyph_format ) @@ -178,24 +219,21 @@ #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING - if ( slot->library->lcd_filter_func ) + if ( lcd_filter_func ) { - FT_Int extra = slot->library->lcd_extra; - - if ( hmul ) { - x_shift += 64 * ( extra >> 1 ); - x_left -= extra >> 1; - width += 3 * extra; + x_shift += 64 * ( lcd_extra >> 1 ); + x_left -= lcd_extra >> 1; + width += 3 * lcd_extra; pitch = FT_PAD_CEIL( width, 4 ); } if ( vmul ) { - y_shift += 64 * ( extra >> 1 ); - y_top += extra >> 1; - height += 3 * extra; + y_shift += 64 * ( lcd_extra >> 1 ); + y_top += lcd_extra >> 1; + height += 3 * lcd_extra; } } @@ -299,8 +337,8 @@ if ( error ) goto Exit; - if ( slot->library->lcd_filter_func ) - slot->library->lcd_filter_func( bitmap, mode, slot->library ); + if ( lcd_filter_func ) + lcd_filter_func( bitmap, mode, lcd_weights ); #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ -- 2.9.3