• 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 2006-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 
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)
ft_lcd_padding(FT_BBox * cbox,FT_GlyphSlot slot,FT_Render_Mode mode)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 )
ft_lcd_filter_fir(FT_Bitmap * bitmap,FT_Render_Mode mode,FT_LcdFiveTapFilter weights)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
_ft_lcd_filter_legacy(FT_Bitmap * bitmap,FT_Render_Mode mode,FT_Byte * weights)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 )
FT_Library_SetLcdFilterWeights(FT_Library library,unsigned char * weights)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 )
FT_Library_SetLcdFilter(FT_Library library,FT_LcdFilter filter)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 )
FT_Library_SetLcdGeometry(FT_Library library,FT_Vector * sub)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)
ft_lcd_padding(FT_BBox * cbox,FT_GlyphSlot slot,FT_Render_Mode mode)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 )
FT_Library_SetLcdFilterWeights(FT_Library library,unsigned char * weights)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 )
FT_Library_SetLcdFilter(FT_Library library,FT_LcdFilter filter)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 )
FT_Library_SetLcdGeometry(FT_Library library,FT_Vector sub[3])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