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