diff --git a/ChangeLog b/ChangeLog index 9008b6b53..f2b4dc168 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,31 @@ +2017-02-16 Nikolaus Waxweiler + Werner Lemberg + + Add face property for LCD filter weights. + + * include/freetype/ftlcdfil.h (FT_PARAM_TAG_LCD_FILTER_WEIGHTS, + FT_LCD_FILTER_FIVE_TAPS): New macros. + (FT_LcdFiveTapFilter): New typedef. + + * include/freetype/ftobjs.h (FT_Face_InternalRec) + [FT_CONFIG_OPTION_SUBPIXEL_RENDERING]: Add `lcd_weights' field. + (FT_Bitmap_LcdFilterFunc): Change third argument to weights array. + (ft_lcd_filter_fir): New prototype. + (FT_LibraryRec): Updated. + + * src/base/ftlcdfil.c (_ft_lcd_filter_fir): Renamed to... + (ft_lcd_filter_dir): ... this base function. + Updated. + (_ft_lcd_filter_legacy): Updated. + (FT_Library_SetLcdFilterWeights, FT_Library_SetLcdFilter): Updated. + + * src/base/ftobjs.c (ft_open_face_internal): Updated. + (FT_Face_Properties): Handle FT_PARAM_TAG_LCD_FILTER_WEIGHTS. + + * src/smooth/ftsmooth.c (ft_smooth_render_generic) + [FT_CONFIG_OPTION_SUBPIXEL_RENDERING: Handle LCD weights from + `FT_Face_Internal'. + 2017-02-14 Nikolaus Waxweiler Werner Lemberg diff --git a/include/freetype/freetype.h b/include/freetype/freetype.h index fd7d445ca..b902bc599 100644 --- a/include/freetype/freetype.h +++ b/include/freetype/freetype.h @@ -3629,6 +3629,12 @@ FT_BEGIN_HEADER * Note that only a subset of the available properties can be * controlled. * + * * LCD filter weights (@FT_PARAM_TAG_LCD_FILTER_WEIGHTS, corresponding + * to function @FT_Library_SetLcdFilterWeights). + * + * Pass NULL as `data' in @FT_Parameter for a given tag to reset the + * option and use the library or module default again. + * * @input: * face :: * A handle to the source face object. diff --git a/include/freetype/ftlcdfil.h b/include/freetype/ftlcdfil.h index 928bb0f3e..181d4db4e 100644 --- a/include/freetype/ftlcdfil.h +++ b/include/freetype/ftlcdfil.h @@ -275,6 +275,37 @@ FT_BEGIN_HEADER FT_Library_SetLcdFilterWeights( FT_Library library, unsigned char *weights ); + + /* + * @constant: + * FT_PARAM_TAG_LCD_FILTER_WEIGHTS + * + * @description: + * An @FT_Parameter tag to be used with @FT_Face_Properties. The + * corresponding argument specifies the five LCD filter weights for a + * given face (if using @FT_LOAD_TARGET_LCD, for example), overriding + * the global default values or the values set up with + * @FT_Library_Set_LcdFilterWeights. + * + */ +#define FT_PARAM_TAG_LCD_FILTER_WEIGHTS \ + FT_MAKE_TAG( 'l', 'c', 'd', 'f' ) + + + /* + * @type: + * FT_LcdFiveTapFilter + * + * @description: + * A typedef for passing the five LCD filter weights to + * @FT_Face_Properties within an @FT_Parameter structure. + * + */ +#define FT_LCD_FILTER_FIVE_TAPS 5 + + typedef FT_Byte FT_LcdFiveTapFilter[FT_LCD_FILTER_FIVE_TAPS]; + + /* */ diff --git a/include/freetype/internal/ftobjs.h b/include/freetype/internal/ftobjs.h index 772d82c31..cd2697ebc 100644 --- a/include/freetype/internal/ftobjs.h +++ b/include/freetype/internal/ftobjs.h @@ -348,6 +348,10 @@ FT_BEGIN_HEADER /* @FT_Done_Face only destroys a face if the counter is~1, */ /* otherwise it simply decrements it. */ /* */ + /* 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_ { FT_Matrix transform_matrix; @@ -362,6 +366,10 @@ FT_BEGIN_HEADER FT_Int refcount; +#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING + FT_LcdFiveTapFilter lcd_weights; /* preset or custom filter weights */ +#endif + } FT_Face_InternalRec; @@ -775,12 +783,19 @@ FT_BEGIN_HEADER /* This hook is used by the TrueType debugger. It must be set to an */ /* alternate truetype bytecode interpreter function. */ -#define FT_DEBUG_HOOK_TRUETYPE 0 +#define FT_DEBUG_HOOK_TRUETYPE 0 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 ); /*************************************************************************/ @@ -876,7 +891,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 f8e1709e9..611b39f57 100644 --- a/src/base/ftlcdfil.c +++ b/src/base/ftlcdfil.c @@ -30,14 +30,13 @@ #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; + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; /* horizontal in-place FIR filter */ @@ -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,10 +306,10 @@ 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 }; + static const FT_LcdFiveTapFilter default_weights = + { 0x08, 0x4d, 0x56, 0x4d, 0x08 }; + static const FT_LcdFiveTapFilter light_weights = + { 0x00, 0x55, 0x56, 0x55, 0x00 }; if ( !library ) @@ -324,14 +323,18 @@ 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, + 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, + light_weights, + FT_LCD_FILTER_FIVE_TAPS ); + library->lcd_filter_func = ft_lcd_filter_fir; library->lcd_extra = 2; break; @@ -356,6 +359,17 @@ #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 c2ab2d1a7..5be56a14a 100644 --- a/src/base/ftobjs.c +++ b/src/base/ftobjs.c @@ -2424,6 +2424,10 @@ internal->transform_delta.y = 0; internal->refcount = 1; + +#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING + ft_memset( internal->lcd_weights, 0, FT_LCD_FILTER_FIVE_TAPS ); +#endif } if ( aface ) @@ -3607,8 +3611,31 @@ for ( ; num_properties > 0; num_properties-- ) { - error = FT_THROW( Invalid_Argument ); - goto Exit; + if ( properties->tag == FT_PARAM_TAG_LCD_FILTER_WEIGHTS ) + { +#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING + if ( properties->data ) + ft_memcpy( face->internal->lcd_weights, + properties->data, + FT_LCD_FILTER_FIVE_TAPS ); + else + { + /* Value NULL indicates `no custom weights, use library */ + /* defaults', 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; diff --git a/src/smooth/ftsmooth.c b/src/smooth/ftsmooth.c index c9292df35..435854e67 100644 --- a/src/smooth/ftsmooth.c +++ b/src/smooth/ftsmooth.c @@ -122,6 +122,60 @@ 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 i; + + + for ( i = 0; i < FT_LCD_FILTER_FIVE_TAPS; i++ ) + if ( slot->face->internal->lcd_weights[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 always uses the default 5-tap + * in-place FIR filter that needs 2 extra pixels. + */ + ft_memcpy( lcd_weights, + slot->face->internal->lcd_weights, + FT_LCD_FILTER_FIVE_TAPS ); + lcd_filter_func = ft_lcd_filter_fir; + lcd_extra = 2; + } + else + { + /* + * The face's lcd_weights is {0, 0, 0, 0, 0}, meaning `use library + * default'. If the library is set to use no LCD filtering + * (lcd_filter_func == NULL), `lcd_filter_func' here is also set to + * NULL and the tests further below pass over the filtering process. + */ + ft_memcpy( lcd_weights, + slot->library->lcd_weights, + FT_LCD_FILTER_FIVE_TAPS ); + lcd_filter_func = slot->library->lcd_filter_func; + lcd_extra = slot->library->lcd_extra; + } + +#endif /*FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ /* check glyph image format */ if ( slot->format != render->glyph_format ) @@ -177,28 +231,23 @@ height *= 3; #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; } } - #endif /* @@ -299,8 +348,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 */