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