[sdf] Use 8 bits for final SDF output instead of 16bits.

Since 8-bits is enough to represent SDF data we no longer require
16-bits for  this purpose. Also, we now normalize the output data
to use the entire 8-bit range efficiently. For example: if we use
3.5 format with a spread of 1 we basically only use the starting
5-bits. By normalizing we can use the entire 8-bit range.

* include/freetype/freetype.h (FT_Render_Mode): Updated 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 8-bit
bitmap instead of 16-bit 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): Use the new `map_fixed_to_sdf` function
and also use 8-bit output buffer.

* src/sdf/ftbsdf.c (finalize_sdf): Output to a 8-bit buffer instead
of 16-bit buffer.
This commit is contained in:
Anuj Verma 2021-06-08 08:29:34 +05:30
parent 2a6665a4c0
commit 2b1d556269
8 changed files with 190 additions and 69 deletions

View File

@ -1,3 +1,35 @@
2021-06-08 Anuj Verma <anujv@iitbhilai.ac.in>
[sdf] Use 8 bits for final SDF output instead of 16bits.
Since 8-bits is enough to represent SDF data we no longer require
16-bits for this purpose. Also, we now normalize the output data
to use the entire 8-bit range efficiently. For example: if we use
3.5 format with a spread of 1 we basically only use the starting
5-bits. By normalizing we can use the entire 8-bit range.
* include/freetype/freetype.h (FT_Render_Mode): Updated 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 8-bit
bitmap instead of 16-bit 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): Use the new `map_fixed_to_sdf` function
and also use 8-bit output buffer.
* src/sdf/ftbsdf.c (finalize_sdf): Output to a 8-bit buffer instead
of 16-bit buffer.
2021-06-02 Ben Wagner <bungeman@chromium.org>
Werner Lemberg <wl@gnu.org>

View File

@ -3302,19 +3302,46 @@ 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.
* the outline (i.e., inside the outline) and negative otherwise. Check
* the note below on how to convert the output values to usable data.
*
* @note:
* The selected render mode only affects vector glyphs of a font.
* Embedded bitmaps often have a different pixel mode like
* @FT_PIXEL_MODE_MONO. You can use @FT_Bitmap_Convert to transform them
* into 8-bit pixmaps.
*
* For @FT_RENDER_MODE_SDF output bitmap buffer contains normalized
* distance values that are packed into unsigned 8-bit buffer. To get
* pixel values in floating point representation use the following
* conversion:
*
* ```
* <load glyph and render using @FT_RENDER_MODE_SDF, then use the
* output buffer as follows>
*
* ...
* FT_Byte buffer = glyph->bitmap->buffer;
*
* for pixel in buffer
* {
* <`sd` is the signed distance and spread is the current `spread`,
* the default spread is 2 and can be changed>
*
* float sd = (float)pixel - 128.0f;
*
* <convert the to pixel values>
*
* sd = ( sd / 128.0f ) * spread;
*
* <store `sd` in a buffer or use as required>
* }
*
* ```
*/
typedef enum FT_Render_Mode_
{

View File

@ -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 */

View File

@ -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:

View File

@ -1092,12 +1092,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 )
@ -1108,7 +1108,7 @@
w = (int)target->width;
r = (int)target->rows;
t_buffer = (FT_6D10*)target->buffer;
t_buffer = (FT_SDFFormat*)target->buffer;
if ( w != worker->width ||
r != worker->rows )
@ -1128,10 +1128,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;
@ -1144,10 +1144,6 @@
dist = square_root( dist );
#endif
/* convert from 16.16 to 6.10 */
dist /= 64;
final_dist = (FT_6D10)(dist & 0x0000FFFF);
/* We assume that if the pixel is inside a contour */
/* its coverage value must be > 127. */
sign = worker->distance_map[index].alpha < 127 ? -1 : 1;
@ -1156,7 +1152,10 @@
if ( worker->params.flip_sign )
sign = -sign;
t_buffer[index] = final_dist * sign;
/* concatenate from 16.16 to appropriate format */
final_dist = map_fixed_to_sdf( dist * sign, spread );
t_buffer[index] = final_dist;
}
}

View File

@ -2897,6 +2897,10 @@
/* `sdf_generate' is not used at the moment */
#if 0
#error "DO NOT USE THIS!"
#error "The function still output 16-bit data which might cause memory"
#error "corruption. If required I will add this later."
/**************************************************************************
*
* @Function:
@ -3193,7 +3197,7 @@
FT_Int sp_sq; /* max value to check */
SDF_Contour* contours; /* list of all contours */
FT_Short* buffer; /* the bitmap buffer */
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 )
{
@ -3229,12 +3235,12 @@
contours = shape->contours;
width = (FT_Int)bitmap->width;
rows = (FT_Int)bitmap->rows;
buffer = (FT_Short*)bitmap->buffer;
buffer = (FT_SDFFormat*)bitmap->buffer;
if ( USE_SQUARED_DISTANCES )
sp_sq = (FT_Int)FT_INT_16D16( spread * spread );
sp_sq = fixed_spread * fixed_spread;
else
sp_sq = (FT_Int)FT_INT_16D16( spread );
sp_sq = fixed_spread;
if ( width == 0 || rows == 0 )
{
@ -3347,21 +3353,23 @@
/* 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 );
if ( dists[index].distance > fixed_spread )
dists[index].distance = fixed_spread;
/* convert from 16.16 to 6.10 */
dists[index].distance /= 64;
/* flip sign if required */
dists[index].distance *= internal_params.flip_sign ?
-current_sign :
current_sign;
if ( internal_params.flip_sign )
buffer[index] = (FT_Short)dists[index].distance * -current_sign;
else
buffer[index] = (FT_Short)dists[index].distance * current_sign;
/* concatenate to appropriate format */
buffer[index] = map_fixed_to_sdf(
dists[index].distance,
fixed_spread );
}
}
@ -3498,9 +3506,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;
@ -3621,7 +3629,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: */
@ -3636,18 +3644,18 @@
{
for ( i = 0; i < width; i++ )
{
FT_Int id = j * width + i; /* index of current pixel */
FT_Int c; /* contour iterator */
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 = 0; /* max clockwise value */
FT_SDFFormat val_ac = UCHAR_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 )
@ -3658,7 +3666,10 @@
/* Finally find the smaller of the two and assign to output. */
/* Also apply `flip_sign` if set. */
t[id] = FT_MIN( val_c, val_ac ) * ( flip_sign ? -1 : 1 );
t[id] = FT_MIN( val_c, val_ac );
if ( flip_sign )
t[id] = invert_sign( t[id] );
}
}
@ -3681,6 +3692,9 @@
}
}
/* restore the `flip_sign` property */
internal_params.flip_sign = flip_sign;
return error;
}

View File

@ -115,7 +115,7 @@ FT_BEGIN_HEADER
typedef FT_Fixed FT_16D16; /* 16.16 fixed-point representation */
typedef FT_Fixed FT_26D6; /* 26.6 fixed-point representation */
typedef FT_Short FT_6D10; /* 6.10 fixed-point representation */
typedef FT_Byte FT_SDFFormat; /* format to represent SDF data */
typedef FT_BBox FT_CBox; /* control box of a curve */
@ -162,6 +162,75 @@ FT_BEGIN_HEADER
return (FT_16D16)q;
}
/**************************************************************************
*
* format and sign manipulating functions
*
*/
/*
* 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.
* You can use this function to map the 16.16 signed values to any
* format required. Do note that the output buffer is 8-bit, so only
* use 8-bit format for `FT_SDFFormat`, or increase buffer size in
* `ftsdfrend.c`.
*/
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 ( dist > 0 && udist > 127 )
udist = 127;
if ( dist < 0 && udist > 128 )
udist = 128;
/* Output the data; negative values are from [0, 127] and positive */
/* from [128, 255]. One important thing is that negative values */
/* are inverted here, that means [0, 128] maps to [-128, 0] linearly. */
/* More on that in `freetype.h` near `FT_RENDER_MODE_SDF` */
out = dist < 0 ? 128 - (FT_SDFFormat)udist :
(FT_SDFFormat)udist + 128;
return out;
}
/*
* Invert the signed distance packed into the corresponding format.
* So if the values are negative they will become positive in the
* chosen format.
*
* [Note]: This function should only be used after converting the
* 16.16 signed distance values to `FT_SDFFormat`, if that
* conversion has not been done, then simply invert the sign
* and use the above function to pack the values.
*/
static FT_SDFFormat
invert_sign( FT_SDFFormat dist ) {
return 255 - dist;
}
FT_END_HEADER

View File

@ -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 = (int)( bitmap->width * 2 );
bitmap->num_grays = 65535;
bitmap->pixel_mode = FT_PIXEL_MODE_GRAY;
bitmap->pitch = (int)( 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 = (int)( target.width * 2 );
target.num_grays = 65535;
target.pixel_mode = FT_PIXEL_MODE_GRAY;
target.pitch = (int)( target.width );
target.num_grays = 255;
if ( FT_ALLOC_MULT( target.buffer, target.rows, target.pitch ) )
goto Exit;