1 | /***************************************************************************/ |
2 | /* */ |
3 | /* ftsmooth.c */ |
4 | /* */ |
5 | /* Anti-aliasing renderer interface (body). */ |
6 | /* */ |
7 | /* Copyright 2000-2018 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 <ft2build.h> |
20 | #include FT_INTERNAL_DEBUG_H |
21 | #include FT_INTERNAL_OBJECTS_H |
22 | #include FT_OUTLINE_H |
23 | #include "ftsmooth.h" |
24 | #include "ftgrays.h" |
25 | #include "ftspic.h" |
26 | |
27 | #include "ftsmerrs.h" |
28 | |
29 | |
30 | /* initialize renderer -- init its raster */ |
31 | static FT_Error |
32 | ft_smooth_init( FT_Renderer render ) |
33 | { |
34 | render->clazz->raster_class->raster_reset( render->raster, NULL, 0 ); |
35 | |
36 | return 0; |
37 | } |
38 | |
39 | |
40 | /* sets render-specific mode */ |
41 | static FT_Error |
42 | ft_smooth_set_mode( FT_Renderer render, |
43 | FT_ULong mode_tag, |
44 | FT_Pointer data ) |
45 | { |
46 | /* we simply pass it to the raster */ |
47 | return render->clazz->raster_class->raster_set_mode( render->raster, |
48 | mode_tag, |
49 | data ); |
50 | } |
51 | |
52 | /* transform a given glyph image */ |
53 | static FT_Error |
54 | ft_smooth_transform( FT_Renderer render, |
55 | FT_GlyphSlot slot, |
56 | const FT_Matrix* matrix, |
57 | const FT_Vector* delta ) |
58 | { |
59 | FT_Error error = FT_Err_Ok; |
60 | |
61 | |
62 | if ( slot->format != render->glyph_format ) |
63 | { |
64 | error = FT_THROW( Invalid_Argument ); |
65 | goto Exit; |
66 | } |
67 | |
68 | if ( matrix ) |
69 | FT_Outline_Transform( &slot->outline, matrix ); |
70 | |
71 | if ( delta ) |
72 | FT_Outline_Translate( &slot->outline, delta->x, delta->y ); |
73 | |
74 | Exit: |
75 | return error; |
76 | } |
77 | |
78 | |
79 | /* return the glyph's control box */ |
80 | static void |
81 | ft_smooth_get_cbox( FT_Renderer render, |
82 | FT_GlyphSlot slot, |
83 | FT_BBox* cbox ) |
84 | { |
85 | FT_ZERO( cbox ); |
86 | |
87 | if ( slot->format == render->glyph_format ) |
88 | FT_Outline_Get_CBox( &slot->outline, cbox ); |
89 | } |
90 | |
91 | |
92 | /* convert a slot's glyph image into a bitmap */ |
93 | static FT_Error |
94 | ft_smooth_render_generic( FT_Renderer render, |
95 | FT_GlyphSlot slot, |
96 | FT_Render_Mode mode, |
97 | const FT_Vector* origin, |
98 | FT_Render_Mode required_mode ) |
99 | { |
100 | FT_Error error = FT_Err_Ok; |
101 | FT_Outline* outline = &slot->outline; |
102 | FT_Bitmap* bitmap = &slot->bitmap; |
103 | FT_Memory memory = render->root.memory; |
104 | FT_Pos x_shift = 0; |
105 | FT_Pos y_shift = 0; |
106 | FT_Int hmul = ( mode == FT_RENDER_MODE_LCD ); |
107 | FT_Int vmul = ( mode == FT_RENDER_MODE_LCD_V ); |
108 | |
109 | FT_Raster_Params params; |
110 | |
111 | |
112 | /* check glyph image format */ |
113 | if ( slot->format != render->glyph_format ) |
114 | { |
115 | error = FT_THROW( Invalid_Argument ); |
116 | goto Exit; |
117 | } |
118 | |
119 | /* check mode */ |
120 | if ( mode != required_mode ) |
121 | { |
122 | error = FT_THROW( Cannot_Render_Glyph ); |
123 | goto Exit; |
124 | } |
125 | |
126 | /* release old bitmap buffer */ |
127 | if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) |
128 | { |
129 | FT_FREE( bitmap->buffer ); |
130 | slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP; |
131 | } |
132 | |
133 | ft_glyphslot_preset_bitmap( slot, mode, origin ); |
134 | |
135 | /* allocate new one */ |
136 | if ( FT_ALLOC_MULT( bitmap->buffer, bitmap->rows, bitmap->pitch ) ) |
137 | goto Exit; |
138 | |
139 | slot->internal->flags |= FT_GLYPH_OWN_BITMAP; |
140 | |
141 | x_shift = 64 * -slot->bitmap_left; |
142 | y_shift = 64 * -slot->bitmap_top; |
143 | if ( bitmap->pixel_mode == FT_PIXEL_MODE_LCD_V ) |
144 | y_shift += 64 * (FT_Int)bitmap->rows / 3; |
145 | else |
146 | y_shift += 64 * (FT_Int)bitmap->rows; |
147 | |
148 | if ( origin ) |
149 | { |
150 | x_shift += origin->x; |
151 | y_shift += origin->y; |
152 | } |
153 | |
154 | /* translate outline to render it into the bitmap */ |
155 | if ( x_shift || y_shift ) |
156 | FT_Outline_Translate( outline, x_shift, y_shift ); |
157 | |
158 | /* set up parameters */ |
159 | params.target = bitmap; |
160 | params.source = outline; |
161 | params.flags = FT_RASTER_FLAG_AA; |
162 | |
163 | #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING |
164 | |
165 | /* implode outline if needed */ |
166 | { |
167 | FT_Vector* points = outline->points; |
168 | FT_Vector* points_end = points + outline->n_points; |
169 | FT_Vector* vec; |
170 | |
171 | |
172 | if ( hmul ) |
173 | for ( vec = points; vec < points_end; vec++ ) |
174 | vec->x *= 3; |
175 | |
176 | if ( vmul ) |
177 | for ( vec = points; vec < points_end; vec++ ) |
178 | vec->y *= 3; |
179 | } |
180 | |
181 | /* render outline into the bitmap */ |
182 | error = render->raster_render( render->raster, ¶ms ); |
183 | |
184 | /* deflate outline if needed */ |
185 | { |
186 | FT_Vector* points = outline->points; |
187 | FT_Vector* points_end = points + outline->n_points; |
188 | FT_Vector* vec; |
189 | |
190 | |
191 | if ( hmul ) |
192 | for ( vec = points; vec < points_end; vec++ ) |
193 | vec->x /= 3; |
194 | |
195 | if ( vmul ) |
196 | for ( vec = points; vec < points_end; vec++ ) |
197 | vec->y /= 3; |
198 | } |
199 | |
200 | if ( error ) |
201 | goto Exit; |
202 | |
203 | /* finally apply filtering */ |
204 | if ( hmul || vmul ) |
205 | { |
206 | FT_Byte* lcd_weights; |
207 | FT_Bitmap_LcdFilterFunc lcd_filter_func; |
208 | |
209 | |
210 | /* Per-face LCD filtering takes priority if set up. */ |
211 | if ( slot->face && slot->face->internal->lcd_filter_func ) |
212 | { |
213 | lcd_weights = slot->face->internal->lcd_weights; |
214 | lcd_filter_func = slot->face->internal->lcd_filter_func; |
215 | } |
216 | else |
217 | { |
218 | lcd_weights = slot->library->lcd_weights; |
219 | lcd_filter_func = slot->library->lcd_filter_func; |
220 | } |
221 | |
222 | if ( lcd_filter_func ) |
223 | lcd_filter_func( bitmap, mode, lcd_weights ); |
224 | } |
225 | |
226 | #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ |
227 | |
228 | if ( hmul ) /* lcd */ |
229 | { |
230 | FT_Byte* line; |
231 | FT_Byte* temp = NULL; |
232 | FT_UInt i, j; |
233 | |
234 | unsigned int height = bitmap->rows; |
235 | unsigned int width = bitmap->width; |
236 | int pitch = bitmap->pitch; |
237 | |
238 | |
239 | /* Render 3 separate monochrome bitmaps, shifting the outline */ |
240 | /* by 1/3 pixel. */ |
241 | width /= 3; |
242 | |
243 | bitmap->buffer += width; |
244 | |
245 | error = render->raster_render( render->raster, ¶ms ); |
246 | if ( error ) |
247 | goto Exit; |
248 | |
249 | FT_Outline_Translate( outline, -21, 0 ); |
250 | x_shift -= 21; |
251 | bitmap->buffer += width; |
252 | |
253 | error = render->raster_render( render->raster, ¶ms ); |
254 | if ( error ) |
255 | goto Exit; |
256 | |
257 | FT_Outline_Translate( outline, 42, 0 ); |
258 | x_shift += 42; |
259 | bitmap->buffer -= 2 * width; |
260 | |
261 | error = render->raster_render( render->raster, ¶ms ); |
262 | if ( error ) |
263 | goto Exit; |
264 | |
265 | /* XXX: Rearrange the bytes according to FT_PIXEL_MODE_LCD. */ |
266 | /* XXX: It is more efficient to render every third byte above. */ |
267 | |
268 | if ( FT_ALLOC( temp, (FT_ULong)pitch ) ) |
269 | goto Exit; |
270 | |
271 | for ( i = 0; i < height; i++ ) |
272 | { |
273 | line = bitmap->buffer + i * (FT_ULong)pitch; |
274 | for ( j = 0; j < width; j++ ) |
275 | { |
276 | temp[3 * j ] = line[j]; |
277 | temp[3 * j + 1] = line[j + width]; |
278 | temp[3 * j + 2] = line[j + width + width]; |
279 | } |
280 | FT_MEM_COPY( line, temp, pitch ); |
281 | } |
282 | |
283 | FT_FREE( temp ); |
284 | } |
285 | else if ( vmul ) /* lcd_v */ |
286 | { |
287 | int pitch = bitmap->pitch; |
288 | |
289 | |
290 | /* Render 3 separate monochrome bitmaps, shifting the outline */ |
291 | /* by 1/3 pixel. Triple the pitch to render on each third row. */ |
292 | bitmap->pitch *= 3; |
293 | bitmap->rows /= 3; |
294 | |
295 | bitmap->buffer += pitch; |
296 | |
297 | error = render->raster_render( render->raster, ¶ms ); |
298 | if ( error ) |
299 | goto Exit; |
300 | |
301 | FT_Outline_Translate( outline, 0, 21 ); |
302 | y_shift += 21; |
303 | bitmap->buffer += pitch; |
304 | |
305 | error = render->raster_render( render->raster, ¶ms ); |
306 | if ( error ) |
307 | goto Exit; |
308 | |
309 | FT_Outline_Translate( outline, 0, -42 ); |
310 | y_shift -= 42; |
311 | bitmap->buffer -= 2 * pitch; |
312 | |
313 | error = render->raster_render( render->raster, ¶ms ); |
314 | if ( error ) |
315 | goto Exit; |
316 | |
317 | bitmap->pitch /= 3; |
318 | bitmap->rows *= 3; |
319 | } |
320 | else /* grayscale */ |
321 | error = render->raster_render( render->raster, ¶ms ); |
322 | |
323 | #endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ |
324 | |
325 | Exit: |
326 | if ( !error ) |
327 | { |
328 | /* everything is fine; the glyph is now officially a bitmap */ |
329 | slot->format = FT_GLYPH_FORMAT_BITMAP; |
330 | } |
331 | else if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) |
332 | { |
333 | FT_FREE( bitmap->buffer ); |
334 | slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP; |
335 | } |
336 | |
337 | if ( x_shift || y_shift ) |
338 | FT_Outline_Translate( outline, -x_shift, -y_shift ); |
339 | |
340 | return error; |
341 | } |
342 | |
343 | |
344 | /* convert a slot's glyph image into a bitmap */ |
345 | static FT_Error |
346 | ft_smooth_render( FT_Renderer render, |
347 | FT_GlyphSlot slot, |
348 | FT_Render_Mode mode, |
349 | const FT_Vector* origin ) |
350 | { |
351 | if ( mode == FT_RENDER_MODE_LIGHT ) |
352 | mode = FT_RENDER_MODE_NORMAL; |
353 | |
354 | return ft_smooth_render_generic( render, slot, mode, origin, |
355 | FT_RENDER_MODE_NORMAL ); |
356 | } |
357 | |
358 | |
359 | /* convert a slot's glyph image into a horizontal LCD bitmap */ |
360 | static FT_Error |
361 | ft_smooth_render_lcd( FT_Renderer render, |
362 | FT_GlyphSlot slot, |
363 | FT_Render_Mode mode, |
364 | const FT_Vector* origin ) |
365 | { |
366 | return ft_smooth_render_generic( render, slot, mode, origin, |
367 | FT_RENDER_MODE_LCD ); |
368 | } |
369 | |
370 | |
371 | /* convert a slot's glyph image into a vertical LCD bitmap */ |
372 | static FT_Error |
373 | ft_smooth_render_lcd_v( FT_Renderer render, |
374 | FT_GlyphSlot slot, |
375 | FT_Render_Mode mode, |
376 | const FT_Vector* origin ) |
377 | { |
378 | return ft_smooth_render_generic( render, slot, mode, origin, |
379 | FT_RENDER_MODE_LCD_V ); |
380 | } |
381 | |
382 | |
383 | FT_DEFINE_RENDERER( |
384 | ft_smooth_renderer_class, |
385 | |
386 | FT_MODULE_RENDERER, |
387 | sizeof ( FT_RendererRec ), |
388 | |
389 | "smooth" , |
390 | 0x10000L, |
391 | 0x20000L, |
392 | |
393 | NULL, /* module specific interface */ |
394 | |
395 | (FT_Module_Constructor)ft_smooth_init, /* module_init */ |
396 | (FT_Module_Destructor) NULL, /* module_done */ |
397 | (FT_Module_Requester) NULL, /* get_interface */ |
398 | |
399 | FT_GLYPH_FORMAT_OUTLINE, |
400 | |
401 | (FT_Renderer_RenderFunc) ft_smooth_render, /* render_glyph */ |
402 | (FT_Renderer_TransformFunc)ft_smooth_transform, /* transform_glyph */ |
403 | (FT_Renderer_GetCBoxFunc) ft_smooth_get_cbox, /* get_glyph_cbox */ |
404 | (FT_Renderer_SetModeFunc) ft_smooth_set_mode, /* set_mode */ |
405 | |
406 | (FT_Raster_Funcs*)&FT_GRAYS_RASTER_GET /* raster_class */ |
407 | ) |
408 | |
409 | |
410 | FT_DEFINE_RENDERER( |
411 | ft_smooth_lcd_renderer_class, |
412 | |
413 | FT_MODULE_RENDERER, |
414 | sizeof ( FT_RendererRec ), |
415 | |
416 | "smooth-lcd" , |
417 | 0x10000L, |
418 | 0x20000L, |
419 | |
420 | NULL, /* module specific interface */ |
421 | |
422 | (FT_Module_Constructor)ft_smooth_init, /* module_init */ |
423 | (FT_Module_Destructor) NULL, /* module_done */ |
424 | (FT_Module_Requester) NULL, /* get_interface */ |
425 | |
426 | FT_GLYPH_FORMAT_OUTLINE, |
427 | |
428 | (FT_Renderer_RenderFunc) ft_smooth_render_lcd, /* render_glyph */ |
429 | (FT_Renderer_TransformFunc)ft_smooth_transform, /* transform_glyph */ |
430 | (FT_Renderer_GetCBoxFunc) ft_smooth_get_cbox, /* get_glyph_cbox */ |
431 | (FT_Renderer_SetModeFunc) ft_smooth_set_mode, /* set_mode */ |
432 | |
433 | (FT_Raster_Funcs*)&FT_GRAYS_RASTER_GET /* raster_class */ |
434 | ) |
435 | |
436 | |
437 | FT_DEFINE_RENDERER( |
438 | ft_smooth_lcdv_renderer_class, |
439 | |
440 | FT_MODULE_RENDERER, |
441 | sizeof ( FT_RendererRec ), |
442 | |
443 | "smooth-lcdv" , |
444 | 0x10000L, |
445 | 0x20000L, |
446 | |
447 | NULL, /* module specific interface */ |
448 | |
449 | (FT_Module_Constructor)ft_smooth_init, /* module_init */ |
450 | (FT_Module_Destructor) NULL, /* module_done */ |
451 | (FT_Module_Requester) NULL, /* get_interface */ |
452 | |
453 | FT_GLYPH_FORMAT_OUTLINE, |
454 | |
455 | (FT_Renderer_RenderFunc) ft_smooth_render_lcd_v, /* render_glyph */ |
456 | (FT_Renderer_TransformFunc)ft_smooth_transform, /* transform_glyph */ |
457 | (FT_Renderer_GetCBoxFunc) ft_smooth_get_cbox, /* get_glyph_cbox */ |
458 | (FT_Renderer_SetModeFunc) ft_smooth_set_mode, /* set_mode */ |
459 | |
460 | (FT_Raster_Funcs*)&FT_GRAYS_RASTER_GET /* raster_class */ |
461 | ) |
462 | |
463 | |
464 | /* END */ |
465 | |