[smooth] Separate LCD paths from gray rendering.

This makes `ft_smooth_render' a lot smaller and easier to follow. It
also cleanly separates Harmony and ClearType-style LCD rendering
algorithms. Now I only wish to move LCD filtering and geometry from
FT_Library to FT_Renderer.

* src/smooth/ftsmooth.c (ft_smooth_render): Move LCD code from here...
(ft_smooth_raster_lcd, ft_smooth_raster_lcdv): ... to here.
[FT_CONFIG_OPTION_SUBPIXEL_RENDERING]: Reorganize #ifdef's.
This commit is contained in:
Alexei Podtelezhnikov 2020-07-03 09:02:09 -04:00
parent a443474755
commit 2d67511a14
2 changed files with 299 additions and 206 deletions

View File

@ -1,3 +1,16 @@
2020-07-03 Alexei Podtelezhnikov <apodtele@gmail.com>
[smooth] Separate LCD paths from gray rendering.
This makes `ft_smooth_render' a lot smaller and easier to follow. It
also cleanly separates Harmony and ClearType-style LCD rendering
algorithms. Now I only wish to move LCD filtering and geometry from
FT_Library to FT_Renderer.
* src/smooth/ftsmooth.c (ft_smooth_render): Move LCD code from here...
(ft_smooth_raster_lcd, ft_smooth_raster_lcdv): ... to here.
[FT_CONFIG_OPTION_SUBPIXEL_RENDERING]: Reorganize #ifdef's.
2020-06-20 Sebastian Rasmussen <sebras@gmail.com>
[cff] Fix handling of `style_name == NULL' (#58630).

View File

@ -25,36 +25,6 @@
#include "ftsmerrs.h"
/* initialize renderer -- init its raster */
static FT_Error
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;
#else /* set up default LCD filtering */
FT_Library_SetLcdFilter( render->root.library, FT_LCD_FILTER_DEFAULT );
#endif
render->clazz->raster_class->raster_reset( render->raster, NULL, 0 );
return 0;
}
/* sets render-specific mode */
static FT_Error
ft_smooth_set_mode( FT_Renderer render,
@ -106,6 +76,255 @@
FT_Outline_Get_CBox( &slot->outline, cbox );
}
#ifndef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
/* initialize renderer -- init its raster */
static FT_Error
ft_smooth_init( FT_Renderer render )
{
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;
render->clazz->raster_class->raster_reset( render->raster, NULL, 0 );
return 0;
}
static FT_Error
ft_smooth_raster_lcd( FT_Renderer render,
FT_Outline* outline,
FT_Bitmap* bitmap )
{
FT_Error error = FT_Err_Ok;
unsigned int width = bitmap->width / 3;
FT_Vector* sub = render->root.library->lcd_geometry;
FT_Pos x, y;
FT_Raster_Params params;
params.target = bitmap;
params.source = outline;
params.flags = FT_RASTER_FLAG_AA;
/* Render 3 separate coverage bitmaps, shifting the outline. */
FT_Outline_Translate( outline,
-sub[0].x,
-sub[0].y );
error = render->raster_render( render->raster, &params );
x = sub[0].x;
y = sub[0].y;
if ( error )
goto Exit;
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 );
x = sub[1].x;
y = sub[1].y;
bitmap->buffer -= width;
if ( error )
goto Exit;
bitmap->buffer += 2 * width;
FT_Outline_Translate( outline,
sub[1].x - sub[2].x,
sub[1].y - sub[2].y );
error = render->raster_render( render->raster, &params );
x = sub[2].x;
y = sub[2].y;
bitmap->buffer -= 2 * width;
if ( error )
goto Exit;
/* XXX: Rearrange the bytes according to FT_PIXEL_MODE_LCD. */
/* XXX: It is more efficient to render every third byte above. */
{
FT_Memory memory = render->root.memory;
FT_Byte* line;
FT_Byte* temp = NULL;
FT_UInt i, j;
unsigned int height = bitmap->rows;
int pitch = bitmap->pitch;
if ( FT_ALLOC( temp, (FT_ULong)pitch ) )
goto Exit;
for ( i = 0; i < height; i++ )
{
line = bitmap->buffer + i * (FT_ULong)pitch;
for ( j = 0; j < width; j++ )
{
temp[3 * j ] = line[j];
temp[3 * j + 1] = line[j + width];
temp[3 * j + 2] = line[j + width + width];
}
FT_MEM_COPY( line, temp, pitch );
}
FT_FREE( temp );
}
Exit:
FT_Outline_Translate( outline, x, y );
return error;
}
static FT_Error
ft_smooth_raster_lcdv( FT_Renderer render,
FT_Outline* outline,
FT_Bitmap* bitmap )
{
FT_Error error = FT_Err_Ok;
int pitch = bitmap->pitch;
FT_Vector* sub = render->root.library->lcd_geometry;
FT_Pos x, y;
FT_Raster_Params params;
params.target = bitmap;
params.source = outline;
params.flags = FT_RASTER_FLAG_AA;
/* Render 3 separate coverage 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->rows /= 3;
FT_Outline_Translate( outline,
-sub[0].y,
sub[0].x );
error = render->raster_render( render->raster, &params );
x = sub[0].y;
y = -sub[0].x;
if ( error )
goto Exit;
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 );
x = sub[1].y;
y = -sub[1].x;
bitmap->buffer -= pitch;
if ( error )
goto Exit;
bitmap->buffer += 2 * pitch;
FT_Outline_Translate( outline,
sub[1].y - sub[2].y,
sub[2].x - sub[1].x );
error = render->raster_render( render->raster, &params );
x = sub[2].y;
y = -sub[2].x;
bitmap->buffer -= 2 * pitch;
Exit:
FT_Outline_Translate( outline, x, y );
bitmap->pitch /= 3;
bitmap->rows *= 3;
return error;
}
#else /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
/* initialize renderer -- init its raster */
static FT_Error
ft_smooth_init( FT_Renderer render )
{
/* set up default LCD filtering */
FT_Library_SetLcdFilter( render->root.library, FT_LCD_FILTER_DEFAULT );
render->clazz->raster_class->raster_reset( render->raster, NULL, 0 );
return 0;
}
static FT_Error
ft_smooth_raster_lcd( FT_Renderer render,
FT_Outline* outline,
FT_Bitmap* bitmap )
{
FT_Error error = FT_Err_Ok;
FT_Vector* points = outline->points;
FT_Vector* points_end = FT_OFFSET( points, outline->n_points );
FT_Vector* vec;
FT_Raster_Params params;
params.target = bitmap;
params.source = outline;
params.flags = FT_RASTER_FLAG_AA;
/* implode outline */
for ( vec = points; vec < points_end; vec++ )
vec->x *= 3;
/* render outline into the bitmap */
error = render->raster_render( render->raster, &params );
/* deflate outline */
for ( vec = points; vec < points_end; vec++ )
vec->x /= 3;
return error;
}
static FT_Error
ft_smooth_raster_lcdv( FT_Renderer render,
FT_Outline* outline,
FT_Bitmap* bitmap )
{
FT_Error error = FT_Err_Ok;
FT_Vector* points = outline->points;
FT_Vector* points_end = FT_OFFSET( points, outline->n_points );
FT_Vector* vec;
FT_Raster_Params params;
params.target = bitmap;
params.source = outline;
params.flags = FT_RASTER_FLAG_AA;
/* implode outline */
for ( vec = points; vec < points_end; vec++ )
vec->y *= 3;
/* render outline into the bitmap */
error = render->raster_render( render->raster, &params );
/* deflate outline */
for ( vec = points; vec < points_end; vec++ )
vec->y /= 3;
return error;
}
#endif /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
/* convert a slot's glyph image into a bitmap */
static FT_Error
@ -120,10 +339,6 @@
FT_Memory memory = render->root.memory;
FT_Pos x_shift = 0;
FT_Pos y_shift = 0;
FT_Int hmul = ( mode == FT_RENDER_MODE_LCD );
FT_Int vmul = ( mode == FT_RENDER_MODE_LCD_V );
FT_Raster_Params params;
/* check glyph image format */
@ -136,7 +351,8 @@
/* check mode */
if ( mode != FT_RENDER_MODE_NORMAL &&
mode != FT_RENDER_MODE_LIGHT &&
!hmul && !vmul )
mode != FT_RENDER_MODE_LCD &&
mode != FT_RENDER_MODE_LCD_V )
{
error = FT_THROW( Cannot_Render_Glyph );
goto Exit;
@ -181,188 +397,52 @@
if ( x_shift || y_shift )
FT_Outline_Translate( outline, x_shift, y_shift );
/* set up parameters */
params.target = bitmap;
params.source = outline;
params.flags = FT_RASTER_FLAG_AA;
if ( mode == FT_RENDER_MODE_NORMAL ||
mode == FT_RENDER_MODE_LIGHT )
{
FT_Raster_Params params;
params.target = bitmap;
params.source = outline;
params.flags = FT_RASTER_FLAG_AA;
error = render->raster_render( render->raster, &params );
}
else
{
if ( mode == FT_RENDER_MODE_LCD )
error = ft_smooth_raster_lcd ( render, outline, bitmap );
else if ( mode == FT_RENDER_MODE_LCD_V )
error = ft_smooth_raster_lcdv( render, outline, bitmap );
#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
/* implode outline if needed */
{
FT_Vector* points = outline->points;
FT_Vector* points_end = FT_OFFSET( points, outline->n_points );
FT_Vector* vec;
if ( hmul )
for ( vec = points; vec < points_end; vec++ )
vec->x *= 3;
if ( vmul )
for ( vec = points; vec < points_end; vec++ )
vec->y *= 3;
}
/* render outline into the bitmap */
error = render->raster_render( render->raster, &params );
/* deflate outline if needed */
{
FT_Vector* points = outline->points;
FT_Vector* points_end = FT_OFFSET( points, outline->n_points );
FT_Vector* vec;
if ( hmul )
for ( vec = points; vec < points_end; vec++ )
vec->x /= 3;
if ( vmul )
for ( vec = points; vec < points_end; vec++ )
vec->y /= 3;
}
if ( error )
goto Exit;
/* finally apply filtering */
if ( hmul || vmul )
{
FT_Byte* lcd_weights;
FT_Bitmap_LcdFilterFunc lcd_filter_func;
/* Per-face LCD filtering takes priority if set up. */
if ( slot->face && slot->face->internal->lcd_filter_func )
/* finally apply filtering */
{
lcd_weights = slot->face->internal->lcd_weights;
lcd_filter_func = slot->face->internal->lcd_filter_func;
}
else
{
lcd_weights = slot->library->lcd_weights;
lcd_filter_func = slot->library->lcd_filter_func;
}
if ( lcd_filter_func )
lcd_filter_func( bitmap, lcd_weights );
}
#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
if ( hmul ) /* lcd */
{
FT_Byte* line;
FT_Byte* temp = NULL;
FT_UInt i, j;
unsigned int height = bitmap->rows;
unsigned int width = bitmap->width;
int pitch = bitmap->pitch;
FT_Vector* sub = slot->library->lcd_geometry;
FT_Byte* lcd_weights;
FT_Bitmap_LcdFilterFunc lcd_filter_func;
/* Render 3 separate monochrome bitmaps, shifting the outline. */
width /= 3;
FT_Outline_Translate( outline,
-sub[0].x,
-sub[0].y );
error = render->raster_render( render->raster, &params );
if ( error )
goto Exit;
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 );
bitmap->buffer -= width;
if ( error )
goto Exit;
bitmap->buffer += 2 * width;
FT_Outline_Translate( outline,
sub[1].x - sub[2].x,
sub[1].y - sub[2].y );
error = render->raster_render( render->raster, &params );
bitmap->buffer -= 2 * width;
if ( error )
goto Exit;
x_shift -= sub[2].x;
y_shift -= sub[2].y;
/* XXX: Rearrange the bytes according to FT_PIXEL_MODE_LCD. */
/* XXX: It is more efficient to render every third byte above. */
if ( FT_ALLOC( temp, (FT_ULong)pitch ) )
goto Exit;
for ( i = 0; i < height; i++ )
{
line = bitmap->buffer + i * (FT_ULong)pitch;
for ( j = 0; j < width; j++ )
/* Per-face LCD filtering takes priority if set up. */
if ( slot->face && slot->face->internal->lcd_filter_func )
{
temp[3 * j ] = line[j];
temp[3 * j + 1] = line[j + width];
temp[3 * j + 2] = line[j + width + width];
lcd_weights = slot->face->internal->lcd_weights;
lcd_filter_func = slot->face->internal->lcd_filter_func;
}
FT_MEM_COPY( line, temp, pitch );
else
{
lcd_weights = slot->library->lcd_weights;
lcd_filter_func = slot->library->lcd_filter_func;
}
if ( lcd_filter_func )
lcd_filter_func( bitmap, lcd_weights );
}
FT_FREE( temp );
#endif /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
}
else if ( vmul ) /* lcd_v */
{
int pitch = bitmap->pitch;
FT_Vector* sub = slot->library->lcd_geometry;
/* 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->rows /= 3;
FT_Outline_Translate( outline,
-sub[0].y,
sub[0].x );
error = render->raster_render( render->raster, &params );
if ( error )
goto Exit;
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 );
bitmap->buffer -= pitch;
if ( error )
goto Exit;
bitmap->buffer += 2 * pitch;
FT_Outline_Translate( outline,
sub[1].y - sub[2].y,
sub[2].x - sub[1].x );
error = render->raster_render( render->raster, &params );
bitmap->buffer -= 2 * pitch;
if ( error )
goto Exit;
x_shift -= sub[2].y;
y_shift += sub[2].x;
bitmap->pitch /= 3;
bitmap->rows *= 3;
}
else /* grayscale */
error = render->raster_render( render->raster, &params );
#endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
Exit:
if ( !error )