[smooth] Formalize Harmony LCD rendering.

This generalizes magic outline shifts that make Harmony LCD
rendering work in terms of precise two-dimensional RGB subpixel
positions. These coordinates are now set in time of the `smooth'
module initialization and later used to shift a glyph outline for
rendering. FT_RENDER_MODE_LCD and FT_RENDER_MODE_LCD_V use the same
coordinates. The letter, however, rotates them before using.
The LCD bitmap padding is also calculated using these coordinates.

* include/freetype/internal/ftobjs.h (FT_LibraryRec): New array field
`lcd_geometry'.
* src/base/ftlcdfil.c (ft_lcd_padding): Reworked.
* src/base/ftobjs.c (ft_glyphslot_preset_bitmap): Updated accordingly.

* src/smooth/ftsmooth.c [!FT_CONFIG_OPTION_SUBPIXEL_RENDERING]
(ft_smooth_init): Initialize `lcd_geometry'.
(ft_smooth_render_generic): Formalize outline shifts.
This commit is contained in:
Alexei Podtelezhnikov 2018-05-24 22:38:24 -04:00
parent 3fcb4cfa26
commit 7b73cb0707
5 changed files with 119 additions and 52 deletions

View File

@ -1,3 +1,24 @@
2018-05-24 Alexei Podtelezhnikov <apodtele@gmail.com>
[smooth] Formalize Harmony LCD rendering.
This generalizes magic outline shifts that make Harmony LCD
rendering work in terms of precise two-dimensional RGB subpixel
positions. These coordinates are now set in time of the `smooth'
module initialization and later used to shift a glyph outline for
rendering. FT_RENDER_MODE_LCD and FT_RENDER_MODE_LCD_V use the same
coordinates. The letter, however, rotates them before using.
The LCD bitmap padding is also calculated using these coordinates.
* include/freetype/internal/ftobjs.h (FT_LibraryRec): New array field
`lcd_geometry'.
* src/base/ftlcdfil.c (ft_lcd_padding): Reworked.
* src/base/ftobjs.c (ft_glyphslot_preset_bitmap): Updated accordingly.
* src/smooth/ftsmooth.c [!FT_CONFIG_OPTION_SUBPIXEL_RENDERING]
(ft_smooth_init): Initialize `lcd_geometry'.
(ft_smooth_render_generic): Formalize outline shifts.
2018-05-22 Werner Lemberg <wl@gnu.org> 2018-05-22 Werner Lemberg <wl@gnu.org>
[truetype] Reject elements of composites with invalid glyph indices. [truetype] Reject elements of composites with invalid glyph indices.

View File

@ -270,11 +270,11 @@ FT_BEGIN_HEADER
FT_CMap_Done( FT_CMap cmap ); FT_CMap_Done( FT_CMap cmap );
/* adds LCD padding to Min and Max boundaries */ /* add LCD padding to CBox */
FT_BASE( void ) FT_BASE( void )
ft_lcd_padding( FT_Pos* Min, ft_lcd_padding( FT_BBox* cbox,
FT_Pos* Max, FT_GlyphSlot slot,
FT_GlyphSlot slot ); FT_Render_Mode mode );
#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
@ -350,8 +350,8 @@ FT_BEGIN_HEADER
/* */ /* */
/* lcd_weights :: */ /* lcd_weights :: */
/* lcd_filter_func :: */ /* lcd_filter_func :: */
/* If subpixel rendering is activated, the LCD filtering weights */ /* These fields specify the LCD filtering weights and callback */
/* and callback function. */ /* function for ClearType-style subpixel rendering. */
/* */ /* */
/* refcount :: */ /* refcount :: */
/* A counter initialized to~1 at the time an @FT_Face structure is */ /* A counter initialized to~1 at the time an @FT_Face structure is */
@ -868,11 +868,15 @@ FT_BEGIN_HEADER
/* interpreter. Currently, only the TrueType */ /* interpreter. Currently, only the TrueType */
/* bytecode debugger uses this. */ /* bytecode debugger uses this. */
/* */ /* */
/* lcd_weights :: If subpixel rendering is activated, the LCD */ /* lcd_weights :: The LCD filter weights for ClearType-style */
/* filter weights, if any. */ /* subpixel rendering. */
/* */ /* */
/* lcd_filter_func :: If subpixel rendering is activated, the LCD */ /* lcd_filter_func :: The LCD filtering callback function for */
/* filtering callback function. */ /* for ClearType-style subpixel rendering. */
/* */
/* lcd_geometry :: This array specifies LCD subpixel geometry */
/* and controls Harmony LCD rendering technique, */
/* alternative to ClearType. */
/* */ /* */
/* pic_container :: Contains global structs and tables, instead */ /* pic_container :: Contains global structs and tables, instead */
/* of defining them globally. */ /* of defining them globally. */
@ -904,6 +908,8 @@ FT_BEGIN_HEADER
#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
FT_LcdFiveTapFilter lcd_weights; /* filter weights, if any */ FT_LcdFiveTapFilter lcd_weights; /* filter weights, if any */
FT_Bitmap_LcdFilterFunc lcd_filter_func; /* filtering callback */ FT_Bitmap_LcdFilterFunc lcd_filter_func; /* filtering callback */
#else
FT_Vector lcd_geometry[3]; /* RGB subpixel positions */
#endif #endif
FT_Int refcount; FT_Int refcount;

View File

@ -34,9 +34,9 @@
/* add padding according to filter weights */ /* add padding according to filter weights */
FT_BASE_DEF (void) FT_BASE_DEF (void)
ft_lcd_padding( FT_Pos* Min, ft_lcd_padding( FT_BBox* cbox,
FT_Pos* Max, FT_GlyphSlot slot,
FT_GlyphSlot slot ) FT_Render_Mode mode )
{ {
FT_Byte* lcd_weights; FT_Byte* lcd_weights;
FT_Bitmap_LcdFilterFunc lcd_filter_func; FT_Bitmap_LcdFilterFunc lcd_filter_func;
@ -56,10 +56,20 @@
if ( lcd_filter_func == ft_lcd_filter_fir ) if ( lcd_filter_func == ft_lcd_filter_fir )
{ {
*Min -= lcd_weights[0] ? 43 : if ( mode == FT_RENDER_MODE_LCD )
lcd_weights[1] ? 22 : 0; {
*Max += lcd_weights[4] ? 43 : cbox->xMin -= lcd_weights[0] ? 43 :
lcd_weights[3] ? 22 : 0; lcd_weights[1] ? 22 : 0;
cbox->xMax += lcd_weights[4] ? 43 :
lcd_weights[3] ? 22 : 0;
}
else if ( mode == FT_RENDER_MODE_LCD_V )
{
cbox->yMin -= lcd_weights[0] ? 43 :
lcd_weights[1] ? 22 : 0;
cbox->yMax += lcd_weights[4] ? 43 :
lcd_weights[3] ? 22 : 0;
}
} }
} }
@ -343,16 +353,28 @@
#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
/* add padding according to accommodate outline shifts */ /* add padding to accommodate outline shifts */
FT_BASE_DEF (void) FT_BASE_DEF (void)
ft_lcd_padding( FT_Pos* Min, ft_lcd_padding( FT_BBox* cbox,
FT_Pos* Max, FT_GlyphSlot slot,
FT_GlyphSlot slot ) FT_Render_Mode mode )
{ {
FT_UNUSED( slot ); FT_Vector* sub = slot->library->lcd_geometry;
*Min -= 21; if ( mode == FT_RENDER_MODE_LCD )
*Max += 21; {
cbox->xMin -= FT_MAX( FT_MAX( sub[0].x, sub[1].x ), sub[2].x );
cbox->xMax -= FT_MIN( FT_MIN( sub[0].x, sub[1].x ), sub[2].x );
cbox->yMin -= FT_MAX( FT_MAX( sub[0].y, sub[1].y ), sub[2].y );
cbox->yMax -= FT_MIN( FT_MIN( sub[0].y, sub[1].y ), sub[2].y );
}
else if ( mode == FT_RENDER_MODE_LCD_V )
{
cbox->xMin -= FT_MAX( FT_MAX( sub[0].y, sub[1].y ), sub[2].y );
cbox->xMax -= FT_MIN( FT_MIN( sub[0].y, sub[1].y ), sub[2].y );
cbox->yMin += FT_MIN( FT_MIN( sub[0].x, sub[1].x ), sub[2].x );
cbox->yMax += FT_MAX( FT_MAX( sub[0].x, sub[1].x ), sub[2].x );
}
} }

View File

@ -397,12 +397,12 @@
case FT_RENDER_MODE_LCD: case FT_RENDER_MODE_LCD:
pixel_mode = FT_PIXEL_MODE_LCD; pixel_mode = FT_PIXEL_MODE_LCD;
ft_lcd_padding( &cbox.xMin, &cbox.xMax, slot ); ft_lcd_padding( &cbox, slot, mode );
goto Round; goto Round;
case FT_RENDER_MODE_LCD_V: case FT_RENDER_MODE_LCD_V:
pixel_mode = FT_PIXEL_MODE_LCD_V; pixel_mode = FT_PIXEL_MODE_LCD_V;
ft_lcd_padding( &cbox.yMin, &cbox.yMax, slot ); ft_lcd_padding( &cbox, slot, mode );
goto Round; goto Round;
case FT_RENDER_MODE_NORMAL: case FT_RENDER_MODE_NORMAL:

View File

@ -30,6 +30,22 @@
static FT_Error static FT_Error
ft_smooth_init( FT_Renderer render ) ft_smooth_init( FT_Renderer render )
{ {
#ifndef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
FT_Vector* sub = render->root.library->lcd_geometry;
/* set up default subpixel geometry for striped RGB panels. */
sub[0].x = -21;
sub[0].y = 0;
sub[1].x = 0;
sub[1].y = 0;
sub[2].x = 21;
sub[2].y = 0;
#endif
render->clazz->raster_class->raster_reset( render->raster, NULL, 0 ); render->clazz->raster_class->raster_reset( render->raster, NULL, 0 );
return 0; return 0;
@ -234,33 +250,33 @@
unsigned int width = bitmap->width; unsigned int width = bitmap->width;
int pitch = bitmap->pitch; int pitch = bitmap->pitch;
FT_Vector* sub = slot->library->lcd_geometry;
/* Render 3 separate monochrome bitmaps, shifting the outline */
/* by 1/3 pixel. */ /* Render 3 separate monochrome bitmaps, shifting the outline. */
width /= 3; width /= 3;
bitmap->buffer += width; FT_Outline_Translate( outline, -sub[0].x, -sub[0].y );
error = render->raster_render( render->raster, &params ); error = render->raster_render( render->raster, &params );
if ( error ) if ( error )
goto Exit; goto Exit;
FT_Outline_Translate( outline, -21, 0 );
x_shift -= 21;
bitmap->buffer += width; bitmap->buffer += width;
FT_Outline_Translate( outline, sub[0].x - sub[1].x, sub[0].y - sub[1].y );
error = render->raster_render( render->raster, &params ); error = render->raster_render( render->raster, &params );
if ( error ) if ( error )
goto Exit; goto Exit;
FT_Outline_Translate( outline, 42, 0 ); bitmap->buffer += width;
x_shift += 42; FT_Outline_Translate( outline, sub[1].x - sub[2].x, sub[1].y - sub[2].y );
error = render->raster_render( render->raster, &params );
if ( error )
goto Exit;
x_shift -= sub[2].x;
y_shift -= sub[2].y;
bitmap->buffer -= 2 * width; bitmap->buffer -= 2 * width;
error = render->raster_render( render->raster, &params );
if ( error )
goto Exit;
/* XXX: Rearrange the bytes according to FT_PIXEL_MODE_LCD. */ /* XXX: Rearrange the bytes according to FT_PIXEL_MODE_LCD. */
/* XXX: It is more efficient to render every third byte above. */ /* XXX: It is more efficient to render every third byte above. */
@ -285,34 +301,36 @@
{ {
int pitch = bitmap->pitch; int pitch = bitmap->pitch;
FT_Vector* sub = slot->library->lcd_geometry;
/* Render 3 separate monochrome bitmaps, shifting the outline */
/* by 1/3 pixel. Triple the pitch to render on each third row. */ /* Render 3 separate monochrome bitmaps, shifting the outline. */
/* Notice that the subpixel geometry vectors are rotated. */
/* Triple the pitch to render on each third row. */
bitmap->pitch *= 3; bitmap->pitch *= 3;
bitmap->rows /= 3; bitmap->rows /= 3;
bitmap->buffer += pitch; FT_Outline_Translate( outline, -sub[0].y, sub[0].x );
error = render->raster_render( render->raster, &params ); error = render->raster_render( render->raster, &params );
if ( error ) if ( error )
goto Exit; goto Exit;
FT_Outline_Translate( outline, 0, 21 );
y_shift += 21;
bitmap->buffer += pitch; bitmap->buffer += pitch;
FT_Outline_Translate( outline, sub[0].y - sub[1].y, sub[1].x - sub[0].x );
error = render->raster_render( render->raster, &params ); error = render->raster_render( render->raster, &params );
if ( error ) if ( error )
goto Exit; goto Exit;
FT_Outline_Translate( outline, 0, -42 ); bitmap->buffer += pitch;
y_shift -= 42; FT_Outline_Translate( outline, sub[1].y - sub[2].y, sub[2].x - sub[1].x );
error = render->raster_render( render->raster, &params );
if ( error )
goto Exit;
x_shift -= sub[2].y;
y_shift += sub[2].x;
bitmap->buffer -= 2 * pitch; bitmap->buffer -= 2 * pitch;
error = render->raster_render( render->raster, &params );
if ( error )
goto Exit;
bitmap->pitch /= 3; bitmap->pitch /= 3;
bitmap->rows *= 3; bitmap->rows *= 3;
} }