freetype2/src/smooth/ftsmooth.c

596 lines
16 KiB
C
Raw Normal View History

/****************************************************************************
*
* ftsmooth.c
*
* Anti-aliasing renderer interface (body).
*
2022-01-11 10:54:10 +01:00
* Copyright (C) 2000-2022 by
* David Turner, Robert Wilhelm, and Werner Lemberg.
*
* This file is part of the FreeType project, and may only be used,
* modified, and distributed under the terms of the FreeType project
* license, LICENSE.TXT. By continuing to use, modify, or distribute
* this file you indicate that you have read the license and
* understand and accept it fully.
*
*/
2000-12-08 17:17:16 +01:00
#include <freetype/internal/ftdebug.h>
#include <freetype/internal/ftobjs.h>
#include <freetype/ftoutln.h>
#include "ftsmooth.h"
#include "ftgrays.h"
Complete redesign of error codes. Please check ftmoderr.h for more details. * include/freetype/internal/cfferrs.h, include/freetype/internal/tterrors.h, include/freetype/internal/t1errors.h: Removed. Replaced with files local to the module. All extra error codes have been moved to `fterrors.h'. * src/sfnt/ttpost.h: Move error codes to `fterrors.h'. * src/autohint/aherrors.h, src/cache/ftcerror.h, src/cff/cfferrs.h, src/cid/ciderrs.h, src/pcf/pcferror.h, src/psaux/psauxerr.h, src/psnames/psnamerr.h, src/raster/rasterrs.h, src/sfnt/sferrors.h, src/smooth/ftsmerrs.h, src/truetype/tterrors.h, src/type1/t1errors.h, src/winfonts/fnterrs.h: New files defining the error names for the module it belongs to. * include/freetype/ftmoderr.h: New file, defining the module error offsets. Its structure is similar to `fterrors.h'. * include/freetype/fterrors.h (FT_NOERRORDEF): New macro. (FT_ERRORDEF): Redefined to use module error offsets. All internal error codes are now public; unused error codes have been removed, some are new. * include/freetype/config/ftheader.h (FT_MODULE_ERRORS_H): New macro. * include/freetype/config/ftoption.h (FT_CONFIG_OPTION_USE_MODULE_ERRORS): New macro. All other source files have been updated to use the new error codes; some already existing (internal) error codes local to a module have been renamed to give them the same name as in the base module. All make files have been updated to include the local error files. * src/cid/cidtokens.h: Replaced with... * src/cid/cidtoken.h: This file for 8+3 consistency. * src/raster/ftraster.c: Use macros for header file names.
2001-06-06 19:30:41 +02:00
#include "ftsmerrs.h"
2000-12-08 17:17:16 +01:00
/* sets render-specific mode */
2001-06-28 01:25:46 +02:00
static FT_Error
ft_smooth_set_mode( FT_Renderer render,
FT_ULong mode_tag,
FT_Pointer data )
2000-06-28 01:18:39 +02:00
{
/* we simply pass it to the raster */
return render->clazz->raster_class->raster_set_mode( render->raster,
mode_tag,
data );
}
2000-06-28 01:18:39 +02:00
/* transform a given glyph image */
2001-06-28 01:25:46 +02:00
static FT_Error
ft_smooth_transform( FT_Renderer render,
FT_GlyphSlot slot,
const FT_Matrix* matrix,
const FT_Vector* delta )
2000-06-28 01:18:39 +02:00
{
FT_Error error = FT_Err_Ok;
if ( slot->format != render->glyph_format )
2000-06-28 01:18:39 +02:00
{
error = FT_THROW( Invalid_Argument );
2000-06-28 01:18:39 +02:00
goto Exit;
}
if ( matrix )
2000-06-28 01:18:39 +02:00
FT_Outline_Transform( &slot->outline, matrix );
if ( delta )
2000-06-28 01:18:39 +02:00
FT_Outline_Translate( &slot->outline, delta->x, delta->y );
2000-06-28 01:18:39 +02:00
Exit:
return error;
}
/* return the glyph's control box */
2001-06-28 01:25:46 +02:00
static void
ft_smooth_get_cbox( FT_Renderer render,
FT_GlyphSlot slot,
FT_BBox* cbox )
2000-06-28 01:18:39 +02:00
{
FT_ZERO( cbox );
2000-06-28 01:18:39 +02:00
if ( slot->format == render->glyph_format )
2000-06-28 01:18:39 +02:00
FT_Outline_Get_CBox( &slot->outline, cbox );
}
typedef struct TOrigin_
{
unsigned char* origin; /* pixmap origin at the bottom-left */
int pitch; /* pitch to go down one row */
} TOrigin;
#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;
}
/* This function writes every third byte in direct rendering mode */
static void
ft_smooth_lcd_spans( int y,
int count,
const FT_Span* spans,
TOrigin* target )
{
unsigned char* dst_line = target->origin - y * target->pitch;
unsigned char* dst;
unsigned short w;
for ( ; count--; spans++ )
for ( dst = dst_line + spans->x * 3, w = spans->len; w--; dst += 3 )
*dst = spans->coverage;
}
static FT_Error
ft_smooth_raster_lcd( FT_Renderer render,
FT_Outline* outline,
FT_Bitmap* bitmap )
{
FT_Error error = FT_Err_Ok;
FT_Vector* sub = render->root.library->lcd_geometry;
FT_Pos x, y;
FT_Raster_Params params;
TOrigin target;
/* Render 3 separate coverage bitmaps, shifting the outline. */
/* Set up direct rendering to record them on each third byte. */
params.source = outline;
params.flags = FT_RASTER_FLAG_AA | FT_RASTER_FLAG_DIRECT;
params.gray_spans = (FT_SpanFunc)ft_smooth_lcd_spans;
params.user = &target;
params.clip_box.xMin = 0;
params.clip_box.yMin = 0;
params.clip_box.xMax = bitmap->width;
params.clip_box.yMax = bitmap->rows;
if ( bitmap->pitch < 0 )
target.origin = bitmap->buffer;
else
target.origin = bitmap->buffer
+ ( bitmap->rows - 1 ) * (unsigned int)bitmap->pitch;
target.pitch = bitmap->pitch;
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;
target.origin++;
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;
if ( error )
goto Exit;
target.origin++;
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;
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 */
2000-06-28 01:18:39 +02:00
/* Oversampling scale to be used in rendering overlaps */
#define SCALE ( 1 << 2 )
/* This function averages inflated spans in direct rendering mode */
static void
ft_smooth_overlap_spans( int y,
int count,
const FT_Span* spans,
TOrigin* target )
{
unsigned char* dst = target->origin - ( y / SCALE ) * target->pitch;
unsigned short x;
unsigned int cover, sum;
/* When accumulating the oversampled spans we need to assure that */
/* fully covered pixels are equal to 255 and do not overflow. */
/* It is important that the SCALE is a power of 2, each subpixel */
/* cover can also reach a power of 2 after rounding, and the total */
/* is clamped to 255 when it adds up to 256. */
for ( ; count--; spans++ )
{
cover = ( spans->coverage + SCALE * SCALE / 2 ) / ( SCALE * SCALE );
for ( x = 0; x < spans->len; x++ )
{
sum = dst[( spans->x + x ) / SCALE] + cover;
dst[( spans->x + x ) / SCALE] = (unsigned char)( sum - ( sum >> 8 ) );
}
}
}
static FT_Error
ft_smooth_raster_overlap( 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;
TOrigin target;
/* Reject outlines that are too wide for 16-bit FT_Span. */
/* Other limits are applied upstream with the same error code. */
if ( bitmap->width * SCALE > 0x7FFF )
return FT_THROW( Raster_Overflow );
/* Set up direct rendering to average oversampled spans. */
params.source = outline;
params.flags = FT_RASTER_FLAG_AA | FT_RASTER_FLAG_DIRECT;
params.gray_spans = (FT_SpanFunc)ft_smooth_overlap_spans;
params.user = &target;
params.clip_box.xMin = 0;
params.clip_box.yMin = 0;
params.clip_box.xMax = bitmap->width * SCALE;
params.clip_box.yMax = bitmap->rows * SCALE;
if ( bitmap->pitch < 0 )
target.origin = bitmap->buffer;
else
target.origin = bitmap->buffer
+ ( bitmap->rows - 1 ) * (unsigned int)bitmap->pitch;
target.pitch = bitmap->pitch;
/* inflate outline */
for ( vec = points; vec < points_end; vec++ )
{
vec->x *= SCALE;
vec->y *= SCALE;
}
/* render outline into the bitmap */
error = render->raster_render( render->raster, &params );
/* deflate outline */
for ( vec = points; vec < points_end; vec++ )
{
vec->x /= SCALE;
vec->y /= SCALE;
}
return error;
}
#undef SCALE
2001-06-28 01:25:46 +02:00
static FT_Error
ft_smooth_render( FT_Renderer render,
FT_GlyphSlot slot,
FT_Render_Mode mode,
const FT_Vector* origin )
2000-06-28 01:18:39 +02:00
{
FT_Error error = FT_Err_Ok;
FT_Outline* outline = &slot->outline;
FT_Bitmap* bitmap = &slot->bitmap;
FT_Memory memory = render->root.memory;
FT_Pos x_shift = 0;
FT_Pos y_shift = 0;
2000-06-28 01:18:39 +02:00
/* check glyph image format */
if ( slot->format != render->glyph_format )
2000-06-28 01:18:39 +02:00
{
error = FT_THROW( Invalid_Argument );
2000-06-28 01:18:39 +02:00
goto Exit;
}
2000-06-28 01:18:39 +02:00
/* check mode */
if ( mode != FT_RENDER_MODE_NORMAL &&
mode != FT_RENDER_MODE_LIGHT &&
mode != FT_RENDER_MODE_LCD &&
mode != FT_RENDER_MODE_LCD_V )
{
error = FT_THROW( Cannot_Render_Glyph );
goto Exit;
}
/* release old bitmap buffer */
if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
{
FT_FREE( bitmap->buffer );
slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
}
if ( ft_glyphslot_preset_bitmap( slot, mode, origin ) )
{
error = FT_THROW( Raster_Overflow );
goto Exit;
}
if ( !bitmap->rows || !bitmap->pitch )
goto Exit;
/* allocate new one */
if ( FT_ALLOC_MULT( bitmap->buffer, bitmap->rows, bitmap->pitch ) )
2000-06-28 01:18:39 +02:00
goto Exit;
slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
x_shift = 64 * -slot->bitmap_left;
y_shift = 64 * -slot->bitmap_top;
if ( bitmap->pixel_mode == FT_PIXEL_MODE_LCD_V )
y_shift += 64 * (FT_Int)bitmap->rows / 3;
else
y_shift += 64 * (FT_Int)bitmap->rows;
if ( origin )
{
x_shift += origin->x;
y_shift += origin->y;
}
/* translate outline to render it into the bitmap */
if ( x_shift || y_shift )
FT_Outline_Translate( outline, x_shift, y_shift );
if ( mode == FT_RENDER_MODE_NORMAL ||
mode == FT_RENDER_MODE_LIGHT )
{
if ( outline->flags & FT_OUTLINE_OVERLAP )
error = ft_smooth_raster_overlap( render, outline, bitmap );
else
{
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
/* finally apply filtering */
{
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 )
{
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;
CHANGES BETWEEN 2.2.1 and 2.2.2 I. IMPORTANT BUG FIXES - Various integer overflows have been fixed. - PFB fonts with MacOS resource fork weren't handled correctly on non-MacOS platforms. - The PCF font loarder has been seriously hardened against malformed font files. II. IMPORTANT CHANGES - the unpatented hinter is now part of the default build of the library, and we added code to automatically support "tricky" fonts that need it. what this means is that FreeType should "just work" with certain Asian fonts, like MingLiu, which cannot properly load without a bytecode interpreter, but fortunately do not use any of the patented bytecode opcodes. Note that the API didn't change, so you can still force unpatented hinting with a special parameter to FT_Open_Face as well. if you're an embedded systems developer, you might want to *disable* the feature to save code space by undefining TT_CONFIG_OPTION_UNPATENTED_HINTING in ftoption.h. - LCD-optimized rendering is now disabled in all default builds of the library, mainly due to patent reasons. For more information see: http://lists.gnu.org/archive/html/freetype/2006-09/msg00064.html a new configuration macro FT_CONFIG_OPTION_SUBPIXEL_RENDERING has been introduced in ftoption.h; manually define it in this file if you want to re-enable the feature. the change only affects the implementation, not the FreeType API. This means that clients don't need to be modified, because the library still generates LCD decimated bitmaps, but with the added constraint that R=G=B on each triplet. - Some computation bugs in the TrueType bytecode interpreter were found, which allow us to get rid of very subtle and rare differences we had with the Windows renderer. III. MISCELLANEOUS - TrueType glyph loading is now about 25% faster. - the anti-aliased rasterizer has been optimized and is now 15% to 25% percent faster than the previous one, depending on content - the Type 1 loader has been improved; as an example, it now skips over top-level dictionaries properly ======================================================================
2006-09-26 18:58:21 +02:00
}
if ( lcd_filter_func )
lcd_filter_func( bitmap, lcd_weights );
}
#endif /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
}
2000-06-28 01:18:39 +02:00
Exit:
if ( !error )
2017-10-01 09:23:42 +02:00
{
/* everything is fine; the glyph is now officially a bitmap */
slot->format = FT_GLYPH_FORMAT_BITMAP;
2017-10-01 09:23:42 +02:00
}
else if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
{
FT_FREE( bitmap->buffer );
slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
}
if ( x_shift || y_shift )
FT_Outline_Translate( outline, -x_shift, -y_shift );
2000-06-28 01:18:39 +02:00
return error;
}
FT_DEFINE_RENDERER(
ft_smooth_renderer_class,
2009-04-05 17:14:04 +02:00
FT_MODULE_RENDERER,
2011-11-30 10:46:53 +01:00
sizeof ( FT_RendererRec ),
2000-06-28 01:18:39 +02:00
"smooth",
0x10000L,
0x20000L,
NULL, /* module specific interface */
(FT_Module_Constructor)ft_smooth_init, /* module_init */
(FT_Module_Destructor) NULL, /* module_done */
(FT_Module_Requester) NULL, /* get_interface */
2000-06-28 01:18:39 +02:00
FT_GLYPH_FORMAT_OUTLINE,
(FT_Renderer_RenderFunc) ft_smooth_render, /* render_glyph */
(FT_Renderer_TransformFunc)ft_smooth_transform, /* transform_glyph */
(FT_Renderer_GetCBoxFunc) ft_smooth_get_cbox, /* get_glyph_cbox */
(FT_Renderer_SetModeFunc) ft_smooth_set_mode, /* set_mode */
(FT_Raster_Funcs*)&ft_grays_raster /* raster_class */
2009-04-05 17:14:04 +02:00
)
/* END */