* 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.
This commit is contained in:
David Turner 2006-11-10 16:49:42 +00:00
parent 49c77a87c3
commit 8765c71b41
7 changed files with 255 additions and 80 deletions

View File

@ -1,3 +1,16 @@
2006-11-10 David Turner <david@freetype.org>
* 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 <david@freetype.org> 2006-11-09 David Turner <david@freetype.org>
* src/pshinter/pshalgo.c (psh_glyph_compute_inflections): Fix * src/pshinter/pshalgo.c (psh_glyph_compute_inflections): Fix

2
README
View File

@ -51,7 +51,7 @@
---------------------------------------------------------------------- ----------------------------------------------------------------------
Copyright 2001, 2002, 2003, 2004, 2006 by Copyright 2001-2006 by
David Turner, Robert Wilhelm, and Werner Lemberg. David Turner, Robert Wilhelm, and Werner Lemberg.
This file is part of the FreeType project, and may only be used, This file is part of the FreeType project, and may only be used,

View File

@ -27,6 +27,51 @@
FT_BEGIN_HEADER 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_EXPORT( FT_Error )
FT_Library_SetLcdFilter( FT_Library library, FT_Library_SetLcdFilter( FT_Library library,
const FT_Byte* filter_weights ); FT_LcdFilter filter );
/**************************************************************************
*
* @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 )
/* */ /* */

View File

@ -29,6 +29,7 @@
#include <ft2build.h> #include <ft2build.h>
#include FT_RENDER_H #include FT_RENDER_H
#include FT_SIZES_H #include FT_SIZES_H
#include FT_LCD_FILTER_H
#include FT_INTERNAL_MEMORY_H #include FT_INTERNAL_MEMORY_H
#include FT_INTERNAL_GLYPH_LOADER_H #include FT_INTERNAL_GLYPH_LOADER_H
#include FT_INTERNAL_DRIVER_H #include FT_INTERNAL_DRIVER_H
@ -637,7 +638,7 @@ FT_BEGIN_HEADER
typedef void (*FT_Bitmap_LcdFilterFunc)( FT_Bitmap* bitmap, typedef void (*FT_Bitmap_LcdFilterFunc)( FT_Bitmap* bitmap,
FT_Render_Mode render_mode, FT_Render_Mode render_mode,
FT_Byte* weights ); FT_Library library );
/*************************************************************************/ /*************************************************************************/
@ -714,8 +715,10 @@ FT_BEGIN_HEADER
FT_DebugHook_Func debug_hooks[4]; FT_DebugHook_Func debug_hooks[4];
#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
FT_Byte lcd_filter_weights[5]; FT_LcdFilter lcd_filter;
FT_Bitmap_LcdFilterFunc 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 #endif
} FT_LibraryRec; } FT_LibraryRec;

View File

@ -965,7 +965,7 @@
* to avoid many problems with serif fonts. We compute the * to avoid many problems with serif fonts. We compute the
* corresponding threshold in font units. * corresponding threshold in font units.
*/ */
if ( dim == AF_DIMENSION_VERT ) if ( dim == AF_DIMENSION_HORZ )
segment_length_threshold = FT_DivFix( 64, hints->y_scale ); segment_length_threshold = FT_DivFix( 64, hints->y_scale );
else else
segment_length_threshold = 0; segment_length_threshold = 0;
@ -1536,6 +1536,7 @@
else else
{ {
/* strong hinting process: snap the stem width to integer pixels */ /* 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 ); dist = af_latin_snap_width( axis->widths, axis->width_count, dist );
@ -1571,7 +1572,27 @@
dist = ( dist + 64 ) >> 1; dist = ( dist + 64 ) >> 1;
else if ( dist < 128 ) 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; 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 else
/* round otherwise to prevent color fringes in LCD mode */ /* round otherwise to prevent color fringes in LCD mode */
dist = ( dist + 32 ) & ~63; dist = ( dist + 32 ) & ~63;

View File

@ -24,14 +24,17 @@
#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING #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 static void
_ft_lcd_filter( FT_Bitmap* bitmap, _ft_lcd_filter_fir( FT_Bitmap* bitmap,
FT_Render_Mode mode, FT_Render_Mode mode,
FT_Byte* weights ) FT_Library library )
{ {
FT_UInt width = (FT_UInt)bitmap->width; FT_Byte* weights = library->lcd_weights;
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 */ /* horizontal in-place FIR filter */
@ -155,39 +158,160 @@
} }
FT_EXPORT( FT_Error ) #ifdef USE_LEGACY
FT_Library_SetLcdFilter( FT_Library library, /* FIR filter used by the default and light filters */
const FT_Byte* filter_weights ) 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 ) if ( library == NULL )
return FT_Err_Invalid_Argument; return FT_Err_Invalid_Argument;
if ( filter_weights == FT_LCD_FILTER_NONE ) switch ( filter )
{ {
library->lcd_filter = NULL; case FT_LCD_FILTER_NONE:
return 0; 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 ) library->lcd_filter = filter;
filter_weights = default_filter;
memcpy( library->lcd_filter_weights, filter_weights, 5 );
library->lcd_filter = _ft_lcd_filter;
return 0; return 0;
} }
#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
FT_EXPORT( FT_Error ) FT_EXPORT( FT_Error )
FT_Library_SetLcdFilter( FT_Library library, FT_Library_SetLcdFilter( FT_Library library,
const FT_Byte* filter_weights ) FT_LcdFilter filter )
{ {
FT_UNUSED( library ); FT_UNUSED( library );
FT_UNUSED( filter_weights ); FT_UNUSED( filter );
return FT_Err_Unimplemented_Feature; return FT_Err_Unimplemented_Feature;
} }

View File

@ -171,20 +171,22 @@
#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING #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 ) if ( hmul )
{ {
x_shift -= 64; x_shift -= 64*(extra >> 1);
width += 6; width += 3*extra;
pitch = FT_PAD_CEIL( width, 4 ); pitch = FT_PAD_CEIL( width, 4 );
x_left -= 1; x_left -= (extra >> 1);
} }
if ( vmul ) if ( vmul )
{ {
y_shift -= 64; y_shift -= 64*(extra >> 1);
height += 6; height += 3*extra;
y_top += 1; y_top += (extra >> 1);
} }
} }
@ -213,20 +215,17 @@
/* implode outline if needed */ /* implode outline if needed */
{ {
FT_Int n; FT_Vector* points = outline->points;
FT_Vector* points_end = points + outline->n_points;
FT_Vector* vec; FT_Vector* vec;
if ( hmul ) if ( hmul )
for ( vec = outline->points, n = 0; for ( vec = points; vec < points_end; vec++ )
n < outline->n_points;
n++, vec++ )
vec->x *= 3; vec->x *= 3;
if ( vmul ) if ( vmul )
for ( vec = outline->points, n = 0; for ( vec = points; vec < points_end; vec++ )
n < outline->n_points;
n++, vec++ )
vec->y *= 3; vec->y *= 3;
} }
@ -235,26 +234,22 @@
/* deflate outline if needed */ /* deflate outline if needed */
{ {
FT_Int n; FT_Vector* points = outline->points;
FT_Vector* points_end = points + outline->n_points;
FT_Vector* vec; FT_Vector* vec;
if ( hmul ) if ( hmul )
for ( vec = outline->points, n = 0; for ( vec = points; vec < points_end; vec++ )
n < outline->n_points;
n++, vec++ )
vec->x /= 3; vec->x /= 3;
if ( vmul ) if ( vmul )
for ( vec = outline->points, n = 0; for ( vec = points; vec < points_end; vec++ )
n < outline->n_points;
n++, vec++ )
vec->y /= 3; vec->y /= 3;
} }
if ( slot->library->lcd_filter ) if ( slot->library->lcd_filter_func )
slot->library->lcd_filter( bitmap, mode, slot->library->lcd_filter_func( bitmap, mode, slot->library );
slot->library->lcd_filter_weights );
#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
@ -264,7 +259,7 @@
/* expand it horizontally */ /* expand it horizontally */
if ( hmul ) if ( hmul )
{ {
FT_Byte* line = bitmap->buffer + ( height - height_org ) * pitch; FT_Byte* line = bitmap->buffer;
FT_UInt hh; FT_UInt hh;