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