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