From 8765c71b41625406fe87598645d842ca8451983c Mon Sep 17 00:00:00 2001 From: David Turner Date: Fri, 10 Nov 2006 16:49:42 +0000 Subject: [PATCH] * include/freetype/ftlcdfil.h, include/internal/ftobjs.h, src/base/ftlcdfilt.c, src/smooth/ftsmooth.c: API change for the LCD filter, the FT_LcdFilter value is a enum describing which filter to apply, new values FT_LCD_FILTER_LIGHT and FT_LCD_FILTER_LEGACY (the latter implements the LibXft original algorithm which produces incredible color fringes for everything except very-well hinted text) * src/autofit/aflatin.c: various tiny improvements that drastically improve the handling of serif fonts and of LCD/LCD_V hinting modes. --- ChangeLog | 13 +++ README | 2 +- include/freetype/ftlcdfil.h | 75 ++++++++----- include/freetype/internal/ftobjs.h | 9 +- src/autofit/aflatin.c | 23 +++- src/base/ftlcdfil.c | 168 +++++++++++++++++++++++++---- src/smooth/ftsmooth.c | 45 ++++---- 7 files changed, 255 insertions(+), 80 deletions(-) diff --git a/ChangeLog b/ChangeLog index b9e0b53f2..d20995580 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2006-11-10 David Turner + + * include/freetype/ftlcdfil.h, include/internal/ftobjs.h, + src/base/ftlcdfilt.c, src/smooth/ftsmooth.c: API change for + the LCD filter, the FT_LcdFilter value is a enum describing + which filter to apply, new values FT_LCD_FILTER_LIGHT and + FT_LCD_FILTER_LEGACY (the latter implements the LibXft original + algorithm which produces incredible color fringes for everything + except very-well hinted text) + + * src/autofit/aflatin.c: various tiny improvements that drastically + improve the handling of serif fonts and of LCD/LCD_V hinting modes. + 2006-11-09 David Turner * src/pshinter/pshalgo.c (psh_glyph_compute_inflections): Fix diff --git a/README b/README index 0116c3da9..99750988c 100644 --- a/README +++ b/README @@ -51,7 +51,7 @@ ---------------------------------------------------------------------- -Copyright 2001, 2002, 2003, 2004, 2006 by +Copyright 2001-2006 by David Turner, Robert Wilhelm, and Werner Lemberg. This file is part of the FreeType project, and may only be used, diff --git a/include/freetype/ftlcdfil.h b/include/freetype/ftlcdfil.h index 5882921e6..132c8d019 100644 --- a/include/freetype/ftlcdfil.h +++ b/include/freetype/ftlcdfil.h @@ -27,6 +27,51 @@ FT_BEGIN_HEADER + /**************************************************************************** + * + * @func: + * FT_LcdFilter + * + * @description: + * a list of values used to identify various types of LCD filters + * + * @values: + * FT_LCD_FILTER_NONE :: value 0 means do not perform filtering. when + * used with subpixel rendering, this will result in sometimes severe + * color fringes + * + * FT_LCD_FILTER_DEFAULT :: + * the default filter reduces color fringes considerably, at the cost of + * a slight bluriness in the output + * + * FT_LCD_FILTER_LIGHT :: + * the light filter is a variant that produces less bluriness + * at the cost of slightly more color fringes than the default one. It + * might be better than the default one, depending on your monitor and + * personal vision. + * + * FT_LCD_FILTER_LEGACY :: + * this filter corresponds to the original libXft color filter, this + * provides high contrast output, but can exhibit really bad color fringes + * if your glyphs are not extremely well hinted to the pixel grid. In + * other words, it only works well when enabling the TrueType bytecode + * interpreter *and* using high-quality hinted fonts. It will suck for + * all other cases. + * + * this filter is only provided for comparison purposes, and might be + * disabled/unsupported in the future... + */ + typedef enum + { + FT_LCD_FILTER_NONE = 0, + FT_LCD_FILTER_DEFAULT = 1, + FT_LCD_FILTER_LIGHT = 2, + FT_LCD_FILTER_LEGACY = 16, + + FT_LCD_FILTER_MAX /* do not remove */ + + } FT_LcdFilter; + /************************************************************************** * @@ -85,34 +130,8 @@ FT_BEGIN_HEADER * */ FT_EXPORT( FT_Error ) - FT_Library_SetLcdFilter( FT_Library library, - const FT_Byte* filter_weights ); - - - - /************************************************************************** - * - * @enum: - * FT_LCD_FILTER_XXX - * - * @description: - * A list of constants which correspond to useful lcd filter settings - * for the @FT_Library_SetLcdFilter function. - * - * @values: - * FT_LCD_FILTER_NONE :: - * The value NULL is reserved to indicate that LCD color filtering - * should be disabled. - * - * FT_LCD_FILTER_DEFAULT :: - * This value is reserved to indicate a default FIR filter that should - * work well on most LCD screen. It corresponds to the array 0x10, - * 0x40, 0x70, 0x40, 0x10. - * - */ -#define FT_LCD_FILTER_NONE ( (const FT_Byte*)NULL ) - -#define FT_LCD_FILTER_DEFAULT ( (const FT_Byte*)(void*)(ft_ptrdiff_t)1 ) + FT_Library_SetLcdFilter( FT_Library library, + FT_LcdFilter filter ); /* */ diff --git a/include/freetype/internal/ftobjs.h b/include/freetype/internal/ftobjs.h index f57f0ac25..2f497582c 100644 --- a/include/freetype/internal/ftobjs.h +++ b/include/freetype/internal/ftobjs.h @@ -29,6 +29,7 @@ #include #include FT_RENDER_H #include FT_SIZES_H +#include FT_LCD_FILTER_H #include FT_INTERNAL_MEMORY_H #include FT_INTERNAL_GLYPH_LOADER_H #include FT_INTERNAL_DRIVER_H @@ -637,7 +638,7 @@ FT_BEGIN_HEADER typedef void (*FT_Bitmap_LcdFilterFunc)( FT_Bitmap* bitmap, FT_Render_Mode render_mode, - FT_Byte* weights ); + FT_Library library ); /*************************************************************************/ @@ -714,8 +715,10 @@ FT_BEGIN_HEADER FT_DebugHook_Func debug_hooks[4]; #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING - FT_Byte lcd_filter_weights[5]; - FT_Bitmap_LcdFilterFunc lcd_filter; + FT_LcdFilter lcd_filter; + FT_Int lcd_extra; /* number of extra pixels */ + FT_Byte lcd_weights[7]; /* filter weights, if any */ + FT_Bitmap_LcdFilterFunc lcd_filter_func; /* filtering callback */ #endif } FT_LibraryRec; diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c index bb333ba03..a8107356a 100644 --- a/src/autofit/aflatin.c +++ b/src/autofit/aflatin.c @@ -965,7 +965,7 @@ * to avoid many problems with serif fonts. We compute the * corresponding threshold in font units. */ - if ( dim == AF_DIMENSION_VERT ) + if ( dim == AF_DIMENSION_HORZ ) segment_length_threshold = FT_DivFix( 64, hints->y_scale ); else segment_length_threshold = 0; @@ -1536,6 +1536,7 @@ else { /* strong hinting process: snap the stem width to integer pixels */ + FT_Pos org_dist = dist; dist = af_latin_snap_width( axis->widths, axis->width_count, dist ); @@ -1571,7 +1572,27 @@ dist = ( dist + 64 ) >> 1; else if ( dist < 128 ) + { + /* ok, we're only going to round to an integer width if + * the corresponding distorsion is less than 1/4 pixel + * otherwise, this really screws everything, since the + * diagonals, which are not hinted, will appear a lot + * more bolder or thinner than the vertical stems + */ + FT_Int delta; + dist = ( dist + 22 ) & ~63; + delta = dist - org_dist; + if ( delta < 0 ) + delta = -delta; + + if (delta >= 16) + { + dist = org_dist; + if ( dist < 48 ) + dist = (dist + 64) >> 1; + } + } else /* round otherwise to prevent color fringes in LCD mode */ dist = ( dist + 32 ) & ~63; diff --git a/src/base/ftlcdfil.c b/src/base/ftlcdfil.c index 1aa7a6c83..4529cf212 100644 --- a/src/base/ftlcdfil.c +++ b/src/base/ftlcdfil.c @@ -24,14 +24,17 @@ #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING - /* The smooth rasterizer invokes this function. */ +#define USE_LEGACY + + /* FIR filter used by the default and light filters */ static void - _ft_lcd_filter( FT_Bitmap* bitmap, - FT_Render_Mode mode, - FT_Byte* weights ) + _ft_lcd_filter_fir( FT_Bitmap* bitmap, + FT_Render_Mode mode, + FT_Library library ) { - FT_UInt width = (FT_UInt)bitmap->width; - FT_UInt height = (FT_UInt)bitmap->rows; + FT_Byte* weights = library->lcd_weights; + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; /* horizontal in-place FIR filter */ @@ -155,39 +158,160 @@ } - FT_EXPORT( FT_Error ) - FT_Library_SetLcdFilter( FT_Library library, - const FT_Byte* filter_weights ) +#ifdef USE_LEGACY + /* FIR filter used by the default and light filters */ + static void + _ft_lcd_filter_legacy( FT_Bitmap* bitmap, + FT_Render_Mode mode, + FT_Library library ) { - static const FT_Byte default_filter[5] = { 0x10, 0x40, 0x70, 0x40, 0x10 }; + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; + FT_Int pitch = bitmap->pitch; + static const int filters[3][3] = + { + { 65538*9/13, 65538*1/6, 65538*1/13 }, + { 65538*3/13, 65538*4/6, 65538*3/13 }, + { 65538*1/13, 65538*1/6, 65538*9/13 } + }; + + FT_UNUSED(library); + + /* horizontal in-place FIR filter */ + if ( mode == FT_RENDER_MODE_LCD && width >= 3 ) + { + FT_Byte* line = bitmap->buffer; + + + for ( ; height > 0; height--, line += pitch ) + { + FT_UInt xx; + + for ( xx = 0; xx < width; xx += 3 ) + { + FT_UInt r = 0; + FT_UInt g = 0; + FT_UInt b = 0; + FT_UInt p; + + p = line[xx]; + r += filters[0][0]*p; + g += filters[0][1]*p; + b += filters[0][2]*p; + + p = line[xx+1]; + r += filters[1][0]*p; + g += filters[1][1]*p; + b += filters[1][2]*p; + + p = line[xx+2]; + r += filters[2][0]*p; + g += filters[2][1]*p; + b += filters[2][2]*p; + + line[xx] = (FT_Byte)( r / 65536 ); + line[xx+1] = (FT_Byte)( g / 65536 ); + line[xx+2] = (FT_Byte)( b / 65536 ); + } + } + } + else if ( mode == FT_RENDER_MODE_LCD_V && height >= 3 ) + { + FT_Byte* column = bitmap->buffer; + + for ( ; width > 0; width--, column++ ) + { + FT_Byte* col = column; + FT_Byte* col_end = col + height*pitch; + + for ( ; col < col_end; col += 3*pitch ) + { + FT_UInt r = 0; + FT_UInt g = 0; + FT_UInt b = 0; + FT_UInt p; + + p = col[0]; + r += filters[0][0]*p; + g += filters[0][1]*p; + b += filters[0][2]*p; + + p = col[pitch]; + r += filters[1][0]*p; + g += filters[1][1]*p; + b += filters[1][2]*p; + + p = col[pitch*2]; + r += filters[2][0]*p; + g += filters[2][1]*p; + b += filters[2][2]*p; + + col[0] = (FT_Byte)( r / 65536 ); + col[pitch] = (FT_Byte)( g / 65536 ); + col[2*pitch] = (FT_Byte)( b / 65536 ); + } + } + } + } +#endif /* USE_LEGACY */ + + FT_EXPORT( FT_Error ) + FT_Library_SetLcdFilter( FT_Library library, + FT_LcdFilter filter ) + { + static const FT_Byte light_filter[5] = { 0, 85, 86, 85, 0 }; + static const FT_Byte default_filter[5] = { 0x10, 0x40, 0x70, 0x40, 0x10 }; if ( library == NULL ) return FT_Err_Invalid_Argument; - if ( filter_weights == FT_LCD_FILTER_NONE ) + switch ( filter ) { - library->lcd_filter = NULL; - return 0; + case FT_LCD_FILTER_NONE: + library->lcd_filter_func = NULL; + library->lcd_extra = 0; + break; + + case FT_LCD_FILTER_DEFAULT: +#if 0 /* DEBUGGING */ + library->lcd_filter_func = _ft_lcd_filter_legacy; + library->lcd_extra = 0; +#else + memcpy( library->lcd_weights, default_filter, 5 ); + library->lcd_filter_func = _ft_lcd_filter_fir; + library->lcd_extra = 2; +#endif + break; + + case FT_LCD_FILTER_LIGHT: + memcpy( library->lcd_weights, light_filter, 5 ); + library->lcd_filter_func = _ft_lcd_filter_fir; + library->lcd_extra = 2; + break; + +#ifdef USE_LEGACY + case FT_LCD_FILTER_LEGACY: + library->lcd_filter_func = _ft_lcd_filter_legacy; + library->lcd_extra = 0; + break; +#endif + default: + return FT_Err_Invalid_Argument; } - if ( filter_weights == FT_LCD_FILTER_DEFAULT ) - filter_weights = default_filter; - - memcpy( library->lcd_filter_weights, filter_weights, 5 ); - library->lcd_filter = _ft_lcd_filter; - + library->lcd_filter = filter; return 0; } #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ FT_EXPORT( FT_Error ) - FT_Library_SetLcdFilter( FT_Library library, - const FT_Byte* filter_weights ) + FT_Library_SetLcdFilter( FT_Library library, + FT_LcdFilter filter ) { FT_UNUSED( library ); - FT_UNUSED( filter_weights ); + FT_UNUSED( filter ); return FT_Err_Unimplemented_Feature; } diff --git a/src/smooth/ftsmooth.c b/src/smooth/ftsmooth.c index ad19645a1..c7e4c7a4b 100644 --- a/src/smooth/ftsmooth.c +++ b/src/smooth/ftsmooth.c @@ -171,20 +171,22 @@ #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING - if ( slot->library->lcd_filter ) + if ( slot->library->lcd_filter_func ) { + FT_Int extra = slot->library->lcd_extra; + if ( hmul ) { - x_shift -= 64; - width += 6; + x_shift -= 64*(extra >> 1); + width += 3*extra; pitch = FT_PAD_CEIL( width, 4 ); - x_left -= 1; + x_left -= (extra >> 1); } if ( vmul ) { - y_shift -= 64; - height += 6; - y_top += 1; + y_shift -= 64*(extra >> 1); + height += 3*extra; + y_top += (extra >> 1); } } @@ -213,20 +215,17 @@ /* implode outline if needed */ { - FT_Int n; + FT_Vector* points = outline->points; + FT_Vector* points_end = points + outline->n_points; FT_Vector* vec; if ( hmul ) - for ( vec = outline->points, n = 0; - n < outline->n_points; - n++, vec++ ) + for ( vec = points; vec < points_end; vec++ ) vec->x *= 3; if ( vmul ) - for ( vec = outline->points, n = 0; - n < outline->n_points; - n++, vec++ ) + for ( vec = points; vec < points_end; vec++ ) vec->y *= 3; } @@ -235,26 +234,22 @@ /* deflate outline if needed */ { - FT_Int n; + FT_Vector* points = outline->points; + FT_Vector* points_end = points + outline->n_points; FT_Vector* vec; if ( hmul ) - for ( vec = outline->points, n = 0; - n < outline->n_points; - n++, vec++ ) + for ( vec = points; vec < points_end; vec++ ) vec->x /= 3; if ( vmul ) - for ( vec = outline->points, n = 0; - n < outline->n_points; - n++, vec++ ) + for ( vec = points; vec < points_end; vec++ ) vec->y /= 3; } - if ( slot->library->lcd_filter ) - slot->library->lcd_filter( bitmap, mode, - slot->library->lcd_filter_weights ); + if ( slot->library->lcd_filter_func ) + slot->library->lcd_filter_func( bitmap, mode, slot->library ); #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ @@ -264,7 +259,7 @@ /* expand it horizontally */ if ( hmul ) { - FT_Byte* line = bitmap->buffer + ( height - height_org ) * pitch; + FT_Byte* line = bitmap->buffer; FT_UInt hh;