1/****************************************************************************
2 *
3 * ftsdfrend.c
4 *
5 * Signed Distance Field renderer interface (body).
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/ftdebug.h>
22#include <freetype/internal/ftobjs.h>
23#include <freetype/internal/services/svprop.h>
24#include <freetype/ftoutln.h>
25#include <freetype/ftbitmap.h>
26#include "ftsdfrend.h"
27#include "ftsdf.h"
28
29#include "ftsdferrs.h"
30
31
32 /**************************************************************************
33 *
34 * The macro FT_COMPONENT is used in trace mode. It is an implicit
35 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
36 * messages during execution.
37 */
38#undef FT_COMPONENT
39#define FT_COMPONENT sdf
40
41
42 /**************************************************************************
43 *
44 * macros and default property values
45 *
46 */
47#define SDF_RENDERER( rend ) ( (SDF_Renderer)rend )
48
49
50 /**************************************************************************
51 *
52 * for setting properties
53 *
54 */
55
56 /* property setter function */
57 static FT_Error
58 sdf_property_set( FT_Module module,
59 const char* property_name,
60 const void* value,
61 FT_Bool value_is_string )
62 {
63 FT_Error error = FT_Err_Ok;
64 SDF_Renderer render = SDF_RENDERER( FT_RENDERER( module ) );
65
66 FT_UNUSED( value_is_string );
67
68
69 if ( ft_strcmp( property_name, "spread" ) == 0 )
70 {
71 FT_Int val = *(const FT_Int*)value;
72
73
74 if ( val > MAX_SPREAD || val < MIN_SPREAD )
75 {
76 FT_TRACE0(( "[sdf] sdf_property_set:"
77 " the `spread' property can have a value\n" ));
78 FT_TRACE0(( " "
79 " within range [%d, %d] (value provided: %d)\n",
80 MIN_SPREAD, MAX_SPREAD, val ));
81
82 error = FT_THROW( Invalid_Argument );
83 goto Exit;
84 }
85
86 render->spread = (FT_UInt)val;
87 FT_TRACE7(( "[sdf] sdf_property_set:"
88 " updated property `spread' to %d\n", val ));
89 }
90
91 else if ( ft_strcmp( property_name, "flip_sign" ) == 0 )
92 {
93 FT_Int val = *(const FT_Int*)value;
94
95
96 render->flip_sign = val ? 1 : 0;
97 FT_TRACE7(( "[sdf] sdf_property_set:"
98 " updated property `flip_sign' to %d\n", val ));
99 }
100
101 else if ( ft_strcmp( property_name, "flip_y" ) == 0 )
102 {
103 FT_Int val = *(const FT_Int*)value;
104
105
106 render->flip_y = val ? 1 : 0;
107 FT_TRACE7(( "[sdf] sdf_property_set:"
108 " updated property `flip_y' to %d\n", val ));
109 }
110
111 else if ( ft_strcmp( property_name, "overlaps" ) == 0 )
112 {
113 FT_Bool val = *(const FT_Bool*)value;
114
115
116 render->overlaps = val;
117 FT_TRACE7(( "[sdf] sdf_property_set:"
118 " updated property `overlaps' to %d\n", val ));
119 }
120
121 else
122 {
123 FT_TRACE0(( "[sdf] sdf_property_set:"
124 " missing property `%s'\n", property_name ));
125 error = FT_THROW( Missing_Property );
126 }
127
128 Exit:
129 return error;
130 }
131
132
133 /* property getter function */
134 static FT_Error
135 sdf_property_get( FT_Module module,
136 const char* property_name,
137 void* value )
138 {
139 FT_Error error = FT_Err_Ok;
140 SDF_Renderer render = SDF_RENDERER( FT_RENDERER( module ) );
141
142
143 if ( ft_strcmp( property_name, "spread" ) == 0 )
144 {
145 FT_UInt* val = (FT_UInt*)value;
146
147
148 *val = render->spread;
149 }
150
151 else if ( ft_strcmp( property_name, "flip_sign" ) == 0 )
152 {
153 FT_Int* val = (FT_Int*)value;
154
155
156 *val = render->flip_sign;
157 }
158
159 else if ( ft_strcmp( property_name, "flip_y" ) == 0 )
160 {
161 FT_Int* val = (FT_Int*)value;
162
163
164 *val = render->flip_y;
165 }
166
167 else if ( ft_strcmp( property_name, "overlaps" ) == 0 )
168 {
169 FT_Int* val = (FT_Int*)value;
170
171
172 *val = render->overlaps;
173 }
174
175 else
176 {
177 FT_TRACE0(( "[sdf] sdf_property_get:"
178 " missing property `%s'\n", property_name ));
179 error = FT_THROW( Missing_Property );
180 }
181
182 return error;
183 }
184
185
186 FT_DEFINE_SERVICE_PROPERTIESREC(
187 sdf_service_properties,
188
189 (FT_Properties_SetFunc)sdf_property_set, /* set_property */
190 (FT_Properties_GetFunc)sdf_property_get ) /* get_property */
191
192
193 FT_DEFINE_SERVICEDESCREC1(
194 sdf_services,
195
196 FT_SERVICE_ID_PROPERTIES, &sdf_service_properties )
197
198
199 static FT_Module_Interface
200 ft_sdf_requester( FT_Module module,
201 const char* module_interface )
202 {
203 FT_UNUSED( module );
204
205 return ft_service_list_lookup( sdf_services, module_interface );
206 }
207
208
209 /*************************************************************************/
210 /*************************************************************************/
211 /** **/
212 /** OUTLINE TO SDF CONVERTER **/
213 /** **/
214 /*************************************************************************/
215 /*************************************************************************/
216
217 /**************************************************************************
218 *
219 * interface functions
220 *
221 */
222
223 static FT_Error
224 ft_sdf_init( FT_Module module ) /* SDF_Renderer */
225 {
226 SDF_Renderer sdf_render = SDF_RENDERER( module );
227
228
229 sdf_render->spread = DEFAULT_SPREAD;
230 sdf_render->flip_sign = 0;
231 sdf_render->flip_y = 0;
232 sdf_render->overlaps = 0;
233
234 return FT_Err_Ok;
235 }
236
237
238 static void
239 ft_sdf_done( FT_Module module )
240 {
241 FT_UNUSED( module );
242 }
243
244
245 /* generate signed distance field from a glyph's slot image */
246 static FT_Error
247 ft_sdf_render( FT_Renderer module,
248 FT_GlyphSlot slot,
249 FT_Render_Mode mode,
250 const FT_Vector* origin )
251 {
252 FT_Error error = FT_Err_Ok;
253 FT_Outline* outline = &slot->outline;
254 FT_Bitmap* bitmap = &slot->bitmap;
255 FT_Memory memory = NULL;
256 FT_Renderer render = NULL;
257
258 FT_Pos x_shift = 0;
259 FT_Pos y_shift = 0;
260
261 FT_Pos x_pad = 0;
262 FT_Pos y_pad = 0;
263
264 SDF_Raster_Params params;
265 SDF_Renderer sdf_module = SDF_RENDERER( module );
266
267
268 render = &sdf_module->root;
269 memory = render->root.memory;
270
271 /* check whether slot format is correct before rendering */
272 if ( slot->format != render->glyph_format )
273 {
274 error = FT_THROW( Invalid_Glyph_Format );
275 goto Exit;
276 }
277
278 /* check whether render mode is correct */
279 if ( mode != FT_RENDER_MODE_SDF )
280 {
281 error = FT_THROW( Cannot_Render_Glyph );
282 goto Exit;
283 }
284
285 /* deallocate the previously allocated bitmap */
286 if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
287 {
288 FT_FREE( bitmap->buffer );
289 slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
290 }
291
292 /* preset the bitmap using the glyph's outline; */
293 /* the sdf bitmap is similar to an anti-aliased bitmap */
294 /* with a slightly bigger size and different pixel mode */
295 if ( ft_glyphslot_preset_bitmap( slot, FT_RENDER_MODE_NORMAL, origin ) )
296 {
297 error = FT_THROW( Raster_Overflow );
298 goto Exit;
299 }
300
301 /* nothing to render */
302 if ( !bitmap->rows || !bitmap->pitch )
303 goto Exit;
304
305 /* the padding will simply be equal to the `spread' */
306 x_pad = sdf_module->spread;
307 y_pad = sdf_module->spread;
308
309 /* apply the padding; will be in all the directions */
310 bitmap->rows += y_pad * 2;
311 bitmap->width += x_pad * 2;
312
313 /* ignore the pitch, pixel mode and set custom */
314 bitmap->pixel_mode = FT_PIXEL_MODE_GRAY;
315 bitmap->pitch = (int)( bitmap->width );
316 bitmap->num_grays = 255;
317
318 /* allocate new buffer */
319 if ( FT_ALLOC_MULT( bitmap->buffer, bitmap->rows, bitmap->pitch ) )
320 goto Exit;
321
322 slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
323
324 slot->bitmap_top += y_pad;
325 slot->bitmap_left -= x_pad;
326
327 x_shift = 64 * -slot->bitmap_left;
328 y_shift = 64 * -slot->bitmap_top;
329 y_shift += 64 * (FT_Int)bitmap->rows;
330
331 if ( origin )
332 {
333 x_shift += origin->x;
334 y_shift += origin->y;
335 }
336
337 /* translate outline to render it into the bitmap */
338 if ( x_shift || y_shift )
339 FT_Outline_Translate( outline, x_shift, y_shift );
340
341 /* set up parameters */
342 params.root.target = bitmap;
343 params.root.source = outline;
344 params.root.flags = FT_RASTER_FLAG_SDF;
345 params.spread = sdf_module->spread;
346 params.flip_sign = sdf_module->flip_sign;
347 params.flip_y = sdf_module->flip_y;
348 params.overlaps = sdf_module->overlaps;
349
350 /* render the outline */
351 error = render->raster_render( render->raster,
352 (const FT_Raster_Params*)&params );
353
354 /* transform the outline back to the original state */
355 if ( x_shift || y_shift )
356 FT_Outline_Translate( outline, -x_shift, -y_shift );
357
358 Exit:
359 if ( !error )
360 {
361 /* the glyph is successfully rendered to a bitmap */
362 slot->format = FT_GLYPH_FORMAT_BITMAP;
363 }
364 else if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
365 {
366 FT_FREE( bitmap->buffer );
367 slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
368 }
369
370 return error;
371 }
372
373
374 /* transform the glyph using matrix and/or delta */
375 static FT_Error
376 ft_sdf_transform( FT_Renderer render,
377 FT_GlyphSlot slot,
378 const FT_Matrix* matrix,
379 const FT_Vector* delta )
380 {
381 FT_Error error = FT_Err_Ok;
382
383
384 if ( slot->format != render->glyph_format )
385 {
386 error = FT_THROW( Invalid_Argument );
387 goto Exit;
388 }
389
390 if ( matrix )
391 FT_Outline_Transform( &slot->outline, matrix );
392
393 if ( delta )
394 FT_Outline_Translate( &slot->outline, delta->x, delta->y );
395
396 Exit:
397 return error;
398 }
399
400
401 /* return the control box of a glyph's outline */
402 static void
403 ft_sdf_get_cbox( FT_Renderer render,
404 FT_GlyphSlot slot,
405 FT_BBox* cbox )
406 {
407 FT_ZERO( cbox );
408
409 if ( slot->format == render->glyph_format )
410 FT_Outline_Get_CBox( &slot->outline, cbox );
411 }
412
413
414 /* set render specific modes or attributes */
415 static FT_Error
416 ft_sdf_set_mode( FT_Renderer render,
417 FT_ULong mode_tag,
418 FT_Pointer data )
419 {
420 /* pass it to the rasterizer */
421 return render->clazz->raster_class->raster_set_mode( render->raster,
422 mode_tag,
423 data );
424 }
425
426
427 FT_DEFINE_RENDERER(
428 ft_sdf_renderer_class,
429
430 FT_MODULE_RENDERER,
431 sizeof ( SDF_Renderer_Module ),
432
433 "sdf",
434 0x10000L,
435 0x20000L,
436
437 NULL,
438
439 (FT_Module_Constructor)ft_sdf_init,
440 (FT_Module_Destructor) ft_sdf_done,
441 (FT_Module_Requester) ft_sdf_requester,
442
443 FT_GLYPH_FORMAT_OUTLINE,
444
445 (FT_Renderer_RenderFunc) ft_sdf_render, /* render_glyph */
446 (FT_Renderer_TransformFunc)ft_sdf_transform, /* transform_glyph */
447 (FT_Renderer_GetCBoxFunc) ft_sdf_get_cbox, /* get_glyph_cbox */
448 (FT_Renderer_SetModeFunc) ft_sdf_set_mode, /* set_mode */
449
450 (FT_Raster_Funcs*)&ft_sdf_raster /* raster_class */
451 )
452
453
454 /*************************************************************************/
455 /*************************************************************************/
456 /** **/
457 /** BITMAP TO SDF CONVERTER **/
458 /** **/
459 /*************************************************************************/
460 /*************************************************************************/
461
462 /* generate signed distance field from glyph's bitmap */
463 static FT_Error
464 ft_bsdf_render( FT_Renderer module,
465 FT_GlyphSlot slot,
466 FT_Render_Mode mode,
467 const FT_Vector* origin )
468 {
469 FT_Error error = FT_Err_Ok;
470 FT_Memory memory = NULL;
471
472 FT_Bitmap* bitmap = &slot->bitmap;
473 FT_Renderer render = NULL;
474 FT_Bitmap target;
475
476 FT_Pos x_pad = 0;
477 FT_Pos y_pad = 0;
478
479 SDF_Raster_Params params;
480 SDF_Renderer sdf_module = SDF_RENDERER( module );
481
482
483 /* initialize the bitmap in case any error occurs */
484 FT_Bitmap_Init( &target );
485
486 render = &sdf_module->root;
487 memory = render->root.memory;
488
489 /* check whether slot format is correct before rendering */
490 if ( slot->format != render->glyph_format )
491 {
492 error = FT_THROW( Invalid_Glyph_Format );
493 goto Exit;
494 }
495
496 /* check whether render mode is correct */
497 if ( mode != FT_RENDER_MODE_SDF )
498 {
499 error = FT_THROW( Cannot_Render_Glyph );
500 goto Exit;
501 }
502
503 if ( origin )
504 {
505 FT_ERROR(( "ft_bsdf_render: can't translate the bitmap\n" ));
506
507 error = FT_THROW( Unimplemented_Feature );
508 goto Exit;
509 }
510
511 /* nothing to render */
512 if ( !bitmap->rows || !bitmap->pitch )
513 goto Exit;
514
515 /* Do not generate SDF if the bitmap is not owned by the */
516 /* glyph: it might be that the source buffer is already freed. */
517 if ( !( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) )
518 {
519 FT_ERROR(( "ft_bsdf_render: can't generate SDF from"
520 " unowned source bitmap\n" ));
521
522 error = FT_THROW( Invalid_Argument );
523 goto Exit;
524 }
525
526 FT_Bitmap_New( &target );
527
528 /* padding will simply be equal to `spread` */
529 x_pad = sdf_module->spread;
530 y_pad = sdf_module->spread;
531
532 /* apply padding, which extends to all directions */
533 target.rows = bitmap->rows + y_pad * 2;
534 target.width = bitmap->width + x_pad * 2;
535
536 /* set up the target bitmap */
537 target.pixel_mode = FT_PIXEL_MODE_GRAY;
538 target.pitch = (int)( target.width );
539 target.num_grays = 255;
540
541 if ( FT_ALLOC_MULT( target.buffer, target.rows, target.pitch ) )
542 goto Exit;
543
544 /* set up parameters */
545 params.root.target = &target;
546 params.root.source = bitmap;
547 params.root.flags = FT_RASTER_FLAG_SDF;
548 params.spread = sdf_module->spread;
549 params.flip_sign = sdf_module->flip_sign;
550 params.flip_y = sdf_module->flip_y;
551
552 error = render->raster_render( render->raster,
553 (const FT_Raster_Params*)&params );
554
555 Exit:
556 if ( !error )
557 {
558 /* the glyph is successfully converted to a SDF */
559 if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
560 FT_FREE( bitmap->buffer );
561
562 slot->bitmap = target;
563 slot->bitmap_top += y_pad;
564 slot->bitmap_left -= x_pad;
565
566 if ( target.buffer )
567 slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
568 }
569 else if ( target.buffer )
570 FT_FREE( target.buffer );
571
572 return error;
573 }
574
575
576 FT_DEFINE_RENDERER(
577 ft_bitmap_sdf_renderer_class,
578
579 FT_MODULE_RENDERER,
580 sizeof ( SDF_Renderer_Module ),
581
582 "bsdf",
583 0x10000L,
584 0x20000L,
585
586 NULL,
587
588 (FT_Module_Constructor)ft_sdf_init,
589 (FT_Module_Destructor) ft_sdf_done,
590 (FT_Module_Requester) ft_sdf_requester,
591
592 FT_GLYPH_FORMAT_BITMAP,
593
594 (FT_Renderer_RenderFunc) ft_bsdf_render, /* render_glyph */
595 (FT_Renderer_TransformFunc)ft_sdf_transform, /* transform_glyph */
596 (FT_Renderer_GetCBoxFunc) ft_sdf_get_cbox, /* get_glyph_cbox */
597 (FT_Renderer_SetModeFunc) ft_sdf_set_mode, /* set_mode */
598
599 (FT_Raster_Funcs*)&ft_bitmap_sdf_raster /* raster_class */
600 )
601
602
603/* END */
604