1 /****************************************************************************
5 * Signed Distance Field support for bitmap fonts (body only).
7 * Copyright (C) 2020-2023 by
8 * David Turner, Robert Wilhelm, and Werner Lemberg.
10 * Written by Anuj Verma.
12 * This file is part of the FreeType project, and may only be used,
13 * modified, and distributed under the terms of the FreeType project
14 * license, LICENSE.TXT. By continuing to use, modify, or distribute
15 * this file you indicate that you have read the license and
16 * understand and accept it fully.
21 #include <freetype/internal/ftobjs.h>
22 #include <freetype/internal/ftdebug.h>
23 #include <freetype/internal/ftmemory.h>
24 #include <freetype/fttrigon.h>
27 #include "ftsdferrs.h"
28 #include "ftsdfcommon.h"
31 /**************************************************************************
33 * A brief technical overview of how the BSDF rasterizer works
34 * -----------------------------------------------------------
37 * * SDF stands for Signed Distance Field everywhere.
39 * * BSDF stands for Bitmap to Signed Distance Field rasterizer.
41 * * This renderer converts rasterized bitmaps to SDF. There is another
42 * renderer called 'sdf', which generates SDF directly from outlines;
43 * see file `ftsdf.c` for more.
45 * * The idea of generating SDF from bitmaps is taken from two research
46 * papers, where one is dependent on the other:
48 * - Per-Erik Danielsson: Euclidean Distance Mapping
49 * http://webstaff.itn.liu.se/~stegu/JFA/Danielsson.pdf
51 * From this paper we use the eight-point sequential Euclidean
52 * distance mapping (8SED). This is the heart of the process used
55 * - Stefan Gustavson, Robin Strand: Anti-aliased Euclidean distance transform.
56 * http://weber.itn.liu.se/~stegu/aadist/edtaa_preprint.pdf
58 * The original 8SED algorithm discards the pixels' alpha values,
59 * which can contain information about the actual outline of the
60 * glyph. This paper takes advantage of those alpha values and
61 * approximates outline pretty accurately.
63 * * This rasterizer also works for monochrome bitmaps. However, the
64 * result is not as accurate since we don't have any way to
65 * approximate outlines from binary bitmaps.
67 * ========================================================================
69 * Generating SDF from bitmap is done in several steps.
71 * (1) The only information we have is the bitmap itself. It can
72 * be monochrome or anti-aliased. If it is anti-aliased, pixel values
73 * are nothing but coverage values. These coverage values can be used
74 * to extract information about the outline of the image. For
75 * example, if the pixel's alpha value is 0.5, then we can safely
76 * assume that the outline passes through the center of the pixel.
78 * (2) Find edge pixels in the bitmap (see `bsdf_is_edge` for more). For
79 * all edge pixels we use the Anti-aliased Euclidean distance
80 * transform algorithm and compute approximate edge distances (see
81 * `compute_edge_distance` and/or the second paper for more).
83 * (3) Now that we have computed approximate distances for edge pixels we
84 * use the 8SED algorithm to basically sweep the entire bitmap and
85 * compute distances for the rest of the pixels. (Since the algorithm
86 * is pretty convoluted it is only explained briefly in a comment to
87 * function `edt8`. To see the actual algorithm refer to the first
90 * (4) Finally, compute the sign for each pixel. This is done in function
91 * `finalize_sdf`. The basic idea is that if a pixel's original
92 * alpha/coverage value is greater than 0.5 then it is 'inside' (and
93 * 'outside' otherwise).
100 * dm = list of distances; // dimension equal to b
102 * foreach grid_point (x, y) in b:
104 * if (is_edge(x, y)):
105 * dm = approximate_edge_distance(b, x, y);
107 * // do the 8SED on the distances
110 * // determine the signs
111 * determine_signs(dm):
113 * // copy SDF data to the target bitmap
120 /**************************************************************************
122 * The macro FT_COMPONENT is used in trace mode. It is an implicit
123 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
124 * messages during execution.
127 #define FT_COMPONENT bsdf
130 /**************************************************************************
136 #define ONE 65536 /* 1 in 16.16 */
139 /**************************************************************************
146 /**************************************************************************
152 * This struct is used in place of @FT_Raster and is stored within the
153 * internal FreeType renderer struct. While rasterizing this is passed
154 * to the @FT_Raster_RenderFunc function, which then can be used however
159 * Used internally to allocate intermediate memory while raterizing.
162 typedef struct BSDF_TRaster_
166 } BSDF_TRaster, *BSDF_PRaster;
169 /**************************************************************************
175 * Euclidean distance. It gets used for Euclidean distance transforms;
176 * it can also be interpreted as an edge distance.
180 * Vector length of the `prox` parameter. Can be squared or absolute
181 * depending on the `USE_SQUARED_DISTANCES` macro defined in file
185 * Vector to the nearest edge. Can also be interpreted as shortest
186 * distance of a point.
189 * Alpha value of the original bitmap from which we generate SDF.
190 * Needed for computing the gradient and determining the proper sign
203 /**************************************************************************
209 * A convenience struct that is passed to functions while generating
210 * SDF; most of those functions require the same parameters.
214 * A one-dimensional array that gets interpreted as two-dimensional
215 * one. It contains the Euclidean distances of all points of the
219 * Width of the above `distance_map`.
222 * Number of rows in the above `distance_map`.
225 * Internal parameters and properties required by the rasterizer. See
226 * file `ftsdf.h` for more.
229 typedef struct BSDF_Worker_
236 SDF_Raster_Params params;
241 /**************************************************************************
247 static const ED zero_ed = { 0, { 0, 0 }, 0 };
250 /**************************************************************************
252 * rasterizer functions
256 /**************************************************************************
262 * Check whether a pixel is an edge pixel, i.e., whether it is
263 * surrounded by a completely black pixel (zero alpha), and the current
264 * pixel is not a completely black pixel.
268 * Array of distances. The parameter must point to the current
269 * pixel, i.e., the pixel that is to be checked for being an edge.
272 * The x position of the current pixel.
275 * The y position of the current pixel.
278 * Width of the bitmap.
281 * Number of rows in the bitmap.
284 * 1~if the current pixel is an edge pixel, 0~otherwise.
288 #ifdef CHECK_NEIGHBOR
289 #undef CHECK_NEIGHBOR
292 #define CHECK_NEIGHBOR( x_offset, y_offset ) \
295 if ( x + x_offset >= 0 && x + x_offset < w && \
296 y + y_offset >= 0 && y + y_offset < r ) \
300 to_check = dm + y_offset * w + x_offset; \
301 if ( to_check->alpha == 0 ) \
310 bsdf_is_edge( ED* dm, /* distance map */
311 FT_Int x, /* x index of point to check */
312 FT_Int y, /* y index of point to check */
313 FT_Int w, /* width */
314 FT_Int r ) /* rows */
318 FT_Int num_neighbors = 0;
321 if ( dm->alpha == 0 )
324 if ( dm->alpha > 0 && dm->alpha < 255 )
331 CHECK_NEIGHBOR( 0, -1 );
334 CHECK_NEIGHBOR( 0, 1 );
337 CHECK_NEIGHBOR( -1, 0 );
340 CHECK_NEIGHBOR( 1, 0 );
343 CHECK_NEIGHBOR( -1, -1 );
346 CHECK_NEIGHBOR( 1, -1 );
349 CHECK_NEIGHBOR( -1, 1 );
352 CHECK_NEIGHBOR( 1, 1 );
354 if ( num_neighbors != 8 )
361 #undef CHECK_NEIGHBOR
364 /**************************************************************************
367 * compute_edge_distance
370 * Approximate the outline and compute the distance from `current`
371 * to the approximated outline.
375 * Array of Euclidean distances. `current` must point to the position
376 * for which the distance is to be caculated. We treat this array as
377 * a two-dimensional array mapped to a one-dimensional array.
380 * The x coordinate of the `current` parameter in the array.
383 * The y coordinate of the `current` parameter in the array.
386 * The width of the distances array.
389 * Number of rows in the distances array.
392 * A vector pointing to the approximate edge distance.
395 * This is a computationally expensive function. Try to reduce the
396 * number of calls to this function. Moreover, this must only be used
397 * for edge pixel positions.
401 compute_edge_distance( ED* current,
408 * This function, based on the paper presented by Stefan Gustavson and
409 * Robin Strand, gets used to approximate edge distances from
410 * anti-aliased bitmaps.
412 * The algorithm is as follows.
414 * (1) In anti-aliased images, the pixel's alpha value is the coverage
415 * of the pixel by the outline. For example, if the alpha value is
416 * 0.5f we can assume that the outline passes through the center of
419 * (2) For this reason we can use that alpha value to approximate the real
420 * distance of the pixel to edge pretty accurately. A simple
421 * approximation is `(0.5f - alpha)`, assuming that the outline is
422 * parallel to the x or y~axis. However, in this algorithm we use a
423 * different approximation which is quite accurate even for
424 * non-axis-aligned edges.
426 * (3) The only remaining piece of information that we cannot
427 * approximate directly from the alpha is the direction of the edge.
428 * This is where we use Sobel's operator to compute the gradient of
429 * the pixel. The gradient give us a pretty good approximation of
430 * the edge direction. We use a 3x3 kernel filter to compute the
433 * (4) After the above two steps we have both the direction and the
434 * distance to the edge which is used to generate the Signed
439 * - Anti-Aliased Euclidean Distance Transform:
440 * http://weber.itn.liu.se/~stegu/aadist/edtaa_preprint.pdf
442 * https://en.wikipedia.org/wiki/Sobel_operator
445 FT_16D16_Vec g = { 0, 0 };
446 FT_16D16 dist, current_alpha;
452 /* Since our spread cannot be 0, this condition */
453 /* can never be true. */
454 if ( x <= 0 || x >= w - 1 ||
455 y <= 0 || y >= r - 1 )
458 /* initialize the alphas */
459 alphas[0] = 256 * (FT_16D16)current[-w - 1].alpha;
460 alphas[1] = 256 * (FT_16D16)current[-w ].alpha;
461 alphas[2] = 256 * (FT_16D16)current[-w + 1].alpha;
462 alphas[3] = 256 * (FT_16D16)current[ -1].alpha;
463 alphas[4] = 256 * (FT_16D16)current[ 0].alpha;
464 alphas[5] = 256 * (FT_16D16)current[ 1].alpha;
465 alphas[6] = 256 * (FT_16D16)current[ w - 1].alpha;
466 alphas[7] = 256 * (FT_16D16)current[ w ].alpha;
467 alphas[8] = 256 * (FT_16D16)current[ w + 1].alpha;
469 current_alpha = alphas[4];
471 /* Compute the gradient using the Sobel operator. */
472 /* In this case we use the following 3x3 filters: */
474 /* For x: | -1 0 -1 | */
475 /* | -root(2) 0 root(2) | */
478 /* For y: | -1 -root(2) -1 | */
480 /* | 1 root(2) 1 | */
482 /* [Note]: 92681 is root(2) in 16.16 format. */
484 FT_MulFix( alphas[3], 92681 ) -
487 FT_MulFix( alphas[5], 92681 ) +
491 FT_MulFix( alphas[1], 92681 ) -
494 FT_MulFix( alphas[7], 92681 ) +
497 FT_Vector_NormLen( &g );
499 /* The gradient gives us the direction of the */
500 /* edge for the current pixel. Once we have the */
501 /* approximate direction of the edge, we can */
502 /* approximate the edge distance much better. */
504 if ( g.x == 0 || g.y == 0 )
505 dist = ONE / 2 - alphas[4];
521 a1 = FT_DivFix( gy, gx ) / 2;
523 if ( current_alpha < a1 )
524 dist = ( gx + gy ) / 2 -
525 square_root( 2 * FT_MulFix( gx,
529 else if ( current_alpha < ( ONE - a1 ) )
530 dist = FT_MulFix( ONE / 2 - current_alpha, gx );
533 dist = -( gx + gy ) / 2 +
534 square_root( 2 * FT_MulFix( gx,
536 ONE - current_alpha ) ) );
539 g.x = FT_MulFix( g.x, dist );
540 g.y = FT_MulFix( g.y, dist );
546 /**************************************************************************
549 * bsdf_approximate_edge
552 * Loops over all the pixels and call `compute_edge_distance` only for
553 * edge pixels. This maked the process a lot faster since
554 * `compute_edge_distance` uses functions such as `FT_Vector_NormLen',
555 * which are quite slow.
559 * Contains the distance map as well as all the relevant parameters
560 * required by the function.
563 * FreeType error, 0 means success.
566 * The function directly manipulates `worker->distance_map`.
570 bsdf_approximate_edge( BSDF_Worker* worker )
572 FT_Error error = FT_Err_Ok;
578 if ( !worker || !worker->distance_map )
580 error = FT_THROW( Invalid_Argument );
584 ed = worker->distance_map;
586 for ( j = 0; j < worker->rows; j++ )
588 for ( i = 0; i < worker->width; i++ )
590 index = j * worker->width + i;
592 if ( bsdf_is_edge( worker->distance_map + index,
597 /* approximate the edge distance for edge pixels */
598 ed[index].prox = compute_edge_distance( ed + index,
602 ed[index].dist = VECTOR_LENGTH_16D16( ed[index].prox );
606 /* for non-edge pixels assign far away distances */
607 ed[index].dist = 400 * ONE;
608 ed[index].prox.x = 200 * ONE;
609 ed[index].prox.y = 200 * ONE;
619 /**************************************************************************
622 * bsdf_init_distance_map
625 * Initialize the distance map according to the '8-point sequential
626 * Euclidean distance mapping' (8SED) algorithm. Basically it copies
627 * the `source` bitmap alpha values to the `distance_map->alpha`
628 * parameter of `worker`.
632 * Source bitmap to copy the data from.
636 * Target distance map to copy the data to.
639 * FreeType error, 0 means success.
643 bsdf_init_distance_map( const FT_Bitmap* source,
644 BSDF_Worker* worker )
646 FT_Error error = FT_Err_Ok;
648 FT_Int x_diff, y_diff;
649 FT_Int t_i, t_j, s_i, s_j;
654 /* again check the parameters (probably unnecessary) */
655 if ( !source || !worker )
657 error = FT_THROW( Invalid_Argument );
661 /* Because of the way we convert a bitmap to SDF, */
662 /* i.e., aligning the source to the center of the */
663 /* target, the target's width and rows must be */
664 /* checked before copying. */
665 if ( worker->width < (FT_Int)source->width ||
666 worker->rows < (FT_Int)source->rows )
668 error = FT_THROW( Invalid_Argument );
672 /* check pixel mode */
673 if ( source->pixel_mode == FT_PIXEL_MODE_NONE )
675 FT_ERROR(( "bsdf_copy_source_to_target:"
676 " Invalid pixel mode of source bitmap" ));
677 error = FT_THROW( Invalid_Argument );
681 #ifdef FT_DEBUG_LEVEL_TRACE
682 if ( source->pixel_mode == FT_PIXEL_MODE_MONO )
684 FT_TRACE0(( "bsdf_copy_source_to_target:"
685 " The `bsdf' renderer can convert monochrome\n" ));
687 " bitmaps to SDF but the results are not perfect\n" ));
689 " because there is no way to approximate actual\n" ));
691 " outlines from monochrome bitmaps. Consider\n" ));
693 " using an anti-aliased bitmap instead.\n" ));
697 /* Calculate the width and row differences */
698 /* between target and source. */
699 x_diff = worker->width - (int)source->width;
700 y_diff = worker->rows - (int)source->rows;
705 t = (ED*)worker->distance_map;
708 /* For now we only support pixel mode `FT_PIXEL_MODE_MONO` */
709 /* and `FT_PIXEL_MODE_GRAY`. More will be added later. */
711 /* [NOTE]: We can also use @FT_Bitmap_Convert to convert */
712 /* bitmap to 8bpp. To avoid extra allocation and */
713 /* since the target bitmap can be 16bpp we manually */
714 /* convert the source bitmap to the desired bpp. */
716 switch ( source->pixel_mode )
718 case FT_PIXEL_MODE_MONO:
720 FT_Int t_width = worker->width;
721 FT_Int t_rows = worker->rows;
722 FT_Int s_width = (int)source->width;
723 FT_Int s_rows = (int)source->rows;
726 for ( t_j = 0; t_j < t_rows; t_j++ )
728 for ( t_i = 0; t_i < t_width; t_i++ )
730 FT_Int t_index = t_j * t_width + t_i;
736 t[t_index] = zero_ed;
741 /* Assign 0 to padding similar to */
742 /* the source bitmap. */
743 if ( s_i < 0 || s_i >= s_width ||
744 s_j < 0 || s_j >= s_rows )
747 if ( worker->params.flip_y )
748 s_index = ( s_rows - s_j - 1 ) * source->pitch;
750 s_index = s_j * source->pitch;
752 div = s_index + s_i / 8;
756 byte = (FT_Byte)( 1 << mod );
758 t[t_index].alpha = pixel & byte ? 255 : 0;
764 case FT_PIXEL_MODE_GRAY:
766 FT_Int t_width = worker->width;
767 FT_Int t_rows = worker->rows;
768 FT_Int s_width = (int)source->width;
769 FT_Int s_rows = (int)source->rows;
772 /* loop over all pixels and assign pixel values from source */
773 for ( t_j = 0; t_j < t_rows; t_j++ )
775 for ( t_i = 0; t_i < t_width; t_i++ )
777 FT_Int t_index = t_j * t_width + t_i;
781 t[t_index] = zero_ed;
786 /* Assign 0 to padding similar to */
787 /* the source bitmap. */
788 if ( s_i < 0 || s_i >= s_width ||
789 s_j < 0 || s_j >= s_rows )
792 if ( worker->params.flip_y )
793 s_index = ( s_rows - s_j - 1 ) * s_width + s_i;
795 s_index = s_j * s_width + s_i;
797 /* simply copy the alpha values */
798 t[t_index].alpha = s[s_index];
805 FT_ERROR(( "bsdf_copy_source_to_target:"
806 " unsopported pixel mode of source bitmap\n" ));
808 error = FT_THROW( Unimplemented_Feature );
817 /**************************************************************************
823 * Compare neighbor pixel (which is defined by the offset) and update
824 * `current` distance if the new distance is shorter than the original.
828 * X offset of the neighbor to be checked. The offset is relative to
832 * Y offset of the neighbor to be checked. The offset is relative to
836 * Width of the `current` array.
840 * Pointer into array of distances. This parameter must point to the
841 * position whose neighbor is to be checked. The array is treated as
842 * a two-dimensional array.
846 compare_neighbor( ED* current,
853 FT_16D16_Vec dist_vec;
856 to_check = current + ( y_offset * width ) + x_offset;
859 * While checking for the nearest point we first approximate the
860 * distance of `current` by adding the deviation (which is sqrt(2) at
861 * most). Only if the new value is less than the current value we
862 * calculate the actual distances using `FT_Vector_Length`. This last
863 * step can be omitted by using squared distances.
867 * Approximate the distance. We subtract 1 to avoid precision errors,
868 * which could happen because the two directions can be opposite.
870 dist = to_check->dist - ONE;
872 if ( dist < current->dist )
874 dist_vec = to_check->prox;
876 dist_vec.x += x_offset * ONE;
877 dist_vec.y += y_offset * ONE;
878 dist = VECTOR_LENGTH_16D16( dist_vec );
880 if ( dist < current->dist )
882 current->dist = dist;
883 current->prox = dist_vec;
889 /**************************************************************************
895 * First pass of the 8SED algorithm. Loop over the bitmap from top to
896 * bottom and scan each row left to right, updating the distances in
897 * `worker->distance_map`.
901 * Contains all the relevant parameters.
905 first_pass( BSDF_Worker* worker )
907 FT_Int i, j; /* iterators */
908 FT_Int w, r; /* width, rows */
909 ED* dm; /* distance map */
912 dm = worker->distance_map;
916 /* Start scanning from top to bottom and sweep each */
917 /* row back and forth comparing the distances of the */
918 /* neighborhood. Leave the first row as it has no top */
919 /* neighbor; it will be covered in the second scan of */
920 /* the image (from bottom to top). */
921 for ( j = 1; j < r; j++ )
927 /* Forward pass of rows (left -> right). Leave the first */
928 /* column, which gets covered in the backward pass. */
929 for ( i = 1; i < w - 1; i++ )
932 current = dm + index;
935 compare_neighbor( current, -1, -1, w );
937 compare_neighbor( current, 0, -1, w );
939 compare_neighbor( current, 1, -1, w );
941 compare_neighbor( current, -1, 0, w );
944 /* Backward pass of rows (right -> left). Leave the last */
945 /* column, which was already covered in the forward pass. */
946 for ( i = w - 2; i >= 0; i-- )
949 current = dm + index;
952 compare_neighbor( current, 1, 0, w );
958 /**************************************************************************
964 * Second pass of the 8SED algorithm. Loop over the bitmap from bottom
965 * to top and scan each row left to right, updating the distances in
966 * `worker->distance_map`.
970 * Contains all the relevant parameters.
974 second_pass( BSDF_Worker* worker )
976 FT_Int i, j; /* iterators */
977 FT_Int w, r; /* width, rows */
978 ED* dm; /* distance map */
981 dm = worker->distance_map;
985 /* Start scanning from bottom to top and sweep each */
986 /* row back and forth comparing the distances of the */
987 /* neighborhood. Leave the last row as it has no down */
988 /* neighbor; it is already covered in the first scan */
989 /* of the image (from top to bottom). */
990 for ( j = r - 2; j >= 0; j-- )
996 /* Forward pass of rows (left -> right). Leave the first */
997 /* column, which gets covered in the backward pass. */
998 for ( i = 1; i < w - 1; i++ )
1001 current = dm + index;
1004 compare_neighbor( current, -1, 1, w );
1006 compare_neighbor( current, 0, 1, w );
1008 compare_neighbor( current, 1, 1, w );
1010 compare_neighbor( current, -1, 0, w );
1013 /* Backward pass of rows (right -> left). Leave the last */
1014 /* column, which was already covered in the forward pass. */
1015 for ( i = w - 2; i >= 0; i-- )
1018 current = dm + index;
1021 compare_neighbor( current, 1, 0, w );
1027 /**************************************************************************
1033 * Compute the distance map of the a bitmap. Execute both first and
1034 * second pass of the 8SED algorithm.
1038 * Contains all the relevant parameters.
1041 * FreeType error, 0 means success.
1045 edt8( BSDF_Worker* worker )
1047 FT_Error error = FT_Err_Ok;
1050 if ( !worker || !worker->distance_map )
1052 error = FT_THROW( Invalid_Argument );
1056 /* first scan of the image */
1057 first_pass( worker );
1059 /* second scan of the image */
1060 second_pass( worker );
1067 /**************************************************************************
1073 * Copy the SDF data from `worker->distance_map` to the `target` bitmap.
1074 * Also transform the data to output format, (which is 6.10 fixed-point
1075 * format at the moment).
1079 * Contains source distance map and other SDF data.
1083 * Target bitmap to which the SDF data is copied to.
1086 * FreeType error, 0 means success.
1090 finalize_sdf( BSDF_Worker* worker,
1091 const FT_Bitmap* target )
1093 FT_Error error = FT_Err_Ok;
1098 FT_SDFFormat* t_buffer;
1099 FT_16D16 sp_sq, spread;
1102 if ( !worker || !target )
1104 error = FT_THROW( Invalid_Argument );
1108 w = (int)target->width;
1109 r = (int)target->rows;
1110 t_buffer = (FT_SDFFormat*)target->buffer;
1112 if ( w != worker->width ||
1115 error = FT_THROW( Invalid_Argument );
1119 spread = (FT_16D16)FT_INT_16D16( worker->params.spread );
1121 #if USE_SQUARED_DISTANCES
1122 sp_sq = (FT_16D16)FT_INT_16D16( worker->params.spread *
1123 worker->params.spread );
1125 sp_sq = (FT_16D16)FT_INT_16D16( worker->params.spread );
1128 for ( j = 0; j < r; j++ )
1130 for ( i = 0; i < w; i++ )
1134 FT_SDFFormat final_dist;
1139 dist = worker->distance_map[index].dist;
1141 if ( dist < 0 || dist > sp_sq )
1144 #if USE_SQUARED_DISTANCES
1145 dist = square_root( dist );
1148 /* We assume that if the pixel is inside a contour */
1149 /* its coverage value must be > 127. */
1150 sign = worker->distance_map[index].alpha < 127 ? -1 : 1;
1152 /* flip the sign according to the property */
1153 if ( worker->params.flip_sign )
1156 /* concatenate from 16.16 to appropriate format */
1157 final_dist = map_fixed_to_sdf( dist * sign, spread );
1159 t_buffer[index] = final_dist;
1168 /**************************************************************************
1170 * interface functions
1174 /* called when adding a new module through @FT_Add_Module */
1176 bsdf_raster_new( void* memory_, /* FT_Memory */
1177 FT_Raster* araster_ ) /* BSDF_PRaster* */
1179 FT_Memory memory = (FT_Memory)memory_;
1180 BSDF_PRaster* araster = (BSDF_PRaster*)araster_;
1183 BSDF_PRaster raster = NULL;
1186 if ( !FT_NEW( raster ) )
1187 raster->memory = memory;
1197 bsdf_raster_reset( FT_Raster raster,
1198 unsigned char* pool_base,
1199 unsigned long pool_size )
1201 FT_UNUSED( raster );
1202 FT_UNUSED( pool_base );
1203 FT_UNUSED( pool_size );
1209 bsdf_raster_set_mode( FT_Raster raster,
1213 FT_UNUSED( raster );
1221 /* called while rendering through @FT_Render_Glyph */
1223 bsdf_raster_render( FT_Raster raster,
1224 const FT_Raster_Params* params )
1226 FT_Error error = FT_Err_Ok;
1227 FT_Memory memory = NULL;
1229 const FT_Bitmap* source = NULL;
1230 const FT_Bitmap* target = NULL;
1232 BSDF_TRaster* bsdf_raster = (BSDF_TRaster*)raster;
1235 const SDF_Raster_Params* sdf_params = (const SDF_Raster_Params*)params;
1238 worker.distance_map = NULL;
1240 /* check for valid parameters */
1241 if ( !raster || !params )
1243 error = FT_THROW( Invalid_Argument );
1247 /* check whether the flag is set */
1248 if ( sdf_params->root.flags != FT_RASTER_FLAG_SDF )
1250 error = FT_THROW( Raster_Corrupted );
1254 source = (const FT_Bitmap*)sdf_params->root.source;
1255 target = (const FT_Bitmap*)sdf_params->root.target;
1257 /* check source and target bitmap */
1258 if ( !source || !target )
1260 error = FT_THROW( Invalid_Argument );
1264 memory = bsdf_raster->memory;
1267 FT_TRACE0(( "bsdf_raster_render: Raster not set up properly,\n" ));
1268 FT_TRACE0(( " unable to find memory handle.\n" ));
1270 error = FT_THROW( Invalid_Handle );
1274 /* check whether spread is set properly */
1275 if ( sdf_params->spread > MAX_SPREAD ||
1276 sdf_params->spread < MIN_SPREAD )
1278 FT_TRACE0(( "bsdf_raster_render:"
1279 " The `spread' field of `SDF_Raster_Params'\n" ));
1281 " is invalid; the value of this field must be\n" ));
1283 " within [%d, %d].\n",
1284 MIN_SPREAD, MAX_SPREAD ));
1286 " Also, you must pass `SDF_Raster_Params'\n" ));
1288 " instead of the default `FT_Raster_Params'\n" ));
1290 " while calling this function and set the fields\n" ));
1292 " accordingly.\n" ));
1294 error = FT_THROW( Invalid_Argument );
1298 /* set up the worker */
1300 /* allocate the distance map */
1301 if ( FT_QALLOC_MULT( worker.distance_map, target->rows,
1302 target->width * sizeof ( *worker.distance_map ) ) )
1305 worker.width = (int)target->width;
1306 worker.rows = (int)target->rows;
1307 worker.params = *sdf_params;
1309 FT_CALL( bsdf_init_distance_map( source, &worker ) );
1310 FT_CALL( bsdf_approximate_edge( &worker ) );
1311 FT_CALL( edt8( &worker ) );
1312 FT_CALL( finalize_sdf( &worker, target ) );
1314 FT_TRACE0(( "bsdf_raster_render: Total memory used = %ld\n",
1315 worker.width * worker.rows *
1316 (long)sizeof ( *worker.distance_map ) ));
1319 if ( worker.distance_map )
1320 FT_FREE( worker.distance_map );
1326 /* called while deleting `FT_Library` only if the module is added */
1328 bsdf_raster_done( FT_Raster raster )
1330 FT_Memory memory = (FT_Memory)((BSDF_TRaster*)raster)->memory;
1337 FT_DEFINE_RASTER_FUNCS(
1338 ft_bitmap_sdf_raster,
1340 FT_GLYPH_FORMAT_BITMAP,
1342 (FT_Raster_New_Func) bsdf_raster_new, /* raster_new */
1343 (FT_Raster_Reset_Func) bsdf_raster_reset, /* raster_reset */
1344 (FT_Raster_Set_Mode_Func)bsdf_raster_set_mode, /* raster_set_mode */
1345 (FT_Raster_Render_Func) bsdf_raster_render, /* raster_render */
1346 (FT_Raster_Done_Func) bsdf_raster_done /* raster_done */