• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  *
3  * ftlcdfil.c
4  *
5  *   FreeType API for color filtering of subpixel bitmap glyphs (body).
6  *
7  * Copyright (C) 2006-2022 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 )
ft_lcd_padding(FT_BBox * cbox,FT_GlyphSlot slot,FT_Render_Mode mode)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 )
ft_lcd_filter_fir(FT_Bitmap * bitmap,FT_LcdFiveTapFilter weights)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
_ft_lcd_filter_legacy(FT_Bitmap * bitmap,FT_Byte * weights)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 )
FT_Library_SetLcdFilterWeights(FT_Library library,unsigned char * weights)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 )
FT_Library_SetLcdFilter(FT_Library library,FT_LcdFilter filter)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 )
FT_Library_SetLcdGeometry(FT_Library library,FT_Vector sub[3])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 )
ft_lcd_padding(FT_BBox * cbox,FT_GlyphSlot slot,FT_Render_Mode mode)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 )
FT_Library_SetLcdFilterWeights(FT_Library library,unsigned char * weights)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 )
FT_Library_SetLcdFilter(FT_Library library,FT_LcdFilter filter)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 )
FT_Library_SetLcdGeometry(FT_Library library,FT_Vector sub[3])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