1/****************************************************************************
2 *
3 * ftbsdf.c
4 *
5 * Signed Distance Field support for bitmap fonts (body only).
6 *
7 * Copyright (C) 2020-2023 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
21#include <freetype/internal/ftobjs.h>
22#include <freetype/internal/ftdebug.h>
23#include <freetype/internal/ftmemory.h>
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 caculated. 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 maked 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 ED* to_check;
852 FT_16D16 dist;
853 FT_16D16_Vec dist_vec;
854
855
856 to_check = current + ( y_offset * width ) + x_offset;
857
858 /*
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.
864 */
865
866 /*
867 * Approximate the distance. We subtract 1 to avoid precision errors,
868 * which could happen because the two directions can be opposite.
869 */
870 dist = to_check->dist - ONE;
871
872 if ( dist < current->dist )
873 {
874 dist_vec = to_check->prox;
875
876 dist_vec.x += x_offset * ONE;
877 dist_vec.y += y_offset * ONE;
878 dist = VECTOR_LENGTH_16D16( dist_vec );
879
880 if ( dist < current->dist )
881 {
882 current->dist = dist;
883 current->prox = dist_vec;
884 }
885 }
886 }
887
888
889 /**************************************************************************
890 *
891 * @Function:
892 * first_pass
893 *
894 * @Description:
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`.
898 *
899 * @InOut:
900 * worker::
901 * Contains all the relevant parameters.
902 *
903 */
904 static void
905 first_pass( BSDF_Worker* worker )
906 {
907 FT_Int i, j; /* iterators */
908 FT_Int w, r; /* width, rows */
909 ED* dm; /* distance map */
910
911
912 dm = worker->distance_map;
913 w = worker->width;
914 r = worker->rows;
915
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++ )
922 {
923 FT_Int index;
924 ED* current;
925
926
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++ )
930 {
931 index = j * w + i;
932 current = dm + index;
933
934 /* left-up */
935 compare_neighbor( current, -1, -1, w );
936 /* up */
937 compare_neighbor( current, 0, -1, w );
938 /* up-right */
939 compare_neighbor( current, 1, -1, w );
940 /* left */
941 compare_neighbor( current, -1, 0, w );
942 }
943
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-- )
947 {
948 index = j * w + i;
949 current = dm + index;
950
951 /* right */
952 compare_neighbor( current, 1, 0, w );
953 }
954 }
955 }
956
957
958 /**************************************************************************
959 *
960 * @Function:
961 * second_pass
962 *
963 * @Description:
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`.
967 *
968 * @InOut:
969 * worker::
970 * Contains all the relevant parameters.
971 *
972 */
973 static void
974 second_pass( BSDF_Worker* worker )
975 {
976 FT_Int i, j; /* iterators */
977 FT_Int w, r; /* width, rows */
978 ED* dm; /* distance map */
979
980
981 dm = worker->distance_map;
982 w = worker->width;
983 r = worker->rows;
984
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-- )
991 {
992 FT_Int index;
993 ED* current;
994
995
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++ )
999 {
1000 index = j * w + i;
1001 current = dm + index;
1002
1003 /* left-up */
1004 compare_neighbor( current, -1, 1, w );
1005 /* up */
1006 compare_neighbor( current, 0, 1, w );
1007 /* up-right */
1008 compare_neighbor( current, 1, 1, w );
1009 /* left */
1010 compare_neighbor( current, -1, 0, w );
1011 }
1012
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-- )
1016 {
1017 index = j * w + i;
1018 current = dm + index;
1019
1020 /* right */
1021 compare_neighbor( current, 1, 0, w );
1022 }
1023 }
1024 }
1025
1026
1027 /**************************************************************************
1028 *
1029 * @Function:
1030 * edt8
1031 *
1032 * @Description:
1033 * Compute the distance map of the a bitmap. Execute both first and
1034 * second pass of the 8SED algorithm.
1035 *
1036 * @InOut:
1037 * worker::
1038 * Contains all the relevant parameters.
1039 *
1040 * @Return:
1041 * FreeType error, 0 means success.
1042 *
1043 */
1044 static FT_Error
1045 edt8( BSDF_Worker* worker )
1046 {
1047 FT_Error error = FT_Err_Ok;
1048
1049
1050 if ( !worker || !worker->distance_map )
1051 {
1052 error = FT_THROW( Invalid_Argument );
1053 goto Exit;
1054 }
1055
1056 /* first scan of the image */
1057 first_pass( worker );
1058
1059 /* second scan of the image */
1060 second_pass( worker );
1061
1062 Exit:
1063 return error;
1064 }
1065
1066
1067 /**************************************************************************
1068 *
1069 * @Function:
1070 * finalize_sdf
1071 *
1072 * @Description:
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).
1076 *
1077 * @Input:
1078 * worker ::
1079 * Contains source distance map and other SDF data.
1080 *
1081 * @Output:
1082 * target ::
1083 * Target bitmap to which the SDF data is copied to.
1084 *
1085 * @Return:
1086 * FreeType error, 0 means success.
1087 *
1088 */
1089 static FT_Error
1090 finalize_sdf( BSDF_Worker* worker,
1091 const FT_Bitmap* target )
1092 {
1093 FT_Error error = FT_Err_Ok;
1094
1095 FT_Int w, r;
1096 FT_Int i, j;
1097
1098 FT_SDFFormat* t_buffer;
1099 FT_16D16 sp_sq, spread;
1100
1101
1102 if ( !worker || !target )
1103 {
1104 error = FT_THROW( Invalid_Argument );
1105 goto Exit;
1106 }
1107
1108 w = (int)target->width;
1109 r = (int)target->rows;
1110 t_buffer = (FT_SDFFormat*)target->buffer;
1111
1112 if ( w != worker->width ||
1113 r != worker->rows )
1114 {
1115 error = FT_THROW( Invalid_Argument );
1116 goto Exit;
1117 }
1118
1119 spread = (FT_16D16)FT_INT_16D16( worker->params.spread );
1120
1121#if USE_SQUARED_DISTANCES
1122 sp_sq = (FT_16D16)FT_INT_16D16( worker->params.spread *
1123 worker->params.spread );
1124#else
1125 sp_sq = (FT_16D16)FT_INT_16D16( worker->params.spread );
1126#endif
1127
1128 for ( j = 0; j < r; j++ )
1129 {
1130 for ( i = 0; i < w; i++ )
1131 {
1132 FT_Int index;
1133 FT_16D16 dist;
1134 FT_SDFFormat final_dist;
1135 FT_Char sign;
1136
1137
1138 index = j * w + i;
1139 dist = worker->distance_map[index].dist;
1140
1141 if ( dist < 0 || dist > sp_sq )
1142 dist = sp_sq;
1143
1144#if USE_SQUARED_DISTANCES
1145 dist = square_root( dist );
1146#endif
1147
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;
1151
1152 /* flip the sign according to the property */
1153 if ( worker->params.flip_sign )
1154 sign = -sign;
1155
1156 /* concatenate from 16.16 to appropriate format */
1157 final_dist = map_fixed_to_sdf( dist * sign, spread );
1158
1159 t_buffer[index] = final_dist;
1160 }
1161 }
1162
1163 Exit:
1164 return error;
1165 }
1166
1167
1168 /**************************************************************************
1169 *
1170 * interface functions
1171 *
1172 */
1173
1174 /* called when adding a new module through @FT_Add_Module */
1175 static FT_Error
1176 bsdf_raster_new( void* memory_, /* FT_Memory */
1177 FT_Raster* araster_ ) /* BSDF_PRaster* */
1178 {
1179 FT_Memory memory = (FT_Memory)memory_;
1180 BSDF_PRaster* araster = (BSDF_PRaster*)araster_;
1181
1182 FT_Error error;
1183 BSDF_PRaster raster = NULL;
1184
1185
1186 if ( !FT_NEW( raster ) )
1187 raster->memory = memory;
1188
1189 *araster = raster;
1190
1191 return error;
1192 }
1193
1194
1195 /* unused */
1196 static void
1197 bsdf_raster_reset( FT_Raster raster,
1198 unsigned char* pool_base,
1199 unsigned long pool_size )
1200 {
1201 FT_UNUSED( raster );
1202 FT_UNUSED( pool_base );
1203 FT_UNUSED( pool_size );
1204 }
1205
1206
1207 /* unused */
1208 static FT_Error
1209 bsdf_raster_set_mode( FT_Raster raster,
1210 unsigned long mode,
1211 void* args )
1212 {
1213 FT_UNUSED( raster );
1214 FT_UNUSED( mode );
1215 FT_UNUSED( args );
1216
1217 return FT_Err_Ok;
1218 }
1219
1220
1221 /* called while rendering through @FT_Render_Glyph */
1222 static FT_Error
1223 bsdf_raster_render( FT_Raster raster,
1224 const FT_Raster_Params* params )
1225 {
1226 FT_Error error = FT_Err_Ok;
1227 FT_Memory memory = NULL;
1228
1229 const FT_Bitmap* source = NULL;
1230 const FT_Bitmap* target = NULL;
1231
1232 BSDF_TRaster* bsdf_raster = (BSDF_TRaster*)raster;
1233 BSDF_Worker worker;
1234
1235 const SDF_Raster_Params* sdf_params = (const SDF_Raster_Params*)params;
1236
1237
1238 worker.distance_map = NULL;
1239
1240 /* check for valid parameters */
1241 if ( !raster || !params )
1242 {
1243 error = FT_THROW( Invalid_Argument );
1244 goto Exit;
1245 }
1246
1247 /* check whether the flag is set */
1248 if ( sdf_params->root.flags != FT_RASTER_FLAG_SDF )
1249 {
1250 error = FT_THROW( Raster_Corrupted );
1251 goto Exit;
1252 }
1253
1254 source = (const FT_Bitmap*)sdf_params->root.source;
1255 target = (const FT_Bitmap*)sdf_params->root.target;
1256
1257 /* check source and target bitmap */
1258 if ( !source || !target )
1259 {
1260 error = FT_THROW( Invalid_Argument );
1261 goto Exit;
1262 }
1263
1264 memory = bsdf_raster->memory;
1265 if ( !memory )
1266 {
1267 FT_TRACE0(( "bsdf_raster_render: Raster not set up properly,\n" ));
1268 FT_TRACE0(( " unable to find memory handle.\n" ));
1269
1270 error = FT_THROW( Invalid_Handle );
1271 goto Exit;
1272 }
1273
1274 /* check whether spread is set properly */
1275 if ( sdf_params->spread > MAX_SPREAD ||
1276 sdf_params->spread < MIN_SPREAD )
1277 {
1278 FT_TRACE0(( "bsdf_raster_render:"
1279 " The `spread' field of `SDF_Raster_Params'\n" ));
1280 FT_TRACE0(( " "
1281 " is invalid; the value of this field must be\n" ));
1282 FT_TRACE0(( " "
1283 " within [%d, %d].\n",
1284 MIN_SPREAD, MAX_SPREAD ));
1285 FT_TRACE0(( " "
1286 " Also, you must pass `SDF_Raster_Params'\n" ));
1287 FT_TRACE0(( " "
1288 " instead of the default `FT_Raster_Params'\n" ));
1289 FT_TRACE0(( " "
1290 " while calling this function and set the fields\n" ));
1291 FT_TRACE0(( " "
1292 " accordingly.\n" ));
1293
1294 error = FT_THROW( Invalid_Argument );
1295 goto Exit;
1296 }
1297
1298 /* set up the worker */
1299
1300 /* allocate the distance map */
1301 if ( FT_QALLOC_MULT( worker.distance_map, target->rows,
1302 target->width * sizeof ( *worker.distance_map ) ) )
1303 goto Exit;
1304
1305 worker.width = (int)target->width;
1306 worker.rows = (int)target->rows;
1307 worker.params = *sdf_params;
1308
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 ) );
1313
1314 FT_TRACE0(( "bsdf_raster_render: Total memory used = %ld\n",
1315 worker.width * worker.rows *
1316 (long)sizeof ( *worker.distance_map ) ));
1317
1318 Exit:
1319 if ( worker.distance_map )
1320 FT_FREE( worker.distance_map );
1321
1322 return error;
1323 }
1324
1325
1326 /* called while deleting `FT_Library` only if the module is added */
1327 static void
1328 bsdf_raster_done( FT_Raster raster )
1329 {
1330 FT_Memory memory = (FT_Memory)((BSDF_TRaster*)raster)->memory;
1331
1332
1333 FT_FREE( raster );
1334 }
1335
1336
1337 FT_DEFINE_RASTER_FUNCS(
1338 ft_bitmap_sdf_raster,
1339
1340 FT_GLYPH_FORMAT_BITMAP,
1341
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 */
1347 )
1348
1349
1350/* END */
1351