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*)¶ms ); |
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 = ⌖ |
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*)¶ms ); |
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 | |