1/****************************************************************************
2 *
3 * ftlcdfil.c
4 *
5 * FreeType API for color filtering of subpixel bitmap glyphs (body).
6 *
7 * Copyright (C) 2006-2019 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
22#include FT_LCD_FILTER_H
23#include FT_IMAGE_H
24#include FT_INTERNAL_OBJECTS_H
25
26
27#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
28
29/* define USE_LEGACY to implement the legacy filter */
30#define USE_LEGACY
31
32#define FT_SHIFTCLAMP( x ) ( x >>= 8, (FT_Byte)( x > 255 ? 255 : x ) )
33
34
35 /* add padding according to filter weights */
36 FT_BASE_DEF (void)
37 ft_lcd_padding( FT_BBox* cbox,
38 FT_GlyphSlot slot,
39 FT_Render_Mode mode )
40 {
41 FT_Byte* lcd_weights;
42 FT_Bitmap_LcdFilterFunc lcd_filter_func;
43
44
45 /* Per-face LCD filtering takes priority if set up. */
46 if ( slot->face && slot->face->internal->lcd_filter_func )
47 {
48 lcd_weights = slot->face->internal->lcd_weights;
49 lcd_filter_func = slot->face->internal->lcd_filter_func;
50 }
51 else
52 {
53 lcd_weights = slot->library->lcd_weights;
54 lcd_filter_func = slot->library->lcd_filter_func;
55 }
56
57 if ( lcd_filter_func == ft_lcd_filter_fir )
58 {
59 if ( mode == FT_RENDER_MODE_LCD )
60 {
61 cbox->xMin -= lcd_weights[0] ? 43 :
62 lcd_weights[1] ? 22 : 0;
63 cbox->xMax += lcd_weights[4] ? 43 :
64 lcd_weights[3] ? 22 : 0;
65 }
66 else if ( mode == FT_RENDER_MODE_LCD_V )
67 {
68 cbox->yMin -= lcd_weights[0] ? 43 :
69 lcd_weights[1] ? 22 : 0;
70 cbox->yMax += lcd_weights[4] ? 43 :
71 lcd_weights[3] ? 22 : 0;
72 }
73 }
74 }
75
76
77 /* FIR filter used by the default and light filters */
78 FT_BASE_DEF( void )
79 ft_lcd_filter_fir( FT_Bitmap* bitmap,
80 FT_Render_Mode mode,
81 FT_LcdFiveTapFilter weights )
82 {
83 FT_UInt width = (FT_UInt)bitmap->width;
84 FT_UInt height = (FT_UInt)bitmap->rows;
85 FT_Int pitch = bitmap->pitch;
86 FT_Byte* origin = bitmap->buffer;
87
88
89 /* take care of bitmap flow */
90 if ( pitch > 0 && height > 0 )
91 origin += pitch * (FT_Int)( height - 1 );
92
93 /* horizontal in-place FIR filter */
94 if ( mode == FT_RENDER_MODE_LCD && width >= 2 )
95 {
96 FT_Byte* line = origin;
97
98
99 /* `fir' must be at least 32 bit wide, since the sum of */
100 /* the values in `weights' can exceed 0xFF */
101
102 for ( ; height > 0; height--, line -= pitch )
103 {
104 FT_UInt fir[5];
105 FT_UInt val, xx;
106
107
108 val = line[0];
109 fir[2] = weights[2] * val;
110 fir[3] = weights[3] * val;
111 fir[4] = weights[4] * val;
112
113 val = line[1];
114 fir[1] = fir[2] + weights[1] * val;
115 fir[2] = fir[3] + weights[2] * val;
116 fir[3] = fir[4] + weights[3] * val;
117 fir[4] = weights[4] * val;
118
119 for ( xx = 2; xx < width; xx++ )
120 {
121 val = line[xx];
122 fir[0] = fir[1] + weights[0] * val;
123 fir[1] = fir[2] + weights[1] * val;
124 fir[2] = fir[3] + weights[2] * val;
125 fir[3] = fir[4] + weights[3] * val;
126 fir[4] = weights[4] * val;
127
128 line[xx - 2] = FT_SHIFTCLAMP( fir[0] );
129 }
130
131 line[xx - 2] = FT_SHIFTCLAMP( fir[1] );
132 line[xx - 1] = FT_SHIFTCLAMP( fir[2] );
133 }
134 }
135
136 /* vertical in-place FIR filter */
137 else if ( mode == FT_RENDER_MODE_LCD_V && height >= 2 )
138 {
139 FT_Byte* column = origin;
140
141
142 for ( ; width > 0; width--, column++ )
143 {
144 FT_Byte* col = column;
145 FT_UInt fir[5];
146 FT_UInt val, yy;
147
148
149 val = col[0];
150 fir[2] = weights[2] * val;
151 fir[3] = weights[3] * val;
152 fir[4] = weights[4] * val;
153 col -= pitch;
154
155 val = col[0];
156 fir[1] = fir[2] + weights[1] * val;
157 fir[2] = fir[3] + weights[2] * val;
158 fir[3] = fir[4] + weights[3] * val;
159 fir[4] = weights[4] * val;
160 col -= pitch;
161
162 for ( yy = 2; yy < height; yy++, col -= pitch )
163 {
164 val = col[0];
165 fir[0] = fir[1] + weights[0] * val;
166 fir[1] = fir[2] + weights[1] * val;
167 fir[2] = fir[3] + weights[2] * val;
168 fir[3] = fir[4] + weights[3] * val;
169 fir[4] = weights[4] * val;
170
171 col[pitch * 2] = FT_SHIFTCLAMP( fir[0] );
172 }
173
174 col[pitch * 2] = FT_SHIFTCLAMP( fir[1] );
175 col[pitch] = FT_SHIFTCLAMP( fir[2] );
176 }
177 }
178 }
179
180
181#ifdef USE_LEGACY
182
183 /* intra-pixel filter used by the legacy filter */
184 static void
185 _ft_lcd_filter_legacy( FT_Bitmap* bitmap,
186 FT_Render_Mode mode,
187 FT_Byte* weights )
188 {
189 FT_UInt width = (FT_UInt)bitmap->width;
190 FT_UInt height = (FT_UInt)bitmap->rows;
191 FT_Int pitch = bitmap->pitch;
192 FT_Byte* origin = bitmap->buffer;
193
194 static const unsigned int filters[3][3] =
195 {
196 { 65538 * 9/13, 65538 * 1/6, 65538 * 1/13 },
197 { 65538 * 3/13, 65538 * 4/6, 65538 * 3/13 },
198 { 65538 * 1/13, 65538 * 1/6, 65538 * 9/13 }
199 };
200
201 FT_UNUSED( weights );
202
203
204 /* take care of bitmap flow */
205 if ( pitch > 0 && height > 0 )
206 origin += pitch * (FT_Int)( height - 1 );
207
208 /* horizontal in-place intra-pixel filter */
209 if ( mode == FT_RENDER_MODE_LCD && width >= 3 )
210 {
211 FT_Byte* line = origin;
212
213
214 for ( ; height > 0; height--, line -= pitch )
215 {
216 FT_UInt xx;
217
218
219 for ( xx = 0; xx < width; xx += 3 )
220 {
221 FT_UInt r, g, b;
222 FT_UInt p;
223
224
225 p = line[xx];
226 r = filters[0][0] * p;
227 g = filters[0][1] * p;
228 b = filters[0][2] * p;
229
230 p = line[xx + 1];
231 r += filters[1][0] * p;
232 g += filters[1][1] * p;
233 b += filters[1][2] * p;
234
235 p = line[xx + 2];
236 r += filters[2][0] * p;
237 g += filters[2][1] * p;
238 b += filters[2][2] * p;
239
240 line[xx] = (FT_Byte)( r / 65536 );
241 line[xx + 1] = (FT_Byte)( g / 65536 );
242 line[xx + 2] = (FT_Byte)( b / 65536 );
243 }
244 }
245 }
246 else if ( mode == FT_RENDER_MODE_LCD_V && height >= 3 )
247 {
248 FT_Byte* column = origin;
249
250
251 for ( ; width > 0; width--, column++ )
252 {
253 FT_Byte* col = column - 2 * pitch;
254
255
256 for ( ; height > 0; height -= 3, col -= 3 * pitch )
257 {
258 FT_UInt r, g, b;
259 FT_UInt p;
260
261
262 p = col[0];
263 r = filters[0][0] * p;
264 g = filters[0][1] * p;
265 b = filters[0][2] * p;
266
267 p = col[pitch];
268 r += filters[1][0] * p;
269 g += filters[1][1] * p;
270 b += filters[1][2] * p;
271
272 p = col[pitch * 2];
273 r += filters[2][0] * p;
274 g += filters[2][1] * p;
275 b += filters[2][2] * p;
276
277 col[0] = (FT_Byte)( r / 65536 );
278 col[pitch] = (FT_Byte)( g / 65536 );
279 col[pitch * 2] = (FT_Byte)( b / 65536 );
280 }
281 }
282 }
283 }
284
285#endif /* USE_LEGACY */
286
287
288 /* documentation in ftlcdfil.h */
289
290 FT_EXPORT_DEF( FT_Error )
291 FT_Library_SetLcdFilterWeights( FT_Library library,
292 unsigned char *weights )
293 {
294 if ( !library )
295 return FT_THROW( Invalid_Library_Handle );
296
297 if ( !weights )
298 return FT_THROW( Invalid_Argument );
299
300 ft_memcpy( library->lcd_weights, weights, FT_LCD_FILTER_FIVE_TAPS );
301 library->lcd_filter_func = ft_lcd_filter_fir;
302
303 return FT_Err_Ok;
304 }
305
306
307 /* documentation in ftlcdfil.h */
308
309 FT_EXPORT_DEF( FT_Error )
310 FT_Library_SetLcdFilter( FT_Library library,
311 FT_LcdFilter filter )
312 {
313 static const FT_LcdFiveTapFilter default_weights =
314 { 0x08, 0x4d, 0x56, 0x4d, 0x08 };
315 static const FT_LcdFiveTapFilter light_weights =
316 { 0x00, 0x55, 0x56, 0x55, 0x00 };
317
318
319 if ( !library )
320 return FT_THROW( Invalid_Library_Handle );
321
322 switch ( filter )
323 {
324 case FT_LCD_FILTER_NONE:
325 library->lcd_filter_func = NULL;
326 break;
327
328 case FT_LCD_FILTER_DEFAULT:
329 ft_memcpy( library->lcd_weights,
330 default_weights,
331 FT_LCD_FILTER_FIVE_TAPS );
332 library->lcd_filter_func = ft_lcd_filter_fir;
333 break;
334
335 case FT_LCD_FILTER_LIGHT:
336 ft_memcpy( library->lcd_weights,
337 light_weights,
338 FT_LCD_FILTER_FIVE_TAPS );
339 library->lcd_filter_func = ft_lcd_filter_fir;
340 break;
341
342#ifdef USE_LEGACY
343
344 case FT_LCD_FILTER_LEGACY:
345 case FT_LCD_FILTER_LEGACY1:
346 library->lcd_filter_func = _ft_lcd_filter_legacy;
347 break;
348
349#endif
350
351 default:
352 return FT_THROW( Invalid_Argument );
353 }
354
355 return FT_Err_Ok;
356 }
357
358
359 FT_EXPORT_DEF( FT_Error )
360 FT_Library_SetLcdGeometry( FT_Library library,
361 FT_Vector* sub )
362 {
363 FT_UNUSED( library );
364 FT_UNUSED( sub );
365
366 return FT_THROW( Unimplemented_Feature );
367 }
368
369#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
370
371 /* add padding to accommodate outline shifts */
372 FT_BASE_DEF (void)
373 ft_lcd_padding( FT_BBox* cbox,
374 FT_GlyphSlot slot,
375 FT_Render_Mode mode )
376 {
377 FT_Vector* sub = slot->library->lcd_geometry;
378
379 if ( mode == FT_RENDER_MODE_LCD )
380 {
381 cbox->xMin -= FT_MAX( FT_MAX( sub[0].x, sub[1].x ), sub[2].x );
382 cbox->xMax -= FT_MIN( FT_MIN( sub[0].x, sub[1].x ), sub[2].x );
383 cbox->yMin -= FT_MAX( FT_MAX( sub[0].y, sub[1].y ), sub[2].y );
384 cbox->yMax -= FT_MIN( FT_MIN( sub[0].y, sub[1].y ), sub[2].y );
385 }
386 else if ( mode == FT_RENDER_MODE_LCD_V )
387 {
388 cbox->xMin -= FT_MAX( FT_MAX( sub[0].y, sub[1].y ), sub[2].y );
389 cbox->xMax -= FT_MIN( FT_MIN( sub[0].y, sub[1].y ), sub[2].y );
390 cbox->yMin += FT_MIN( FT_MIN( sub[0].x, sub[1].x ), sub[2].x );
391 cbox->yMax += FT_MAX( FT_MAX( sub[0].x, sub[1].x ), sub[2].x );
392 }
393 }
394
395
396 FT_EXPORT_DEF( FT_Error )
397 FT_Library_SetLcdFilterWeights( FT_Library library,
398 unsigned char *weights )
399 {
400 FT_UNUSED( library );
401 FT_UNUSED( weights );
402
403 return FT_THROW( Unimplemented_Feature );
404 }
405
406
407 FT_EXPORT_DEF( FT_Error )
408 FT_Library_SetLcdFilter( FT_Library library,
409 FT_LcdFilter filter )
410 {
411 FT_UNUSED( library );
412 FT_UNUSED( filter );
413
414 return FT_THROW( Unimplemented_Feature );
415 }
416
417
418 /* documentation in ftlcdfil.h */
419
420 FT_EXPORT_DEF( FT_Error )
421 FT_Library_SetLcdGeometry( FT_Library library,
422 FT_Vector sub[3] )
423 {
424 if ( !library )
425 return FT_THROW( Invalid_Library_Handle );
426
427 if ( !sub )
428 return FT_THROW( Invalid_Argument );
429
430 ft_memcpy( library->lcd_geometry, sub, 3 * sizeof( FT_Vector ) );
431
432 return FT_THROW( Unimplemented_Feature );
433 }
434
435#endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
436
437
438/* END */
439