commit 2b99e72ca36cf25e8df0d9341e1cd094987b461f Author: Anuj Verma Date: Fri May 21 19:10:27 2021 +0530 [sdf] Use 8 bits for final SDF output instead of 16bits. Since 8bits is enough to represent SDF data we no longer require 16bits for this purpose. Also, we now normalize the output data to use the entire 8bit range efficiently. For example: if we use 3.5 format with a spread of 1 we basically only use the first 5 bits. By normalizing we can use the entire 8bit range. * include/freetype/freetype.h (FT_Render_Mode): Update the description for `FT_RENDER_MODE_SDF` regarding this change. * include/freetype/ftimage.h (FT_Pixel_Mode): Removed `FT_PIXEL_MODE_GRAY16` since no longer required. * include/freetype/fttypes.h (FT_F6Dot10): Removed since no longer required. * src/sdf/ftsdfrend.c (ft_sdf_render, ft_bsdf_render): Allocate 8bit bitmap instead of 16bit buffer. * src/sdf/ftsdfcommon.h (map_fixed_to_sdf): Added function to convert 16.16 distance value to our desired format. * src/sdf/ftsdf.c (sdf_generate_with_overlaps, sdf_generate_bounding_box): Modified to use the new `map_fixed_to_sdf` function and also use 8bit output buffer. * src/sdf/ftbsdf.c (finalize_sdf): Output to a 8bit buffer instead of 16bit buffer. diff --git a/include/freetype/freetype.h b/include/freetype/freetype.h index 03438b531..82e655cf8 100644 --- a/include/freetype/freetype.h +++ b/include/freetype/freetype.h @@ -3302,13 +3302,14 @@ FT_BEGIN_HEADER * pixels and use the @FT_PIXEL_MODE_LCD_V mode. * * FT_RENDER_MODE_SDF :: - * This mode corresponds to 16-bit signed distance fields (SDF) + * This mode corresponds to 8-bit signed distance fields (SDF) * bitmaps. Each pixel in a SDF bitmap contains information about the * nearest edge of the glyph outline. The distances are calculated * from the center of the pixel and are positive if they are filled by * the outline (i.e., inside the outline) and negative otherwise. The - * output bitmap buffer is represented as 6.10 fixed-point values; use - * @FT_F6Dot10 and convert accordingly. + * output bitmap buffer contains a normalized distance values (range + * [-1.0, 1.0] in float). To get the actual distances in pixels simply + * multiply the values with the `spread` used. * * @note: * The selected render mode only affects vector glyphs of a font. diff --git a/include/freetype/ftimage.h b/include/freetype/ftimage.h index e3cc68f6c..66a8b89aa 100644 --- a/include/freetype/ftimage.h +++ b/include/freetype/ftimage.h @@ -157,13 +157,6 @@ FT_BEGIN_HEADER * in font files according to the OpenType specification. We haven't * found a single font using this format, however. * - * FT_PIXEL_MODE_GRAY16 :: - * A 16-bit per pixel bitmap used to represent signed distances in a - * signed distance field bitmap as needed by @FT_RENDER_MODE_SDF. - * Values are represented in a 6.10 fixed-point format; this means - * that you have to divide by 1024 to get the actual data generated by - * the SDF rasterizers. - * * FT_PIXEL_MODE_LCD :: * An 8-bit bitmap, representing RGB or BGR decimated glyph images used * for display on LCD displays; the bitmap is three times wider than @@ -194,7 +187,6 @@ FT_BEGIN_HEADER FT_PIXEL_MODE_LCD, FT_PIXEL_MODE_LCD_V, FT_PIXEL_MODE_BGRA, - FT_PIXEL_MODE_GRAY16, FT_PIXEL_MODE_MAX /* do not remove */ diff --git a/include/freetype/fttypes.h b/include/freetype/fttypes.h index 3e4a474bd..d5ca1c4f4 100644 --- a/include/freetype/fttypes.h +++ b/include/freetype/fttypes.h @@ -78,7 +78,6 @@ FT_BEGIN_HEADER * FT_FWord * FT_UFWord * FT_F2Dot14 - * FT_F6Dot10 * FT_UnitVector * FT_F26Dot6 * FT_Data @@ -265,17 +264,6 @@ FT_BEGIN_HEADER typedef signed short FT_F2Dot14; - /************************************************************************** - * - * @type: - * FT_F6Dot10 - * - * @description: - * A signed 6.10 fixed-point type used for signed distance values. - */ - typedef signed short FT_F6Dot10; - - /************************************************************************** * * @type: diff --git a/src/sdf/ftbsdf.c b/src/sdf/ftbsdf.c index af1e40382..6d1deb604 100644 --- a/src/sdf/ftbsdf.c +++ b/src/sdf/ftbsdf.c @@ -1089,12 +1089,12 @@ finalize_sdf( BSDF_Worker* worker, const FT_Bitmap* target ) { - FT_Error error = FT_Err_Ok; + FT_Error error = FT_Err_Ok; - FT_Int w, r; - FT_Int i, j; - FT_6D10* t_buffer; - FT_16D16 spread; + FT_Int w, r; + FT_Int i, j; + FT_SDFFormat* t_buffer; + FT_16D16 spread; if ( !worker || !target ) @@ -1105,7 +1105,7 @@ w = target->width; r = target->rows; - t_buffer = (FT_6D10*)target->buffer; + t_buffer = (FT_SDFFormat*)target->buffer; if ( w != worker->width || r != worker->rows ) @@ -1125,10 +1125,10 @@ { for ( i = 0; i < w; i++ ) { - FT_Int index; - FT_16D16 dist; - FT_6D10 final_dist; - FT_Char sign; + FT_Int index; + FT_16D16 dist; + FT_SDFFormat final_dist; + FT_Char sign; index = j * w + i; @@ -1141,9 +1141,8 @@ dist = square_root( dist ); #endif - /* convert from 16.16 to 6.10 */ - dist /= 64; - final_dist = (FT_6D10)(dist & 0x0000FFFF); + /* concatenate from 16.16 to appropriate format */ + final_dist = map_fixed_to_sdf( dist, spread ); /* We assume that if the pixel is inside a contour */ /* its coverage value must be > 127. */ diff --git a/src/sdf/ftsdf.c b/src/sdf/ftsdf.c index a58d1448f..36efd67f6 100644 --- a/src/sdf/ftsdf.c +++ b/src/sdf/ftsdf.c @@ -2978,6 +2978,10 @@ } + /* Might fix this later if this generate faster than the subdivision */ + /* method. But currently I don't see any use for this. */ + #error "do not use `sdf_generate` as it outputs 16bit instead of 8bit data" + /************************************************************************** * * @Function: @@ -3190,10 +3194,10 @@ FT_Memory memory = NULL; FT_Int width, rows, i, j; - FT_Int sp_sq; /* max value to check */ + FT_Int sp_sq; /* max value to check */ - SDF_Contour* contours; /* list of all contours */ - FT_Short* buffer; /* the bitmap buffer */ + SDF_Contour* contours; /* list of all contours */ + FT_SDFFormat* buffer; /* the bitmap buffer */ /* This buffer has the same size in indices as the */ /* bitmap buffer. When we check a pixel position for */ @@ -3202,6 +3206,8 @@ /* and also determine the signs properly. */ SDF_Signed_Distance* dists = NULL; + const FT_16D16 fixed_spread = FT_INT_16D16( spread ); + if ( !shape || !bitmap ) { @@ -3225,15 +3231,15 @@ contours = shape->contours; width = (FT_Int)bitmap->width; rows = (FT_Int)bitmap->rows; - buffer = (FT_Short*)bitmap->buffer; + buffer = (FT_SDFFormat*)bitmap->buffer; if ( FT_ALLOC( dists, width * rows * sizeof ( *dists ) ) ) goto Exit; if ( USE_SQUARED_DISTANCES ) - sp_sq = FT_INT_16D16( spread * spread ); + sp_sq = fixed_spread * fixed_spread; else - sp_sq = FT_INT_16D16( spread ); + sp_sq = fixed_spread; if ( width == 0 || rows == 0 ) { @@ -3346,21 +3352,20 @@ /* if the pixel is not set */ /* its shortest distance is more than `spread` */ if ( dists[index].sign == 0 ) - dists[index].distance = FT_INT_16D16( spread ); + dists[index].distance = fixed_spread; else current_sign = dists[index].sign; /* clamp the values */ - if ( dists[index].distance > (FT_Int)FT_INT_16D16( spread ) ) - dists[index].distance = FT_INT_16D16( spread ); - - /* convert from 16.16 to 6.10 */ - dists[index].distance /= 64; + if ( dists[index].distance > (FT_Int)fixed_spread ) + dists[index].distance = fixed_spread; if ( internal_params.flip_sign ) - buffer[index] = (FT_Short)dists[index].distance * -current_sign; + dists[index].distance *= -current_sign; else - buffer[index] = (FT_Short)dists[index].distance * current_sign; + dists[index].distance *= current_sign; + + buffer[index] = map_fixed_to_sdf( dists[index].distance, fixed_spread ); } } @@ -3497,9 +3502,9 @@ SDF_Contour* head; /* head of the contour list */ SDF_Shape temp_shape; /* temporary shape */ - FT_Memory memory; /* to allocate memory */ - FT_6D10* t; /* target bitmap buffer */ - FT_Bool flip_sign; /* filp sign? */ + FT_Memory memory; /* to allocate memory */ + FT_SDFFormat* t; /* target bitmap buffer */ + FT_Bool flip_sign; /* filp sign? */ /* orientation of all the separate contours */ SDF_Contour_Orientation* orientations; @@ -3617,7 +3622,7 @@ shape->contours = head; /* cast the output bitmap buffer */ - t = (FT_6D10*)bitmap->buffer; + t = (FT_SDFFormat*)bitmap->buffer; /* Iterate over all pixels and combine all separate */ /* contours. These are the rules for combining: */ @@ -3635,15 +3640,15 @@ FT_Int id = j * width + i; /* index of current pixel */ FT_Int c; /* contour iterator */ - FT_6D10 val_c = SHRT_MIN; /* max clockwise value */ - FT_6D10 val_ac = SHRT_MAX; /* min counter-clockwise val */ + FT_SDFFormat val_c = SCHAR_MIN; /* max clockwise value */ + FT_SDFFormat val_ac = SCHAR_MAX; /* min counter-clockwise val */ /* iterate through all the contours */ for ( c = 0; c < num_contours; c++ ) { /* current contour value */ - FT_6D10 temp = ((FT_6D10*)bitmaps[c].buffer)[id]; + FT_SDFFormat temp = ( (FT_SDFFormat*)bitmaps[c].buffer )[id]; if ( orientations[c] == SDF_ORIENTATION_CW ) diff --git a/src/sdf/ftsdfcommon.h b/src/sdf/ftsdfcommon.h index 7c5581762..d81202245 100644 --- a/src/sdf/ftsdfcommon.h +++ b/src/sdf/ftsdfcommon.h @@ -43,7 +43,7 @@ FT_BEGIN_HEADER */ /* default spread value */ -#define DEFAULT_SPREAD 8 +#define DEFAULT_SPREAD 2 /* minimum spread supported by the renderer */ #define MIN_SPREAD 2 /* maximum spread supported by the renderer */ @@ -117,6 +117,8 @@ FT_BEGIN_HEADER typedef FT_Fixed FT_26D6; /* 26.6 fixed-point representation */ typedef FT_Short FT_6D10; /* 6.10 fixed-point representation */ + typedef FT_Char FT_SDFFormat; /* format to represent SDF data */ + typedef FT_BBox FT_CBox; /* control box of a curve */ @@ -163,6 +165,46 @@ FT_BEGIN_HEADER } + /* + * Convert 16.16 fixed point value to the desired output format. + * In this case we reduce 16.16 fixed point value to normalized + * 8 bit values. + * The `max_value` in the parameter is the maximum value in the + * distance field map and is equal to the spread. We normalize + * the distances using this value instead of computing the maximum + * value for the entire bitmap. + */ + static FT_SDFFormat + map_fixed_to_sdf( FT_16D16 dist, FT_16D16 max_value ) + { + FT_SDFFormat out; + FT_16D16 udist; + + + /* normalize the distance values */ + dist = FT_DivFix( dist, max_value ); + + udist = dist < 0 ? -dist : dist; + + /* Reduce the distance values to 8 bits, +1/-1 in */ + /* 16.16 takes the 16th bit. So we right shift the */ + /* number by 9 to make it fit in the 7 bit range. */ + /* 1 bit is reserved for the sign. */ + udist >>= 9; + + /* Since char can only store max positive value */ + /* of 127 we need to make sure it does not wrap */ + /* around and give a negative value. */ + if ( udist == 128 && dist > 0 ) + udist = 127; + + /* output the data */ + out = dist < 0 ? (FT_SDFFormat)udist * -1 + : (FT_SDFFormat)udist; + + return out; + } + FT_END_HEADER #endif /* FTSDFCOMMON_H_ */ diff --git a/src/sdf/ftsdfrend.c b/src/sdf/ftsdfrend.c index 3226bdc9d..d96db8190 100644 --- a/src/sdf/ftsdfrend.c +++ b/src/sdf/ftsdfrend.c @@ -313,9 +313,9 @@ bitmap->width += x_pad * 2; /* ignore the pitch, pixel mode and set custom */ - bitmap->pixel_mode = FT_PIXEL_MODE_GRAY16; - bitmap->pitch = bitmap->width * 2; - bitmap->num_grays = 65535; + bitmap->pixel_mode = FT_PIXEL_MODE_GRAY; + bitmap->pitch = bitmap->width; + bitmap->num_grays = 255; /* allocate new buffer */ if ( FT_ALLOC_MULT( bitmap->buffer, bitmap->rows, bitmap->pitch ) ) @@ -524,9 +524,9 @@ target.width = bitmap->width + x_pad * 2; /* set up the target bitmap */ - target.pixel_mode = FT_PIXEL_MODE_GRAY16; - target.pitch = target.width * 2; - target.num_grays = 65535; + target.pixel_mode = FT_PIXEL_MODE_GRAY; + target.pitch = target.width; + target.num_grays = 255; if ( FT_ALLOC_MULT( target.buffer, target.rows, target.pitch ) ) goto Exit;