1/***************************************************************************/
2/*                                                                         */
3/*  ftlcdfil.c                                                             */
4/*                                                                         */
5/*    FreeType API for color filtering of subpixel bitmap glyphs (body).   */
6/*                                                                         */
7/*  Copyright 2006, 2008, 2009 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_LCD_FILTER_H
21#include FT_IMAGE_H
22#include FT_INTERNAL_OBJECTS_H
23
24
25#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
26
27/* define USE_LEGACY to implement the legacy filter */
28#define  USE_LEGACY
29
30  /* FIR filter used by the default and light filters */
31  static void
32  _ft_lcd_filter_fir( FT_Bitmap*      bitmap,
33                      FT_Render_Mode  mode,
34                      FT_Library      library )
35  {
36    FT_Byte*  weights = library->lcd_weights;
37    FT_UInt   width   = (FT_UInt)bitmap->width;
38    FT_UInt   height  = (FT_UInt)bitmap->rows;
39
40
41    /* horizontal in-place FIR filter */
42    if ( mode == FT_RENDER_MODE_LCD && width >= 4 )
43    {
44      FT_Byte*  line = bitmap->buffer;
45
46
47      for ( ; height > 0; height--, line += bitmap->pitch )
48      {
49        FT_UInt  fir[5];
50        FT_UInt  val1, xx;
51
52
53        val1   = line[0];
54        fir[0] = weights[2] * val1;
55        fir[1] = weights[3] * val1;
56        fir[2] = weights[4] * val1;
57        fir[3] = 0;
58        fir[4] = 0;
59
60        val1    = line[1];
61        fir[0] += weights[1] * val1;
62        fir[1] += weights[2] * val1;
63        fir[2] += weights[3] * val1;
64        fir[3] += weights[4] * val1;
65
66        for ( xx = 2; xx < width; xx++ )
67        {
68          FT_UInt  val, pix;
69
70
71          val    = line[xx];
72          pix    = fir[0] + weights[0] * val;
73          fir[0] = fir[1] + weights[1] * val;
74          fir[1] = fir[2] + weights[2] * val;
75          fir[2] = fir[3] + weights[3] * val;
76          fir[3] =          weights[4] * val;
77
78          pix        >>= 8;
79          pix         |= -( pix >> 8 );
80          line[xx - 2] = (FT_Byte)pix;
81        }
82
83        {
84          FT_UInt  pix;
85
86
87          pix          = fir[0] >> 8;
88          pix         |= -( pix >> 8 );
89          line[xx - 2] = (FT_Byte)pix;
90
91          pix          = fir[1] >> 8;
92          pix         |= -( pix >> 8 );
93          line[xx - 1] = (FT_Byte)pix;
94        }
95      }
96    }
97
98    /* vertical in-place FIR filter */
99    else if ( mode == FT_RENDER_MODE_LCD_V && height >= 4 )
100    {
101      FT_Byte*  column = bitmap->buffer;
102      FT_Int    pitch  = bitmap->pitch;
103
104
105      for ( ; width > 0; width--, column++ )
106      {
107        FT_Byte*  col = column;
108        FT_UInt   fir[5];
109        FT_UInt   val1, yy;
110
111
112        val1   = col[0];
113        fir[0] = weights[2] * val1;
114        fir[1] = weights[3] * val1;
115        fir[2] = weights[4] * val1;
116        fir[3] = 0;
117        fir[4] = 0;
118        col   += pitch;
119
120        val1    = col[0];
121        fir[0] += weights[1] * val1;
122        fir[1] += weights[2] * val1;
123        fir[2] += weights[3] * val1;
124        fir[3] += weights[4] * val1;
125        col    += pitch;
126
127        for ( yy = 2; yy < height; yy++ )
128        {
129          FT_UInt  val, pix;
130
131
132          val    = col[0];
133          pix    = fir[0] + weights[0] * val;
134          fir[0] = fir[1] + weights[1] * val;
135          fir[1] = fir[2] + weights[2] * val;
136          fir[2] = fir[3] + weights[3] * val;
137          fir[3] =          weights[4] * val;
138
139          pix           >>= 8;
140          pix            |= -( pix >> 8 );
141          col[-2 * pitch] = (FT_Byte)pix;
142          col            += pitch;
143        }
144
145        {
146          FT_UInt  pix;
147
148
149          pix             = fir[0] >> 8;
150          pix            |= -( pix >> 8 );
151          col[-2 * pitch] = (FT_Byte)pix;
152
153          pix         = fir[1] >> 8;
154          pix        |= -( pix >> 8 );
155          col[-pitch] = (FT_Byte)pix;
156        }
157      }
158    }
159  }
160
161
162#ifdef USE_LEGACY
163
164  /* intra-pixel filter used by the legacy filter */
165  static void
166  _ft_lcd_filter_legacy( FT_Bitmap*      bitmap,
167                         FT_Render_Mode  mode,
168                         FT_Library      library )
169  {
170    FT_UInt  width  = (FT_UInt)bitmap->width;
171    FT_UInt  height = (FT_UInt)bitmap->rows;
172    FT_Int   pitch  = bitmap->pitch;
173
174    static const int  filters[3][3] =
175    {
176      { 65538 * 9/13, 65538 * 1/6, 65538 * 1/13 },
177      { 65538 * 3/13, 65538 * 4/6, 65538 * 3/13 },
178      { 65538 * 1/13, 65538 * 1/6, 65538 * 9/13 }
179    };
180
181    FT_UNUSED( library );
182
183
184    /* horizontal in-place intra-pixel filter */
185    if ( mode == FT_RENDER_MODE_LCD && width >= 3 )
186    {
187      FT_Byte*  line = bitmap->buffer;
188
189
190      for ( ; height > 0; height--, line += pitch )
191      {
192        FT_UInt  xx;
193
194
195        for ( xx = 0; xx < width; xx += 3 )
196        {
197          FT_UInt  r = 0;
198          FT_UInt  g = 0;
199          FT_UInt  b = 0;
200          FT_UInt  p;
201
202
203          p  = line[xx];
204          r += filters[0][0] * p;
205          g += filters[0][1] * p;
206          b += filters[0][2] * p;
207
208          p  = line[xx + 1];
209          r += filters[1][0] * p;
210          g += filters[1][1] * p;
211          b += filters[1][2] * p;
212
213          p  = line[xx + 2];
214          r += filters[2][0] * p;
215          g += filters[2][1] * p;
216          b += filters[2][2] * p;
217
218          line[xx]     = (FT_Byte)( r / 65536 );
219          line[xx + 1] = (FT_Byte)( g / 65536 );
220          line[xx + 2] = (FT_Byte)( b / 65536 );
221        }
222      }
223    }
224    else if ( mode == FT_RENDER_MODE_LCD_V && height >= 3 )
225    {
226      FT_Byte*  column = bitmap->buffer;
227
228
229      for ( ; width > 0; width--, column++ )
230      {
231        FT_Byte*  col     = column;
232        FT_Byte*  col_end = col + height * pitch;
233
234
235        for ( ; col < col_end; col += 3 * pitch )
236        {
237          FT_UInt  r = 0;
238          FT_UInt  g = 0;
239          FT_UInt  b = 0;
240          FT_UInt  p;
241
242
243          p  = col[0];
244          r += filters[0][0] * p;
245          g += filters[0][1] * p;
246          b += filters[0][2] * p;
247
248          p  = col[pitch];
249          r += filters[1][0] * p;
250          g += filters[1][1] * p;
251          b += filters[1][2] * p;
252
253          p  = col[pitch * 2];
254          r += filters[2][0] * p;
255          g += filters[2][1] * p;
256          b += filters[2][2] * p;
257
258          col[0]         = (FT_Byte)( r / 65536 );
259          col[pitch]     = (FT_Byte)( g / 65536 );
260          col[2 * pitch] = (FT_Byte)( b / 65536 );
261        }
262      }
263    }
264  }
265
266#endif /* USE_LEGACY */
267
268
269  FT_EXPORT_DEF( FT_Error )
270  FT_Library_SetLcdFilter( FT_Library     library,
271                           FT_LcdFilter   filter )
272  {
273    static const FT_Byte  light_filter[5] =
274                            { 0, 85, 86, 85, 0 };
275    /* the values here sum up to a value larger than 256, */
276    /* providing a cheap gamma correction                 */
277    static const FT_Byte  default_filter[5] =
278                            { 0x10, 0x40, 0x70, 0x40, 0x10 };
279
280
281    if ( library == NULL )
282      return FT_Err_Invalid_Argument;
283
284    switch ( filter )
285    {
286    case FT_LCD_FILTER_NONE:
287      library->lcd_filter_func = NULL;
288      library->lcd_extra       = 0;
289      break;
290
291    case FT_LCD_FILTER_DEFAULT:
292#if defined( FT_FORCE_LEGACY_LCD_FILTER )
293
294      library->lcd_filter_func = _ft_lcd_filter_legacy;
295      library->lcd_extra       = 0;
296
297#elif defined( FT_FORCE_LIGHT_LCD_FILTER )
298
299      ft_memcpy( library->lcd_weights, light_filter, 5 );
300      library->lcd_filter_func = _ft_lcd_filter_fir;
301      library->lcd_extra       = 2;
302
303#else
304
305      ft_memcpy( library->lcd_weights, default_filter, 5 );
306      library->lcd_filter_func = _ft_lcd_filter_fir;
307      library->lcd_extra       = 2;
308
309#endif
310
311      break;
312
313    case FT_LCD_FILTER_LIGHT:
314      ft_memcpy( library->lcd_weights, light_filter, 5 );
315      library->lcd_filter_func = _ft_lcd_filter_fir;
316      library->lcd_extra       = 2;
317      break;
318
319#ifdef USE_LEGACY
320
321    case FT_LCD_FILTER_LEGACY:
322      library->lcd_filter_func = _ft_lcd_filter_legacy;
323      library->lcd_extra       = 0;
324      break;
325
326#endif
327
328    default:
329      return FT_Err_Invalid_Argument;
330    }
331
332    library->lcd_filter = filter;
333    return 0;
334  }
335
336#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
337
338  FT_EXPORT_DEF( FT_Error )
339  FT_Library_SetLcdFilter( FT_Library    library,
340                           FT_LcdFilter  filter )
341  {
342    FT_UNUSED( library );
343    FT_UNUSED( filter );
344
345    return FT_Err_Unimplemented_Feature;
346  }
347
348#endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
349
350
351/* END */
352