sdk-world v2026.11.05
PS2 SDK - world tier (copyleft-allowed, non-essential packages)
Loading...
Searching...
No Matches
ftbsdf.c
1/****************************************************************************
2 *
3 * ftbsdf.c
4 *
5 * Signed Distance Field support for bitmap fonts (body only).
6 *
7 * Copyright (C) 2020-2025 by
8 * David Turner, Robert Wilhelm, and Werner Lemberg.
9 *
10 * Written by Anuj Verma.
11 *
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.
17 *
18 */
19
20
24#include <freetype/fttrigon.h>
25
26#include "ftsdf.h"
27#include "ftsdferrs.h"
28#include "ftsdfcommon.h"
29
30
31 /**************************************************************************
32 *
33 * A brief technical overview of how the BSDF rasterizer works
34 * -----------------------------------------------------------
35 *
36 * [Notes]:
37 * * SDF stands for Signed Distance Field everywhere.
38 *
39 * * BSDF stands for Bitmap to Signed Distance Field rasterizer.
40 *
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.
44 *
45 * * The idea of generating SDF from bitmaps is taken from two research
46 * papers, where one is dependent on the other:
47 *
48 * - Per-Erik Danielsson: Euclidean Distance Mapping
49 * http://webstaff.itn.liu.se/~stegu/JFA/Danielsson.pdf
50 *
51 * From this paper we use the eight-point sequential Euclidean
52 * distance mapping (8SED). This is the heart of the process used
53 * in this rasterizer.
54 *
55 * - Stefan Gustavson, Robin Strand: Anti-aliased Euclidean distance transform.
56 * http://weber.itn.liu.se/~stegu/aadist/edtaa_preprint.pdf
57 *
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.
62 *
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.
66 *
67 * ========================================================================
68 *
69 * Generating SDF from bitmap is done in several steps.
70 *
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.
77 *
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).
82 *
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
88 * paper.)
89 *
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).
94 *
95 * Pseudo Code:
96 *
97 * ```
98 * b = source bitmap;
99 * t = target bitmap;
100 * dm = list of distances; // dimension equal to b
101 *
102 * foreach grid_point (x, y) in b:
103 * {
104 * if (is_edge(x, y)):
105 * dm = approximate_edge_distance(b, x, y);
106 *
107 * // do the 8SED on the distances
108 * edt8(dm);
109 *
110 * // determine the signs
111 * determine_signs(dm):
112 *
113 * // copy SDF data to the target bitmap
114 * copy(dm to t);
115 * }
116 *
117 */
118
119
120 /**************************************************************************
121 *
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.
125 */
126#undef FT_COMPONENT
127#define FT_COMPONENT bsdf
128
129
130 /**************************************************************************
131 *
132 * useful macros
133 *
134 */
135
136#define ONE 65536 /* 1 in 16.16 */
137
138
139 /**************************************************************************
140 *
141 * structs
142 *
143 */
144
145
146 /**************************************************************************
147 *
148 * @Struct:
149 * BSDF_TRaster
150 *
151 * @Description:
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
155 * we want.
156 *
157 * @Fields:
158 * memory ::
159 * Used internally to allocate intermediate memory while raterizing.
160 *
161 */
162 typedef struct BSDF_TRaster_
163 {
164 FT_Memory memory;
165
166 } BSDF_TRaster, *BSDF_PRaster;
167
168
169 /**************************************************************************
170 *
171 * @Struct:
172 * ED
173 *
174 * @Description:
175 * Euclidean distance. It gets used for Euclidean distance transforms;
176 * it can also be interpreted as an edge distance.
177 *
178 * @Fields:
179 * dist ::
180 * Vector length of the `prox` parameter. Can be squared or absolute
181 * depending on the `USE_SQUARED_DISTANCES` macro defined in file
182 * `ftsdfcommon.h`.
183 *
184 * prox ::
185 * Vector to the nearest edge. Can also be interpreted as shortest
186 * distance of a point.
187 *
188 * alpha ::
189 * Alpha value of the original bitmap from which we generate SDF.
190 * Needed for computing the gradient and determining the proper sign
191 * of a pixel.
192 *
193 */
194 typedef struct ED_
195 {
196 FT_16D16 dist;
197 FT_16D16_Vec prox;
198 FT_Byte alpha;
199
200 } ED;
201
202
203 /**************************************************************************
204 *
205 * @Struct:
206 * BSDF_Worker
207 *
208 * @Description:
209 * A convenience struct that is passed to functions while generating
210 * SDF; most of those functions require the same parameters.
211 *
212 * @Fields:
213 * distance_map ::
214 * A one-dimensional array that gets interpreted as two-dimensional
215 * one. It contains the Euclidean distances of all points of the
216 * bitmap.
217 *
218 * width ::
219 * Width of the above `distance_map`.
220 *
221 * rows ::
222 * Number of rows in the above `distance_map`.
223 *
224 * params ::
225 * Internal parameters and properties required by the rasterizer. See
226 * file `ftsdf.h` for more.
227 *
228 */
229 typedef struct BSDF_Worker_
230 {
231 ED* distance_map;
232
233 FT_Int width;
234 FT_Int rows;
235
236 SDF_Raster_Params params;
237
238 } BSDF_Worker;
239
240
241 /**************************************************************************
242 *
243 * initializer
244 *
245 */
246
247 static const ED zero_ed = { 0, { 0, 0 }, 0 };
248
249
250 /**************************************************************************
251 *
252 * rasterizer functions
253 *
254 */
255
256 /**************************************************************************
257 *
258 * @Function:
259 * bsdf_is_edge
260 *
261 * @Description:
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.
265 *
266 * @Input:
267 * dm ::
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.
270 *
271 * x ::
272 * The x position of the current pixel.
273 *
274 * y ::
275 * The y position of the current pixel.
276 *
277 * w ::
278 * Width of the bitmap.
279 *
280 * r ::
281 * Number of rows in the bitmap.
282 *
283 * @Return:
284 * 1~if the current pixel is an edge pixel, 0~otherwise.
285 *
286 */
287
288#ifdef CHECK_NEIGHBOR
289#undef CHECK_NEIGHBOR
290#endif
291
292#define CHECK_NEIGHBOR( x_offset, y_offset ) \
293 do \
294 { \
295 if ( x + x_offset >= 0 && x + x_offset < w && \
296 y + y_offset >= 0 && y + y_offset < r ) \
297 { \
298 num_neighbors++; \
299 \
300 to_check = dm + y_offset * w + x_offset; \
301 if ( to_check->alpha == 0 ) \
302 { \
303 is_edge = 1; \
304 goto Done; \
305 } \
306 } \
307 } while ( 0 )
308
309 static FT_Bool
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 */
315 {
316 FT_Bool is_edge = 0;
317 ED* to_check = NULL;
318 FT_Int num_neighbors = 0;
319
320
321 if ( dm->alpha == 0 )
322 goto Done;
323
324 if ( dm->alpha > 0 && dm->alpha < 255 )
325 {
326 is_edge = 1;
327 goto Done;
328 }
329
330 /* up */
331 CHECK_NEIGHBOR( 0, -1 );
332
333 /* down */
334 CHECK_NEIGHBOR( 0, 1 );
335
336 /* left */
337 CHECK_NEIGHBOR( -1, 0 );
338
339 /* right */
340 CHECK_NEIGHBOR( 1, 0 );
341
342 /* up left */
343 CHECK_NEIGHBOR( -1, -1 );
344
345 /* up right */
346 CHECK_NEIGHBOR( 1, -1 );
347
348 /* down left */
349 CHECK_NEIGHBOR( -1, 1 );
350
351 /* down right */
352 CHECK_NEIGHBOR( 1, 1 );
353
354 if ( num_neighbors != 8 )
355 is_edge = 1;
356
357 Done:
358 return is_edge;
359 }
360
361#undef CHECK_NEIGHBOR
362
363
364 /**************************************************************************
365 *
366 * @Function:
367 * compute_edge_distance
368 *
369 * @Description:
370 * Approximate the outline and compute the distance from `current`
371 * to the approximated outline.
372 *
373 * @Input:
374 * current ::
375 * Array of Euclidean distances. `current` must point to the position
376 * for which the distance is to be calculated. We treat this array as
377 * a two-dimensional array mapped to a one-dimensional array.
378 *
379 * x ::
380 * The x coordinate of the `current` parameter in the array.
381 *
382 * y ::
383 * The y coordinate of the `current` parameter in the array.
384 *
385 * w ::
386 * The width of the distances array.
387 *
388 * r ::
389 * Number of rows in the distances array.
390 *
391 * @Return:
392 * A vector pointing to the approximate edge distance.
393 *
394 * @Note:
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.
398 *
399 */
400 static FT_16D16_Vec
401 compute_edge_distance( ED* current,
402 FT_Int x,
403 FT_Int y,
404 FT_Int w,
405 FT_Int r )
406 {
407 /*
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.
411 *
412 * The algorithm is as follows.
413 *
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
417 * the pixel.
418 *
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.
425 *
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
431 * gradient.
432 *
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
435 * Distance Field.
436 *
437 * References:
438 *
439 * - Anti-Aliased Euclidean Distance Transform:
440 * http://weber.itn.liu.se/~stegu/aadist/edtaa_preprint.pdf
441 * - Sobel Operator:
442 * https://en.wikipedia.org/wiki/Sobel_operator
443 */
444
445 FT_16D16_Vec g = { 0, 0 };
446 FT_16D16 dist, current_alpha;
447 FT_16D16 a1, temp;
448 FT_16D16 gx, gy;
449 FT_16D16 alphas[9];
450
451
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 )
456 return g;
457
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;
468
469 current_alpha = alphas[4];
470
471 /* Compute the gradient using the Sobel operator. */
472 /* In this case we use the following 3x3 filters: */
473 /* */
474 /* For x: | -1 0 -1 | */
475 /* | -root(2) 0 root(2) | */
476 /* | -1 0 1 | */
477 /* */
478 /* For y: | -1 -root(2) -1 | */
479 /* | 0 0 0 | */
480 /* | 1 root(2) 1 | */
481 /* */
482 /* [Note]: 92681 is root(2) in 16.16 format. */
483 g.x = -alphas[0] -
484 FT_MulFix( alphas[3], 92681 ) -
485 alphas[6] +
486 alphas[2] +
487 FT_MulFix( alphas[5], 92681 ) +
488 alphas[8];
489
490 g.y = -alphas[0] -
491 FT_MulFix( alphas[1], 92681 ) -
492 alphas[2] +
493 alphas[6] +
494 FT_MulFix( alphas[7], 92681 ) +
495 alphas[8];
496
497 FT_Vector_NormLen( &g );
498
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. */
503
504 if ( g.x == 0 || g.y == 0 )
505 dist = ONE / 2 - alphas[4];
506 else
507 {
508 gx = g.x;
509 gy = g.y;
510
511 gx = FT_ABS( gx );
512 gy = FT_ABS( gy );
513
514 if ( gx < gy )
515 {
516 temp = gx;
517 gx = gy;
518 gy = temp;
519 }
520
521 a1 = FT_DivFix( gy, gx ) / 2;
522
523 if ( current_alpha < a1 )
524 dist = ( gx + gy ) / 2 -
525 square_root( 2 * FT_MulFix( gx,
526 FT_MulFix( gy,
527 current_alpha ) ) );
528
529 else if ( current_alpha < ( ONE - a1 ) )
530 dist = FT_MulFix( ONE / 2 - current_alpha, gx );
531
532 else
533 dist = -( gx + gy ) / 2 +
534 square_root( 2 * FT_MulFix( gx,
535 FT_MulFix( gy,
536 ONE - current_alpha ) ) );
537 }
538
539 g.x = FT_MulFix( g.x, dist );
540 g.y = FT_MulFix( g.y, dist );
541
542 return g;
543 }
544
545
546 /**************************************************************************
547 *
548 * @Function:
549 * bsdf_approximate_edge
550 *
551 * @Description:
552 * Loops over all the pixels and call `compute_edge_distance` only for
553 * edge pixels. This makes the process a lot faster since
554 * `compute_edge_distance` uses functions such as `FT_Vector_NormLen',
555 * which are quite slow.
556 *
557 * @InOut:
558 * worker ::
559 * Contains the distance map as well as all the relevant parameters
560 * required by the function.
561 *
562 * @Return:
563 * FreeType error, 0 means success.
564 *
565 * @Note:
566 * The function directly manipulates `worker->distance_map`.
567 *
568 */
569 static FT_Error
570 bsdf_approximate_edge( BSDF_Worker* worker )
571 {
572 FT_Error error = FT_Err_Ok;
573 FT_Int i, j;
574 FT_Int index;
575 ED* ed;
576
577
578 if ( !worker || !worker->distance_map )
579 {
580 error = FT_THROW( Invalid_Argument );
581 goto Exit;
582 }
583
584 ed = worker->distance_map;
585
586 for ( j = 0; j < worker->rows; j++ )
587 {
588 for ( i = 0; i < worker->width; i++ )
589 {
590 index = j * worker->width + i;
591
592 if ( bsdf_is_edge( worker->distance_map + index,
593 i, j,
594 worker->width,
595 worker->rows ) )
596 {
597 /* approximate the edge distance for edge pixels */
598 ed[index].prox = compute_edge_distance( ed + index,
599 i, j,
600 worker->width,
601 worker->rows );
602 ed[index].dist = VECTOR_LENGTH_16D16( ed[index].prox );
603 }
604 else
605 {
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;
610 }
611 }
612 }
613
614 Exit:
615 return error;
616 }
617
618
619 /**************************************************************************
620 *
621 * @Function:
622 * bsdf_init_distance_map
623 *
624 * @Description:
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`.
629 *
630 * @Input:
631 * source ::
632 * Source bitmap to copy the data from.
633 *
634 * @Output:
635 * worker ::
636 * Target distance map to copy the data to.
637 *
638 * @Return:
639 * FreeType error, 0 means success.
640 *
641 */
642 static FT_Error
643 bsdf_init_distance_map( const FT_Bitmap* source,
644 BSDF_Worker* worker )
645 {
646 FT_Error error = FT_Err_Ok;
647
648 FT_Int x_diff, y_diff;
649 FT_Int t_i, t_j, s_i, s_j;
650 FT_Byte* s;
651 ED* t;
652
653
654 /* again check the parameters (probably unnecessary) */
655 if ( !source || !worker )
656 {
657 error = FT_THROW( Invalid_Argument );
658 goto Exit;
659 }
660
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 )
667 {
668 error = FT_THROW( Invalid_Argument );
669 goto Exit;
670 }
671
672 /* check pixel mode */
673 if ( source->pixel_mode == FT_PIXEL_MODE_NONE )
674 {
675 FT_ERROR(( "bsdf_copy_source_to_target:"
676 " Invalid pixel mode of source bitmap" ));
677 error = FT_THROW( Invalid_Argument );
678 goto Exit;
679 }
680
681#ifdef FT_DEBUG_LEVEL_TRACE
682 if ( source->pixel_mode == FT_PIXEL_MODE_MONO )
683 {
684 FT_TRACE0(( "bsdf_copy_source_to_target:"
685 " The `bsdf' renderer can convert monochrome\n" ));
686 FT_TRACE0(( " "
687 " bitmaps to SDF but the results are not perfect\n" ));
688 FT_TRACE0(( " "
689 " because there is no way to approximate actual\n" ));
690 FT_TRACE0(( " "
691 " outlines from monochrome bitmaps. Consider\n" ));
692 FT_TRACE0(( " "
693 " using an anti-aliased bitmap instead.\n" ));
694 }
695#endif
696
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;
701
702 x_diff /= 2;
703 y_diff /= 2;
704
705 t = (ED*)worker->distance_map;
706 s = source->buffer;
707
708 /* For now we only support pixel mode `FT_PIXEL_MODE_MONO` */
709 /* and `FT_PIXEL_MODE_GRAY`. More will be added later. */
710 /* */
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. */
715
716 switch ( source->pixel_mode )
717 {
718 case FT_PIXEL_MODE_MONO:
719 {
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;
724
725
726 for ( t_j = 0; t_j < t_rows; t_j++ )
727 {
728 for ( t_i = 0; t_i < t_width; t_i++ )
729 {
730 FT_Int t_index = t_j * t_width + t_i;
731 FT_Int s_index;
732 FT_Int div, mod;
733 FT_Byte pixel, byte;
734
735
736 t[t_index] = zero_ed;
737
738 s_i = t_i - x_diff;
739 s_j = t_j - y_diff;
740
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 )
745 continue;
746
747 if ( worker->params.flip_y )
748 s_index = ( s_rows - s_j - 1 ) * source->pitch;
749 else
750 s_index = s_j * source->pitch;
751
752 div = s_index + s_i / 8;
753 mod = 7 - s_i % 8;
754
755 pixel = s[div];
756 byte = (FT_Byte)( 1 << mod );
757
758 t[t_index].alpha = pixel & byte ? 255 : 0;
759 }
760 }
761 }
762 break;
763
764 case FT_PIXEL_MODE_GRAY:
765 {
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;
770
771
772 /* loop over all pixels and assign pixel values from source */
773 for ( t_j = 0; t_j < t_rows; t_j++ )
774 {
775 for ( t_i = 0; t_i < t_width; t_i++ )
776 {
777 FT_Int t_index = t_j * t_width + t_i;
778 FT_Int s_index;
779
780
781 t[t_index] = zero_ed;
782
783 s_i = t_i - x_diff;
784 s_j = t_j - y_diff;
785
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 )
790 continue;
791
792 if ( worker->params.flip_y )
793 s_index = ( s_rows - s_j - 1 ) * s_width + s_i;
794 else
795 s_index = s_j * s_width + s_i;
796
797 /* simply copy the alpha values */
798 t[t_index].alpha = s[s_index];
799 }
800 }
801 }
802 break;
803
804 default:
805 FT_ERROR(( "bsdf_copy_source_to_target:"
806 " unsopported pixel mode of source bitmap\n" ));
807
808 error = FT_THROW( Unimplemented_Feature );
809 break;
810 }
811
812 Exit:
813 return error;
814 }
815
816
817 /**************************************************************************
818 *
819 * @Function:
820 * compare_neighbor
821 *
822 * @Description:
823 * Compare neighbor pixel (which is defined by the offset) and update
824 * `current` distance if the new distance is shorter than the original.
825 *
826 * @Input:
827 * x_offset ::
828 * X offset of the neighbor to be checked. The offset is relative to
829 * the `current`.
830 *
831 * y_offset ::
832 * Y offset of the neighbor to be checked. The offset is relative to
833 * the `current`.
834 *
835 * width ::
836 * Width of the `current` array.
837 *
838 * @InOut:
839 * current ::
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.
843 *
844 */
845 static void
846 compare_neighbor( ED* current,
847 FT_Int x_offset,
848 FT_Int y_offset,
849 FT_Int width )
850 {
851#if USE_SQUARED_DISTANCES
852 FT_16D16 edge_threshold = ONE / 4;
853#else
854 FT_16D16 edge_threshold = ONE / 2;
855#endif
856 ED* to_check;
857 FT_16D16 dist;
858 FT_16D16_Vec dist_vec;
859
860
861 /*
862 * Skip neighbor comparison if the distance is less than or equal to 0.5.
863 * When using squared distances, compare to 0.25 (= 0.5^2) instead.
864 */
865 if ( current->dist <= edge_threshold )
866 return;
867
868 to_check = current + ( y_offset * width ) + x_offset;
869
870 /*
871 * While checking for the nearest point we first approximate the
872 * distance of `current` by adding the deviation (which is sqrt(2) at
873 * most). Only if the new value is less than the current value we
874 * calculate the actual distances using `FT_Vector_Length`. This last
875 * step can be omitted by using squared distances.
876 */
877
878 /*
879 * Approximate the distance. We subtract 1 to avoid precision errors,
880 * which could happen because the two directions can be opposite.
881 */
882 dist = to_check->dist - ONE;
883
884 if ( dist < current->dist )
885 {
886 dist_vec = to_check->prox;
887
888 dist_vec.x += x_offset * ONE;
889 dist_vec.y += y_offset * ONE;
890 dist = VECTOR_LENGTH_16D16( dist_vec );
891
892 if ( dist < current->dist )
893 {
894 current->dist = dist;
895 current->prox = dist_vec;
896 }
897 }
898 }
899
900
901 /**************************************************************************
902 *
903 * @Function:
904 * first_pass
905 *
906 * @Description:
907 * First pass of the 8SED algorithm. Loop over the bitmap from top to
908 * bottom and scan each row left to right, updating the distances in
909 * `worker->distance_map`.
910 *
911 * @InOut:
912 * worker::
913 * Contains all the relevant parameters.
914 *
915 */
916 static void
917 first_pass( BSDF_Worker* worker )
918 {
919 FT_Int i, j; /* iterators */
920 FT_Int w, r; /* width, rows */
921 ED* dm; /* distance map */
922
923
924 dm = worker->distance_map;
925 w = worker->width;
926 r = worker->rows;
927
928 /* Start scanning from top to bottom and sweep each */
929 /* row back and forth comparing the distances of the */
930 /* neighborhood. Leave the first row as it has no top */
931 /* neighbor; it will be covered in the second scan of */
932 /* the image (from bottom to top). */
933 for ( j = 1; j < r; j++ )
934 {
935 FT_Int index;
936 ED* current;
937
938
939 /* Forward pass of rows (left -> right). Leave the first */
940 /* column, which gets covered in the backward pass. */
941 for ( i = 1; i < w - 1; i++ )
942 {
943 index = j * w + i;
944 current = dm + index;
945
946 /* left-up */
947 compare_neighbor( current, -1, -1, w );
948 /* up */
949 compare_neighbor( current, 0, -1, w );
950 /* up-right */
951 compare_neighbor( current, 1, -1, w );
952 /* left */
953 compare_neighbor( current, -1, 0, w );
954 }
955
956 /* Backward pass of rows (right -> left). Leave the last */
957 /* column, which was already covered in the forward pass. */
958 for ( i = w - 2; i >= 0; i-- )
959 {
960 index = j * w + i;
961 current = dm + index;
962
963 /* right */
964 compare_neighbor( current, 1, 0, w );
965 }
966 }
967 }
968
969
970 /**************************************************************************
971 *
972 * @Function:
973 * second_pass
974 *
975 * @Description:
976 * Second pass of the 8SED algorithm. Loop over the bitmap from bottom
977 * to top and scan each row left to right, updating the distances in
978 * `worker->distance_map`.
979 *
980 * @InOut:
981 * worker::
982 * Contains all the relevant parameters.
983 *
984 */
985 static void
986 second_pass( BSDF_Worker* worker )
987 {
988 FT_Int i, j; /* iterators */
989 FT_Int w, r; /* width, rows */
990 ED* dm; /* distance map */
991
992
993 dm = worker->distance_map;
994 w = worker->width;
995 r = worker->rows;
996
997 /* Start scanning from bottom to top and sweep each */
998 /* row back and forth comparing the distances of the */
999 /* neighborhood. Leave the last row as it has no down */
1000 /* neighbor; it is already covered in the first scan */
1001 /* of the image (from top to bottom). */
1002 for ( j = r - 2; j >= 0; j-- )
1003 {
1004 FT_Int index;
1005 ED* current;
1006
1007
1008 /* Forward pass of rows (left -> right). Leave the first */
1009 /* column, which gets covered in the backward pass. */
1010 for ( i = 1; i < w - 1; i++ )
1011 {
1012 index = j * w + i;
1013 current = dm + index;
1014
1015 /* left-up */
1016 compare_neighbor( current, -1, 1, w );
1017 /* up */
1018 compare_neighbor( current, 0, 1, w );
1019 /* up-right */
1020 compare_neighbor( current, 1, 1, w );
1021 /* left */
1022 compare_neighbor( current, -1, 0, w );
1023 }
1024
1025 /* Backward pass of rows (right -> left). Leave the last */
1026 /* column, which was already covered in the forward pass. */
1027 for ( i = w - 2; i >= 0; i-- )
1028 {
1029 index = j * w + i;
1030 current = dm + index;
1031
1032 /* right */
1033 compare_neighbor( current, 1, 0, w );
1034 }
1035 }
1036 }
1037
1038
1039 /**************************************************************************
1040 *
1041 * @Function:
1042 * edt8
1043 *
1044 * @Description:
1045 * Compute the distance map of the a bitmap. Execute both first and
1046 * second pass of the 8SED algorithm.
1047 *
1048 * @InOut:
1049 * worker::
1050 * Contains all the relevant parameters.
1051 *
1052 * @Return:
1053 * FreeType error, 0 means success.
1054 *
1055 */
1056 static FT_Error
1057 edt8( BSDF_Worker* worker )
1058 {
1059 FT_Error error = FT_Err_Ok;
1060
1061
1062 if ( !worker || !worker->distance_map )
1063 {
1064 error = FT_THROW( Invalid_Argument );
1065 goto Exit;
1066 }
1067
1068 /* first scan of the image */
1069 first_pass( worker );
1070
1071 /* second scan of the image */
1072 second_pass( worker );
1073
1074 Exit:
1075 return error;
1076 }
1077
1078
1079 /**************************************************************************
1080 *
1081 * @Function:
1082 * finalize_sdf
1083 *
1084 * @Description:
1085 * Copy the SDF data from `worker->distance_map` to the `target` bitmap.
1086 * Also transform the data to output format, (which is 6.10 fixed-point
1087 * format at the moment).
1088 *
1089 * @Input:
1090 * worker ::
1091 * Contains source distance map and other SDF data.
1092 *
1093 * @Output:
1094 * target ::
1095 * Target bitmap to which the SDF data is copied to.
1096 *
1097 * @Return:
1098 * FreeType error, 0 means success.
1099 *
1100 */
1101 static FT_Error
1102 finalize_sdf( BSDF_Worker* worker,
1103 const FT_Bitmap* target )
1104 {
1105 FT_Error error = FT_Err_Ok;
1106
1107 FT_Int w, r;
1108 FT_Int i, j;
1109
1110 FT_SDFFormat* t_buffer;
1111 FT_16D16 sp_sq, spread;
1112
1113
1114 if ( !worker || !target )
1115 {
1116 error = FT_THROW( Invalid_Argument );
1117 goto Exit;
1118 }
1119
1120 w = (int)target->width;
1121 r = (int)target->rows;
1122 t_buffer = (FT_SDFFormat*)target->buffer;
1123
1124 if ( w != worker->width ||
1125 r != worker->rows )
1126 {
1127 error = FT_THROW( Invalid_Argument );
1128 goto Exit;
1129 }
1130
1131 spread = (FT_16D16)FT_INT_16D16( worker->params.spread );
1132
1133#if USE_SQUARED_DISTANCES
1134 sp_sq = (FT_16D16)FT_INT_16D16( worker->params.spread *
1135 worker->params.spread );
1136#else
1137 sp_sq = (FT_16D16)FT_INT_16D16( worker->params.spread );
1138#endif
1139
1140 for ( j = 0; j < r; j++ )
1141 {
1142 for ( i = 0; i < w; i++ )
1143 {
1144 FT_Int index;
1145 FT_16D16 dist;
1146 FT_SDFFormat final_dist;
1147 FT_Char sign;
1148
1149
1150 index = j * w + i;
1151 dist = worker->distance_map[index].dist;
1152
1153 if ( dist < 0 || dist > sp_sq )
1154 dist = sp_sq;
1155
1156#if USE_SQUARED_DISTANCES
1157 dist = square_root( dist );
1158#endif
1159
1160 /* We assume that if the pixel is inside a contour */
1161 /* its coverage value must be > 127. */
1162 sign = worker->distance_map[index].alpha < 127 ? -1 : 1;
1163
1164 /* flip the sign according to the property */
1165 if ( worker->params.flip_sign )
1166 sign = -sign;
1167
1168 /* concatenate from 16.16 to appropriate format */
1169 final_dist = map_fixed_to_sdf( dist * sign, spread );
1170
1171 t_buffer[index] = final_dist;
1172 }
1173 }
1174
1175 Exit:
1176 return error;
1177 }
1178
1179
1180 /**************************************************************************
1181 *
1182 * interface functions
1183 *
1184 */
1185
1186 /* called when adding a new module through @FT_Add_Module */
1187 static FT_Error
1188 bsdf_raster_new( void* memory_, /* FT_Memory */
1189 FT_Raster* araster_ ) /* BSDF_PRaster* */
1190 {
1191 FT_Memory memory = (FT_Memory)memory_;
1192 BSDF_PRaster* araster = (BSDF_PRaster*)araster_;
1193
1194 FT_Error error;
1195 BSDF_PRaster raster = NULL;
1196
1197
1198 if ( !FT_NEW( raster ) )
1199 raster->memory = memory;
1200
1201 *araster = raster;
1202
1203 return error;
1204 }
1205
1206
1207 /* unused */
1208 static void
1209 bsdf_raster_reset( FT_Raster raster,
1210 unsigned char* pool_base,
1211 unsigned long pool_size )
1212 {
1213 FT_UNUSED( raster );
1214 FT_UNUSED( pool_base );
1215 FT_UNUSED( pool_size );
1216 }
1217
1218
1219 /* unused */
1220 static FT_Error
1221 bsdf_raster_set_mode( FT_Raster raster,
1222 unsigned long mode,
1223 void* args )
1224 {
1225 FT_UNUSED( raster );
1226 FT_UNUSED( mode );
1227 FT_UNUSED( args );
1228
1229 return FT_Err_Ok;
1230 }
1231
1232
1233 /* called while rendering through @FT_Render_Glyph */
1234 static FT_Error
1235 bsdf_raster_render( FT_Raster raster,
1236 const FT_Raster_Params* params )
1237 {
1238 FT_Error error = FT_Err_Ok;
1239 FT_Memory memory = NULL;
1240
1241 const FT_Bitmap* source = NULL;
1242 const FT_Bitmap* target = NULL;
1243
1244 BSDF_TRaster* bsdf_raster = (BSDF_TRaster*)raster;
1245 BSDF_Worker worker;
1246
1247 const SDF_Raster_Params* sdf_params = (const SDF_Raster_Params*)params;
1248
1249
1250 worker.distance_map = NULL;
1251
1252 /* check for valid parameters */
1253 if ( !raster || !params )
1254 {
1255 error = FT_THROW( Invalid_Argument );
1256 goto Exit;
1257 }
1258
1259 /* check whether the flag is set */
1260 if ( sdf_params->root.flags != FT_RASTER_FLAG_SDF )
1261 {
1262 error = FT_THROW( Raster_Corrupted );
1263 goto Exit;
1264 }
1265
1266 source = (const FT_Bitmap*)sdf_params->root.source;
1267 target = (const FT_Bitmap*)sdf_params->root.target;
1268
1269 /* check source and target bitmap */
1270 if ( !source || !target )
1271 {
1272 error = FT_THROW( Invalid_Argument );
1273 goto Exit;
1274 }
1275
1276 memory = bsdf_raster->memory;
1277 if ( !memory )
1278 {
1279 FT_TRACE0(( "bsdf_raster_render: Raster not set up properly,\n" ));
1280 FT_TRACE0(( " unable to find memory handle.\n" ));
1281
1282 error = FT_THROW( Invalid_Handle );
1283 goto Exit;
1284 }
1285
1286 /* check whether spread is set properly */
1287 if ( sdf_params->spread > MAX_SPREAD ||
1288 sdf_params->spread < MIN_SPREAD )
1289 {
1290 FT_TRACE0(( "bsdf_raster_render:"
1291 " The `spread' field of `SDF_Raster_Params'\n" ));
1292 FT_TRACE0(( " "
1293 " is invalid; the value of this field must be\n" ));
1294 FT_TRACE0(( " "
1295 " within [%d, %d].\n",
1296 MIN_SPREAD, MAX_SPREAD ));
1297 FT_TRACE0(( " "
1298 " Also, you must pass `SDF_Raster_Params'\n" ));
1299 FT_TRACE0(( " "
1300 " instead of the default `FT_Raster_Params'\n" ));
1301 FT_TRACE0(( " "
1302 " while calling this function and set the fields\n" ));
1303 FT_TRACE0(( " "
1304 " accordingly.\n" ));
1305
1306 error = FT_THROW( Invalid_Argument );
1307 goto Exit;
1308 }
1309
1310 /* set up the worker */
1311
1312 /* allocate the distance map */
1313 if ( FT_QALLOC_MULT( worker.distance_map, target->rows,
1314 target->width * sizeof ( *worker.distance_map ) ) )
1315 goto Exit;
1316
1317 worker.width = (int)target->width;
1318 worker.rows = (int)target->rows;
1319 worker.params = *sdf_params;
1320
1321 FT_CALL( bsdf_init_distance_map( source, &worker ) );
1322 FT_CALL( bsdf_approximate_edge( &worker ) );
1323 FT_CALL( edt8( &worker ) );
1324 FT_CALL( finalize_sdf( &worker, target ) );
1325
1326 FT_TRACE0(( "bsdf_raster_render: Total memory used = %ld\n",
1327 worker.width * worker.rows *
1328 (long)sizeof ( *worker.distance_map ) ));
1329
1330 Exit:
1331 if ( worker.distance_map )
1332 FT_FREE( worker.distance_map );
1333
1334 return error;
1335 }
1336
1337
1338 /* called while deleting `FT_Library` only if the module is added */
1339 static void
1340 bsdf_raster_done( FT_Raster raster )
1341 {
1342 FT_Memory memory = (FT_Memory)((BSDF_TRaster*)raster)->memory;
1343
1344
1345 FT_FREE( raster );
1346 }
1347
1348
1349 FT_DEFINE_RASTER_FUNCS(
1350 ft_bitmap_sdf_raster,
1351
1352 FT_GLYPH_FORMAT_BITMAP,
1353
1354 (FT_Raster_New_Func) bsdf_raster_new, /* raster_new */
1355 (FT_Raster_Reset_Func) bsdf_raster_reset, /* raster_reset */
1356 (FT_Raster_Set_Mode_Func)bsdf_raster_set_mode, /* raster_set_mode */
1357 (FT_Raster_Render_Func) bsdf_raster_render, /* raster_render */
1358 (FT_Raster_Done_Func) bsdf_raster_done /* raster_done */
1359 )
1360
1361
1362/* END */
Definition ftbsdf.c:195