godot/thirdparty/freetype/src/sdf/ftbsdf.c

/****************************************************************************
 *
 * ftbsdf.c
 *
 *   Signed Distance Field support for bitmap fonts (body only).
 *
 * Copyright (C) 2020-2023 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * Written by Anuj Verma.
 *
 * 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.
 *
 */


#include <freetype/internal/ftobjs.h>
#include <freetype/internal/ftdebug.h>
#include <freetype/internal/ftmemory.h>
#include <freetype/fttrigon.h>

#include "ftsdf.h"
#include "ftsdferrs.h"
#include "ftsdfcommon.h"


  /**************************************************************************
   *
   * A brief technical overview of how the BSDF rasterizer works
   * -----------------------------------------------------------
   *
   * [Notes]:
   *   * SDF stands for Signed Distance Field everywhere.
   *
   *   * BSDF stands for Bitmap to Signed Distance Field rasterizer.
   *
   *   * This renderer converts rasterized bitmaps to SDF.  There is another
   *     renderer called 'sdf', which generates SDF directly from outlines;
   *     see file `ftsdf.c` for more.
   *
   *   * The idea of generating SDF from bitmaps is taken from two research
   *     papers, where one is dependent on the other:
   *
   *     - Per-Erik Danielsson: Euclidean Distance Mapping
   *       http://webstaff.itn.liu.se/~stegu/JFA/Danielsson.pdf
   *
   *       From this paper we use the eight-point sequential Euclidean
   *       distance mapping (8SED).  This is the heart of the process used
   *       in this rasterizer.
   *
   *     - Stefan Gustavson, Robin Strand: Anti-aliased Euclidean distance transform.
   *       http://weber.itn.liu.se/~stegu/aadist/edtaa_preprint.pdf
   *
   *       The original 8SED algorithm discards the pixels' alpha values,
   *       which can contain information about the actual outline of the
   *       glyph.  This paper takes advantage of those alpha values and
   *       approximates outline pretty accurately.
   *
   *   * This rasterizer also works for monochrome bitmaps.  However, the
   *     result is not as accurate since we don't have any way to
   *     approximate outlines from binary bitmaps.
   *
   * ========================================================================
   *
   * Generating SDF from bitmap is done in several steps.
   *
   * (1) The only information we have is the bitmap itself.  It can
   *     be monochrome or anti-aliased.  If it is anti-aliased, pixel values
   *     are nothing but coverage values.  These coverage values can be used
   *     to extract information about the outline of the image.  For
   *     example, if the pixel's alpha value is 0.5, then we can safely
   *     assume that the outline passes through the center of the pixel.
   *
   * (2) Find edge pixels in the bitmap (see `bsdf_is_edge` for more).  For
   *     all edge pixels we use the Anti-aliased Euclidean distance
   *     transform algorithm and compute approximate edge distances (see
   *     `compute_edge_distance` and/or the second paper for more).
   *
   * (3) Now that we have computed approximate distances for edge pixels we
   *     use the 8SED algorithm to basically sweep the entire bitmap and
   *     compute distances for the rest of the pixels.  (Since the algorithm
   *     is pretty convoluted it is only explained briefly in a comment to
   *     function `edt8`.  To see the actual algorithm refer to the first
   *     paper.)
   *
   * (4) Finally, compute the sign for each pixel.  This is done in function
   *     `finalize_sdf`.  The basic idea is that if a pixel's original
   *     alpha/coverage value is greater than 0.5 then it is 'inside' (and
   *     'outside' otherwise).
   *
   * Pseudo Code:
   *
   * ```
   * b  = source bitmap;
   * t  = target bitmap;
   * dm = list of distances; // dimension equal to b
   *
   * foreach grid_point (x, y) in b:
   * {
   *   if (is_edge(x, y)):
   *     dm = approximate_edge_distance(b, x, y);
   *
   *   // do the 8SED on the distances
   *   edt8(dm);
   *
   *   // determine the signs
   *   determine_signs(dm):
   *
   *   // copy SDF data to the target bitmap
   *   copy(dm to t);
   * }
   *
   */


  /**************************************************************************
   *
   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
   * messages during execution.
   */
#undef  FT_COMPONENT
#define FT_COMPONENT


  /**************************************************************************
   *
   * useful macros
   *
   */

#define ONE


  /**************************************************************************
   *
   * structs
   *
   */


  /**************************************************************************
   *
   * @Struct:
   *   BSDF_TRaster
   *
   * @Description:
   *   This struct is used in place of @FT_Raster and is stored within the
   *   internal FreeType renderer struct.  While rasterizing this is passed
   *   to the @FT_Raster_RenderFunc function, which then can be used however
   *   we want.
   *
   * @Fields:
   *   memory ::
   *     Used internally to allocate intermediate memory while raterizing.
   *
   */
  BSDF_PRaster;


  /**************************************************************************
   *
   * @Struct:
   *   ED
   *
   * @Description:
   *   Euclidean distance.  It gets used for Euclidean distance transforms;
   *   it can also be interpreted as an edge distance.
   *
   * @Fields:
   *   dist ::
   *     Vector length of the `prox` parameter.  Can be squared or absolute
   *     depending on the `USE_SQUARED_DISTANCES` macro defined in file
   *     `ftsdfcommon.h`.
   *
   *   prox ::
   *     Vector to the nearest edge.  Can also be interpreted as shortest
   *     distance of a point.
   *
   *   alpha ::
   *     Alpha value of the original bitmap from which we generate SDF.
   *     Needed for computing the gradient and determining the proper sign
   *     of a pixel.
   *
   */
  ED;


  /**************************************************************************
   *
   * @Struct:
   *   BSDF_Worker
   *
   * @Description:
   *   A convenience struct that is passed to functions while generating
   *   SDF; most of those functions require the same parameters.
   *
   * @Fields:
   *   distance_map ::
   *     A one-dimensional array that gets interpreted as two-dimensional
   *     one.  It contains the Euclidean distances of all points of the
   *     bitmap.
   *
   *   width ::
   *     Width of the above `distance_map`.
   *
   *   rows ::
   *     Number of rows in the above `distance_map`.
   *
   *   params ::
   *     Internal parameters and properties required by the rasterizer.  See
   *     file `ftsdf.h` for more.
   *
   */
  BSDF_Worker;


  /**************************************************************************
   *
   * initializer
   *
   */

  static const ED  zero_ed =;


  /**************************************************************************
   *
   * rasterizer functions
   *
   */

  /**************************************************************************
   *
   * @Function:
   *   bsdf_is_edge
   *
   * @Description:
   *   Check whether a pixel is an edge pixel, i.e., whether it is
   *   surrounded by a completely black pixel (zero alpha), and the current
   *   pixel is not a completely black pixel.
   *
   * @Input:
   *   dm ::
   *     Array of distances.  The parameter must point to the current
   *     pixel, i.e., the pixel that is to be checked for being an edge.
   *
   *   x ::
   *     The x position of the current pixel.
   *
   *   y ::
   *     The y position of the current pixel.
   *
   *   w ::
   *     Width of the bitmap.
   *
   *   r ::
   *     Number of rows in the bitmap.
   *
   * @Return:
   *   1~if the current pixel is an edge pixel, 0~otherwise.
   *
   */

#ifdef CHECK_NEIGHBOR
#undef CHECK_NEIGHBOR
#endif

#define CHECK_NEIGHBOR

  static FT_Bool
  bsdf_is_edge( ED*     dm,   /* distance map              */
                FT_Int  x,    /* x index of point to check */
                FT_Int  y,    /* y index of point to check */
                FT_Int  w,    /* width                     */
                FT_Int  r )   /* rows                      */
  {}

#undef CHECK_NEIGHBOR


  /**************************************************************************
   *
   * @Function:
   *   compute_edge_distance
   *
   * @Description:
   *   Approximate the outline and compute the distance from `current`
   *   to the approximated outline.
   *
   * @Input:
   *   current ::
   *     Array of Euclidean distances.  `current` must point to the position
   *     for which the distance is to be caculated.  We treat this array as
   *     a two-dimensional array mapped to a one-dimensional array.
   *
   *   x ::
   *     The x coordinate of the `current` parameter in the array.
   *
   *   y ::
   *     The y coordinate of the `current` parameter in the array.
   *
   *   w ::
   *     The width of the distances array.
   *
   *   r ::
   *     Number of rows in the distances array.
   *
   * @Return:
   *   A vector pointing to the approximate edge distance.
   *
   * @Note:
   *   This is a computationally expensive function.  Try to reduce the
   *   number of calls to this function.  Moreover, this must only be used
   *   for edge pixel positions.
   *
   */
  static FT_16D16_Vec
  compute_edge_distance( ED*     current,
                         FT_Int  x,
                         FT_Int  y,
                         FT_Int  w,
                         FT_Int  r )
  {}


  /**************************************************************************
   *
   * @Function:
   *   bsdf_approximate_edge
   *
   * @Description:
   *   Loops over all the pixels and call `compute_edge_distance` only for
   *   edge pixels.  This maked the process a lot faster since
   *   `compute_edge_distance` uses functions such as `FT_Vector_NormLen',
   *   which are quite slow.
   *
   * @InOut:
   *   worker ::
   *     Contains the distance map as well as all the relevant parameters
   *     required by the function.
   *
   * @Return:
   *   FreeType error, 0 means success.
   *
   * @Note:
   *   The function directly manipulates `worker->distance_map`.
   *
   */
  static FT_Error
  bsdf_approximate_edge( BSDF_Worker*  worker )
  {}


  /**************************************************************************
   *
   * @Function:
   *   bsdf_init_distance_map
   *
   * @Description:
   *   Initialize the distance map according to the '8-point sequential
   *   Euclidean distance mapping' (8SED) algorithm.  Basically it copies
   *   the `source` bitmap alpha values to the `distance_map->alpha`
   *   parameter of `worker`.
   *
   * @Input:
   *   source ::
   *     Source bitmap to copy the data from.
   *
   * @Output:
   *   worker ::
   *     Target distance map to copy the data to.
   *
   * @Return:
   *   FreeType error, 0 means success.
   *
   */
  static FT_Error
  bsdf_init_distance_map( const FT_Bitmap*  source,
                          BSDF_Worker*      worker )
  {}


  /**************************************************************************
   *
   * @Function:
   *   compare_neighbor
   *
   * @Description:
   *   Compare neighbor pixel (which is defined by the offset) and update
   *   `current` distance if the new distance is shorter than the original.
   *
   * @Input:
   *   x_offset ::
   *     X offset of the neighbor to be checked.  The offset is relative to
   *     the `current`.
   *
   *   y_offset ::
   *     Y offset of the neighbor to be checked.  The offset is relative to
   *     the `current`.
   *
   *   width ::
   *     Width of the `current` array.
   *
   * @InOut:
   *   current ::
   *     Pointer into array of distances.  This parameter must point to the
   *     position whose neighbor is to be checked.  The array is treated as
   *     a two-dimensional array.
   *
   */
  static void
  compare_neighbor( ED*     current,
                    FT_Int  x_offset,
                    FT_Int  y_offset,
                    FT_Int  width )
  {}


  /**************************************************************************
   *
   * @Function:
   *   first_pass
   *
   * @Description:
   *   First pass of the 8SED algorithm.  Loop over the bitmap from top to
   *   bottom and scan each row left to right, updating the distances in
   *   `worker->distance_map`.
   *
   * @InOut:
   *   worker::
   *     Contains all the relevant parameters.
   *
   */
  static void
  first_pass( BSDF_Worker*  worker )
  {}


  /**************************************************************************
   *
   * @Function:
   *   second_pass
   *
   * @Description:
   *   Second pass of the 8SED algorithm.  Loop over the bitmap from bottom
   *   to top and scan each row left to right, updating the distances in
   *   `worker->distance_map`.
   *
   * @InOut:
   *   worker::
   *     Contains all the relevant parameters.
   *
   */
  static void
  second_pass( BSDF_Worker*  worker )
  {}


  /**************************************************************************
   *
   * @Function:
   *   edt8
   *
   * @Description:
   *   Compute the distance map of the a bitmap.  Execute both first and
   *   second pass of the 8SED algorithm.
   *
   * @InOut:
   *   worker::
   *     Contains all the relevant parameters.
   *
   * @Return:
   *   FreeType error, 0 means success.
   *
   */
  static FT_Error
  edt8( BSDF_Worker*  worker )
  {}


  /**************************************************************************
   *
   * @Function:
   *   finalize_sdf
   *
   * @Description:
   *   Copy the SDF data from `worker->distance_map` to the `target` bitmap.
   *   Also transform the data to output format, (which is 6.10 fixed-point
   *   format at the moment).
   *
   * @Input:
   *   worker ::
   *     Contains source distance map and other SDF data.
   *
   * @Output:
   *   target ::
   *     Target bitmap to which the SDF data is copied to.
   *
   * @Return:
   *   FreeType error, 0 means success.
   *
   */
  static FT_Error
  finalize_sdf( BSDF_Worker*      worker,
                const FT_Bitmap*  target )
  {}


  /**************************************************************************
   *
   * interface functions
   *
   */

  /* called when adding a new module through @FT_Add_Module */
  static FT_Error
  bsdf_raster_new( void*       memory_,    /* FT_Memory     */
                   FT_Raster*  araster_ )  /* BSDF_PRaster* */
  {}


  /* unused */
  static void
  bsdf_raster_reset( FT_Raster       raster,
                     unsigned char*  pool_base,
                     unsigned long   pool_size )
  {}


  /* unused */
  static FT_Error
  bsdf_raster_set_mode( FT_Raster      raster,
                        unsigned long  mode,
                        void*          args )
  {}


  /* called while rendering through @FT_Render_Glyph */
  static FT_Error
  bsdf_raster_render( FT_Raster                raster,
                      const FT_Raster_Params*  params )
  {}


  /* called while deleting `FT_Library` only if the module is added */
  static void
  bsdf_raster_done( FT_Raster  raster )
  {}


  FT_DEFINE_RASTER_FUNCS(
    ft_bitmap_sdf_raster,

    FT_GLYPH_FORMAT_BITMAP,

    (FT_Raster_New_Func)     bsdf_raster_new,       /* raster_new      */
    (FT_Raster_Reset_Func)   bsdf_raster_reset,     /* raster_reset    */
    (FT_Raster_Set_Mode_Func)bsdf_raster_set_mode,  /* raster_set_mode */
    (FT_Raster_Render_Func)  bsdf_raster_render,    /* raster_render   */
    (FT_Raster_Done_Func)    bsdf_raster_done       /* raster_done     */
  )


/* END */