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