1/****************************************************************************
2 *
3 * ftsmooth.c
4 *
5 * Anti-aliasing renderer interface (body).
6 *
7 * Copyright (C) 2000-2023 by
8 * David Turner, Robert Wilhelm, and Werner Lemberg.
9 *
10 * This file is part of the FreeType project, and may only be used,
11 * modified, and distributed under the terms of the FreeType project
12 * license, LICENSE.TXT. By continuing to use, modify, or distribute
13 * this file you indicate that you have read the license and
14 * understand and accept it fully.
15 *
16 */
17
18
19#include <freetype/internal/ftdebug.h>
20#include <freetype/internal/ftobjs.h>
21#include <freetype/ftoutln.h>
22#include "ftsmooth.h"
23#include "ftgrays.h"
24
25#include "ftsmerrs.h"
26
27
28 /* sets render-specific mode */
29 static FT_Error
30 ft_smooth_set_mode( FT_Renderer render,
31 FT_ULong mode_tag,
32 FT_Pointer data )
33 {
34 /* we simply pass it to the raster */
35 return render->clazz->raster_class->raster_set_mode( render->raster,
36 mode_tag,
37 data );
38 }
39
40 /* transform a given glyph image */
41 static FT_Error
42 ft_smooth_transform( FT_Renderer render,
43 FT_GlyphSlot slot,
44 const FT_Matrix* matrix,
45 const FT_Vector* delta )
46 {
47 FT_Error error = FT_Err_Ok;
48
49
50 if ( slot->format != render->glyph_format )
51 {
52 error = FT_THROW( Invalid_Argument );
53 goto Exit;
54 }
55
56 if ( matrix )
57 FT_Outline_Transform( &slot->outline, matrix );
58
59 if ( delta )
60 FT_Outline_Translate( &slot->outline, delta->x, delta->y );
61
62 Exit:
63 return error;
64 }
65
66
67 /* return the glyph's control box */
68 static void
69 ft_smooth_get_cbox( FT_Renderer render,
70 FT_GlyphSlot slot,
71 FT_BBox* cbox )
72 {
73 FT_ZERO( cbox );
74
75 if ( slot->format == render->glyph_format )
76 FT_Outline_Get_CBox( &slot->outline, cbox );
77 }
78
79 typedef struct TOrigin_
80 {
81 unsigned char* origin; /* pixmap origin at the bottom-left */
82 int pitch; /* pitch to go down one row */
83
84 } TOrigin;
85
86#ifndef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
87
88 /* initialize renderer -- init its raster */
89 static FT_Error
90 ft_smooth_init( FT_Module module ) /* FT_Renderer */
91 {
92 FT_Renderer render = (FT_Renderer)module;
93
94 FT_Vector* sub = render->root.library->lcd_geometry;
95
96
97 /* set up default subpixel geometry for striped RGB panels. */
98 sub[0].x = -21;
99 sub[0].y = 0;
100 sub[1].x = 0;
101 sub[1].y = 0;
102 sub[2].x = 21;
103 sub[2].y = 0;
104
105 render->clazz->raster_class->raster_reset( render->raster, NULL, 0 );
106
107 return 0;
108 }
109
110
111 /* This function writes every third byte in direct rendering mode */
112 static void
113 ft_smooth_lcd_spans( int y,
114 int count,
115 const FT_Span* spans,
116 void* target_ ) /* TOrigin* */
117 {
118 TOrigin* target = (TOrigin*)target_;
119
120 unsigned char* dst_line = target->origin - y * target->pitch;
121 unsigned char* dst;
122 unsigned short w;
123
124
125 for ( ; count--; spans++ )
126 for ( dst = dst_line + spans->x * 3, w = spans->len; w--; dst += 3 )
127 *dst = spans->coverage;
128 }
129
130
131 static FT_Error
132 ft_smooth_raster_lcd( FT_Renderer render,
133 FT_Outline* outline,
134 FT_Bitmap* bitmap )
135 {
136 FT_Error error = FT_Err_Ok;
137 FT_Vector* sub = render->root.library->lcd_geometry;
138 FT_Pos x, y;
139
140 FT_Raster_Params params;
141 TOrigin target;
142
143
144 /* Render 3 separate coverage bitmaps, shifting the outline. */
145 /* Set up direct rendering to record them on each third byte. */
146 params.source = outline;
147 params.flags = FT_RASTER_FLAG_AA | FT_RASTER_FLAG_DIRECT;
148 params.gray_spans = ft_smooth_lcd_spans;
149 params.user = &target;
150
151 params.clip_box.xMin = 0;
152 params.clip_box.yMin = 0;
153 params.clip_box.xMax = bitmap->width;
154 params.clip_box.yMax = bitmap->rows;
155
156 if ( bitmap->pitch < 0 )
157 target.origin = bitmap->buffer;
158 else
159 target.origin = bitmap->buffer
160 + ( bitmap->rows - 1 ) * (unsigned int)bitmap->pitch;
161
162 target.pitch = bitmap->pitch;
163
164 FT_Outline_Translate( outline,
165 -sub[0].x,
166 -sub[0].y );
167 error = render->raster_render( render->raster, &params );
168 x = sub[0].x;
169 y = sub[0].y;
170 if ( error )
171 goto Exit;
172
173 target.origin++;
174 FT_Outline_Translate( outline,
175 sub[0].x - sub[1].x,
176 sub[0].y - sub[1].y );
177 error = render->raster_render( render->raster, &params );
178 x = sub[1].x;
179 y = sub[1].y;
180 if ( error )
181 goto Exit;
182
183 target.origin++;
184 FT_Outline_Translate( outline,
185 sub[1].x - sub[2].x,
186 sub[1].y - sub[2].y );
187 error = render->raster_render( render->raster, &params );
188 x = sub[2].x;
189 y = sub[2].y;
190
191 Exit:
192 FT_Outline_Translate( outline, x, y );
193
194 return error;
195 }
196
197
198 static FT_Error
199 ft_smooth_raster_lcdv( FT_Renderer render,
200 FT_Outline* outline,
201 FT_Bitmap* bitmap )
202 {
203 FT_Error error = FT_Err_Ok;
204 int pitch = bitmap->pitch;
205 FT_Vector* sub = render->root.library->lcd_geometry;
206 FT_Pos x, y;
207
208 FT_Raster_Params params;
209
210
211 params.target = bitmap;
212 params.source = outline;
213 params.flags = FT_RASTER_FLAG_AA;
214
215 /* Render 3 separate coverage bitmaps, shifting the outline. */
216 /* Notice that the subpixel geometry vectors are rotated. */
217 /* Triple the pitch to render on each third row. */
218 bitmap->pitch *= 3;
219 bitmap->rows /= 3;
220
221 FT_Outline_Translate( outline,
222 -sub[0].y,
223 sub[0].x );
224 error = render->raster_render( render->raster, &params );
225 x = sub[0].y;
226 y = -sub[0].x;
227 if ( error )
228 goto Exit;
229
230 bitmap->buffer += pitch;
231 FT_Outline_Translate( outline,
232 sub[0].y - sub[1].y,
233 sub[1].x - sub[0].x );
234 error = render->raster_render( render->raster, &params );
235 x = sub[1].y;
236 y = -sub[1].x;
237 bitmap->buffer -= pitch;
238 if ( error )
239 goto Exit;
240
241 bitmap->buffer += 2 * pitch;
242 FT_Outline_Translate( outline,
243 sub[1].y - sub[2].y,
244 sub[2].x - sub[1].x );
245 error = render->raster_render( render->raster, &params );
246 x = sub[2].y;
247 y = -sub[2].x;
248 bitmap->buffer -= 2 * pitch;
249
250 Exit:
251 FT_Outline_Translate( outline, x, y );
252
253 bitmap->pitch /= 3;
254 bitmap->rows *= 3;
255
256 return error;
257 }
258
259#else /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
260
261 /* initialize renderer -- init its raster */
262 static FT_Error
263 ft_smooth_init( FT_Module module ) /* FT_Renderer */
264 {
265 FT_Renderer render = (FT_Renderer)module;
266
267
268 /* set up default LCD filtering */
269 FT_Library_SetLcdFilter( render->root.library, FT_LCD_FILTER_DEFAULT );
270
271 render->clazz->raster_class->raster_reset( render->raster, NULL, 0 );
272
273 return 0;
274 }
275
276
277 static FT_Error
278 ft_smooth_raster_lcd( FT_Renderer render,
279 FT_Outline* outline,
280 FT_Bitmap* bitmap )
281 {
282 FT_Error error = FT_Err_Ok;
283 FT_Vector* points = outline->points;
284 FT_Vector* points_end = FT_OFFSET( points, outline->n_points );
285 FT_Vector* vec;
286
287 FT_Raster_Params params;
288
289
290 params.target = bitmap;
291 params.source = outline;
292 params.flags = FT_RASTER_FLAG_AA;
293
294 /* implode outline */
295 for ( vec = points; vec < points_end; vec++ )
296 vec->x *= 3;
297
298 /* render outline into the bitmap */
299 error = render->raster_render( render->raster, &params );
300
301 /* deflate outline */
302 for ( vec = points; vec < points_end; vec++ )
303 vec->x /= 3;
304
305 return error;
306 }
307
308
309 static FT_Error
310 ft_smooth_raster_lcdv( FT_Renderer render,
311 FT_Outline* outline,
312 FT_Bitmap* bitmap )
313 {
314 FT_Error error = FT_Err_Ok;
315 FT_Vector* points = outline->points;
316 FT_Vector* points_end = FT_OFFSET( points, outline->n_points );
317 FT_Vector* vec;
318
319 FT_Raster_Params params;
320
321
322 params.target = bitmap;
323 params.source = outline;
324 params.flags = FT_RASTER_FLAG_AA;
325
326 /* implode outline */
327 for ( vec = points; vec < points_end; vec++ )
328 vec->y *= 3;
329
330 /* render outline into the bitmap */
331 error = render->raster_render( render->raster, &params );
332
333 /* deflate outline */
334 for ( vec = points; vec < points_end; vec++ )
335 vec->y /= 3;
336
337 return error;
338 }
339
340#endif /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
341
342/* Oversampling scale to be used in rendering overlaps */
343#define SCALE ( 1 << 2 )
344
345 /* This function averages inflated spans in direct rendering mode */
346 static void
347 ft_smooth_overlap_spans( int y,
348 int count,
349 const FT_Span* spans,
350 void* target_ )
351 {
352 TOrigin* target = (TOrigin*)target_;
353
354
355 unsigned char* dst = target->origin - ( y / SCALE ) * target->pitch;
356 unsigned short x;
357 unsigned int cover, sum;
358
359
360 /* When accumulating the oversampled spans we need to assure that */
361 /* fully covered pixels are equal to 255 and do not overflow. */
362 /* It is important that the SCALE is a power of 2, each subpixel */
363 /* cover can also reach a power of 2 after rounding, and the total */
364 /* is clamped to 255 when it adds up to 256. */
365 for ( ; count--; spans++ )
366 {
367 cover = ( spans->coverage + SCALE * SCALE / 2 ) / ( SCALE * SCALE );
368 for ( x = 0; x < spans->len; x++ )
369 {
370 sum = dst[( spans->x + x ) / SCALE] + cover;
371 dst[( spans->x + x ) / SCALE] = (unsigned char)( sum - ( sum >> 8 ) );
372 }
373 }
374 }
375
376
377 static FT_Error
378 ft_smooth_raster_overlap( FT_Renderer render,
379 FT_Outline* outline,
380 FT_Bitmap* bitmap )
381 {
382 FT_Error error = FT_Err_Ok;
383 FT_Vector* points = outline->points;
384 FT_Vector* points_end = FT_OFFSET( points, outline->n_points );
385 FT_Vector* vec;
386
387 FT_Raster_Params params;
388 TOrigin target;
389
390
391 /* Reject outlines that are too wide for 16-bit FT_Span. */
392 /* Other limits are applied upstream with the same error code. */
393 if ( bitmap->width * SCALE > 0x7FFF )
394 return FT_THROW( Raster_Overflow );
395
396 /* Set up direct rendering to average oversampled spans. */
397 params.source = outline;
398 params.flags = FT_RASTER_FLAG_AA | FT_RASTER_FLAG_DIRECT;
399 params.gray_spans = ft_smooth_overlap_spans;
400 params.user = &target;
401
402 params.clip_box.xMin = 0;
403 params.clip_box.yMin = 0;
404 params.clip_box.xMax = bitmap->width * SCALE;
405 params.clip_box.yMax = bitmap->rows * SCALE;
406
407 if ( bitmap->pitch < 0 )
408 target.origin = bitmap->buffer;
409 else
410 target.origin = bitmap->buffer
411 + ( bitmap->rows - 1 ) * (unsigned int)bitmap->pitch;
412
413 target.pitch = bitmap->pitch;
414
415 /* inflate outline */
416 for ( vec = points; vec < points_end; vec++ )
417 {
418 vec->x *= SCALE;
419 vec->y *= SCALE;
420 }
421
422 /* render outline into the bitmap */
423 error = render->raster_render( render->raster, &params );
424
425 /* deflate outline */
426 for ( vec = points; vec < points_end; vec++ )
427 {
428 vec->x /= SCALE;
429 vec->y /= SCALE;
430 }
431
432 return error;
433 }
434
435#undef SCALE
436
437 static FT_Error
438 ft_smooth_render( FT_Renderer render,
439 FT_GlyphSlot slot,
440 FT_Render_Mode mode,
441 const FT_Vector* origin )
442 {
443 FT_Error error = FT_Err_Ok;
444 FT_Outline* outline = &slot->outline;
445 FT_Bitmap* bitmap = &slot->bitmap;
446 FT_Memory memory = render->root.memory;
447 FT_Pos x_shift = 0;
448 FT_Pos y_shift = 0;
449
450
451 /* check glyph image format */
452 if ( slot->format != render->glyph_format )
453 {
454 error = FT_THROW( Invalid_Argument );
455 goto Exit;
456 }
457
458 /* check mode */
459 if ( mode != FT_RENDER_MODE_NORMAL &&
460 mode != FT_RENDER_MODE_LIGHT &&
461 mode != FT_RENDER_MODE_LCD &&
462 mode != FT_RENDER_MODE_LCD_V )
463 {
464 error = FT_THROW( Cannot_Render_Glyph );
465 goto Exit;
466 }
467
468 /* release old bitmap buffer */
469 if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
470 {
471 FT_FREE( bitmap->buffer );
472 slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
473 }
474
475 if ( ft_glyphslot_preset_bitmap( slot, mode, origin ) )
476 {
477 error = FT_THROW( Raster_Overflow );
478 goto Exit;
479 }
480
481 if ( !bitmap->rows || !bitmap->pitch )
482 goto Exit;
483
484 /* allocate new one */
485 if ( FT_ALLOC_MULT( bitmap->buffer, bitmap->rows, bitmap->pitch ) )
486 goto Exit;
487
488 slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
489
490 x_shift = 64 * -slot->bitmap_left;
491 y_shift = 64 * -slot->bitmap_top;
492 if ( bitmap->pixel_mode == FT_PIXEL_MODE_LCD_V )
493 y_shift += 64 * (FT_Int)bitmap->rows / 3;
494 else
495 y_shift += 64 * (FT_Int)bitmap->rows;
496
497 if ( origin )
498 {
499 x_shift += origin->x;
500 y_shift += origin->y;
501 }
502
503 /* translate outline to render it into the bitmap */
504 if ( x_shift || y_shift )
505 FT_Outline_Translate( outline, x_shift, y_shift );
506
507 if ( mode == FT_RENDER_MODE_NORMAL ||
508 mode == FT_RENDER_MODE_LIGHT )
509 {
510 if ( outline->flags & FT_OUTLINE_OVERLAP )
511 error = ft_smooth_raster_overlap( render, outline, bitmap );
512 else
513 {
514 FT_Raster_Params params;
515
516
517 params.target = bitmap;
518 params.source = outline;
519 params.flags = FT_RASTER_FLAG_AA;
520
521 error = render->raster_render( render->raster, &params );
522 }
523 }
524 else
525 {
526 if ( mode == FT_RENDER_MODE_LCD )
527 error = ft_smooth_raster_lcd ( render, outline, bitmap );
528 else if ( mode == FT_RENDER_MODE_LCD_V )
529 error = ft_smooth_raster_lcdv( render, outline, bitmap );
530
531#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
532
533 /* finally apply filtering */
534 {
535 FT_Byte* lcd_weights;
536 FT_Bitmap_LcdFilterFunc lcd_filter_func;
537
538
539 /* Per-face LCD filtering takes priority if set up. */
540 if ( slot->face && slot->face->internal->lcd_filter_func )
541 {
542 lcd_weights = slot->face->internal->lcd_weights;
543 lcd_filter_func = slot->face->internal->lcd_filter_func;
544 }
545 else
546 {
547 lcd_weights = slot->library->lcd_weights;
548 lcd_filter_func = slot->library->lcd_filter_func;
549 }
550
551 if ( lcd_filter_func )
552 lcd_filter_func( bitmap, lcd_weights );
553 }
554
555#endif /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
556
557 }
558
559 Exit:
560 if ( !error )
561 {
562 /* everything is fine; the glyph is now officially a bitmap */
563 slot->format = FT_GLYPH_FORMAT_BITMAP;
564 }
565 else if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
566 {
567 FT_FREE( bitmap->buffer );
568 slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
569 }
570
571 if ( x_shift || y_shift )
572 FT_Outline_Translate( outline, -x_shift, -y_shift );
573
574 return error;
575 }
576
577
578 FT_DEFINE_RENDERER(
579 ft_smooth_renderer_class,
580
581 FT_MODULE_RENDERER,
582 sizeof ( FT_RendererRec ),
583
584 "smooth",
585 0x10000L,
586 0x20000L,
587
588 NULL, /* module specific interface */
589
590 (FT_Module_Constructor)ft_smooth_init, /* module_init */
591 (FT_Module_Destructor) NULL, /* module_done */
592 (FT_Module_Requester) NULL, /* get_interface */
593
594 FT_GLYPH_FORMAT_OUTLINE,
595
596 (FT_Renderer_RenderFunc) ft_smooth_render, /* render_glyph */
597 (FT_Renderer_TransformFunc)ft_smooth_transform, /* transform_glyph */
598 (FT_Renderer_GetCBoxFunc) ft_smooth_get_cbox, /* get_glyph_cbox */
599 (FT_Renderer_SetModeFunc) ft_smooth_set_mode, /* set_mode */
600
601 (FT_Raster_Funcs*)&ft_grays_raster /* raster_class */
602 )
603
604
605/* END */
606