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)
37  ft_lcd_padding( FT_Pos*       Min,
38                  FT_Pos*       Max,
39                  FT_GlyphSlot  slot )
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      *Min -= lcd_weights[0] ? 43 :
60              lcd_weights[1] ? 22 : 0;
61      *Max += lcd_weights[4] ? 43 :
62              lcd_weights[3] ? 22 : 0;
63    }
64  }
65
66
67  /* FIR filter used by the default and light filters */
68  FT_BASE_DEF( void )
69  ft_lcd_filter_fir( FT_Bitmap*           bitmap,
70                     FT_Render_Mode       mode,
71                     FT_LcdFiveTapFilter  weights )
72  {
73    FT_UInt   width  = (FT_UInt)bitmap->width;
74    FT_UInt   height = (FT_UInt)bitmap->rows;
75    FT_Int    pitch  = bitmap->pitch;
76    FT_Byte*  origin = bitmap->buffer;
77
78
79    /* take care of bitmap flow */
80    if ( pitch > 0 )
81      origin += pitch * (FT_Int)( height - 1 );
82
83    /* horizontal in-place FIR filter */
84    if ( mode == FT_RENDER_MODE_LCD && width >= 2 )
85    {
86      FT_Byte*  line = origin;
87
88
89      /* `fir' must be at least 32 bit wide, since the sum of */
90      /* the values in `weights' can exceed 0xFF              */
91
92      for ( ; height > 0; height--, line -= pitch )
93      {
94        FT_UInt  fir[5];
95        FT_UInt  val, xx;
96
97
98        val    = line[0];
99        fir[2] = weights[2] * val;
100        fir[3] = weights[3] * val;
101        fir[4] = weights[4] * val;
102
103        val    = line[1];
104        fir[1] = fir[2] + weights[1] * val;
105        fir[2] = fir[3] + weights[2] * val;
106        fir[3] = fir[4] + weights[3] * val;
107        fir[4] =          weights[4] * val;
108
109        for ( xx = 2; xx < width; xx++ )
110        {
111          val    = line[xx];
112          fir[0] = fir[1] + weights[0] * val;
113          fir[1] = fir[2] + weights[1] * val;
114          fir[2] = fir[3] + weights[2] * val;
115          fir[3] = fir[4] + weights[3] * val;
116          fir[4] =          weights[4] * val;
117
118          line[xx - 2] = FT_SHIFTCLAMP( fir[0] );
119        }
120
121        line[xx - 2] = FT_SHIFTCLAMP( fir[1] );
122        line[xx - 1] = FT_SHIFTCLAMP( fir[2] );
123      }
124    }
125
126    /* vertical in-place FIR filter */
127    else if ( mode == FT_RENDER_MODE_LCD_V && height >= 2 )
128    {
129      FT_Byte*  column = origin;
130
131
132      for ( ; width > 0; width--, column++ )
133      {
134        FT_Byte*  col = column;
135        FT_UInt   fir[5];
136        FT_UInt   val, yy;
137
138
139        val    = col[0];
140        fir[2] = weights[2] * val;
141        fir[3] = weights[3] * val;
142        fir[4] = weights[4] * val;
143        col   -= pitch;
144
145        val    = col[0];
146        fir[1] = fir[2] + weights[1] * val;
147        fir[2] = fir[3] + weights[2] * val;
148        fir[3] = fir[4] + weights[3] * val;
149        fir[4] =          weights[4] * val;
150        col   -= pitch;
151
152        for ( yy = 2; yy < height; yy++, col -= pitch )
153        {
154          val    = col[0];
155          fir[0] = fir[1] + weights[0] * val;
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
161          col[pitch * 2]  = FT_SHIFTCLAMP( fir[0] );
162        }
163
164        col[pitch * 2]  = FT_SHIFTCLAMP( fir[1] );
165        col[pitch]      = FT_SHIFTCLAMP( fir[2] );
166      }
167    }
168  }
169
170
171#ifdef USE_LEGACY
172
173  /* intra-pixel filter used by the legacy filter */
174  static void
175  _ft_lcd_filter_legacy( FT_Bitmap*      bitmap,
176                         FT_Render_Mode  mode,
177                         FT_Byte*        weights )
178  {
179    FT_UInt   width  = (FT_UInt)bitmap->width;
180    FT_UInt   height = (FT_UInt)bitmap->rows;
181    FT_Int    pitch  = bitmap->pitch;
182    FT_Byte*  origin = bitmap->buffer;
183
184    static const unsigned int  filters[3][3] =
185    {
186      { 65538 * 9/13, 65538 * 1/6, 65538 * 1/13 },
187      { 65538 * 3/13, 65538 * 4/6, 65538 * 3/13 },
188      { 65538 * 1/13, 65538 * 1/6, 65538 * 9/13 }
189    };
190
191    FT_UNUSED( weights );
192
193
194    /* take care of bitmap flow */
195    if ( pitch > 0 )
196      origin += pitch * (FT_Int)( height - 1 );
197
198    /* horizontal in-place intra-pixel filter */
199    if ( mode == FT_RENDER_MODE_LCD && width >= 3 )
200    {
201      FT_Byte*  line = origin;
202
203
204      for ( ; height > 0; height--, line -= pitch )
205      {
206        FT_UInt  xx;
207
208
209        for ( xx = 0; xx < width; xx += 3 )
210        {
211          FT_UInt  r, g, b;
212          FT_UInt  p;
213
214
215          p  = line[xx];
216          r  = filters[0][0] * p;
217          g  = filters[0][1] * p;
218          b  = filters[0][2] * p;
219
220          p  = line[xx + 1];
221          r += filters[1][0] * p;
222          g += filters[1][1] * p;
223          b += filters[1][2] * p;
224
225          p  = line[xx + 2];
226          r += filters[2][0] * p;
227          g += filters[2][1] * p;
228          b += filters[2][2] * p;
229
230          line[xx]     = (FT_Byte)( r / 65536 );
231          line[xx + 1] = (FT_Byte)( g / 65536 );
232          line[xx + 2] = (FT_Byte)( b / 65536 );
233        }
234      }
235    }
236    else if ( mode == FT_RENDER_MODE_LCD_V && height >= 3 )
237    {
238      FT_Byte*  column = origin;
239
240
241      for ( ; width > 0; width--, column++ )
242      {
243        FT_Byte*  col = column - 2 * pitch;
244
245
246        for ( ; height > 0; height -= 3, col -= 3 * pitch )
247        {
248          FT_UInt  r, g, b;
249          FT_UInt  p;
250
251
252          p  = col[0];
253          r  = filters[0][0] * p;
254          g  = filters[0][1] * p;
255          b  = filters[0][2] * p;
256
257          p  = col[pitch];
258          r += filters[1][0] * p;
259          g += filters[1][1] * p;
260          b += filters[1][2] * p;
261
262          p  = col[pitch * 2];
263          r += filters[2][0] * p;
264          g += filters[2][1] * p;
265          b += filters[2][2] * p;
266
267          col[0]         = (FT_Byte)( r / 65536 );
268          col[pitch]     = (FT_Byte)( g / 65536 );
269          col[pitch * 2] = (FT_Byte)( b / 65536 );
270        }
271      }
272    }
273  }
274
275#endif /* USE_LEGACY */
276
277
278  FT_EXPORT_DEF( FT_Error )
279  FT_Library_SetLcdFilterWeights( FT_Library      library,
280                                  unsigned char  *weights )
281  {
282    if ( !library )
283      return FT_THROW( Invalid_Library_Handle );
284
285    if ( !weights )
286      return FT_THROW( Invalid_Argument );
287
288    ft_memcpy( library->lcd_weights, weights, FT_LCD_FILTER_FIVE_TAPS );
289    library->lcd_filter_func = ft_lcd_filter_fir;
290
291    return FT_Err_Ok;
292  }
293
294
295  FT_EXPORT_DEF( FT_Error )
296  FT_Library_SetLcdFilter( FT_Library    library,
297                           FT_LcdFilter  filter )
298  {
299    static const FT_LcdFiveTapFilter  default_weights =
300                   { 0x08, 0x4d, 0x56, 0x4d, 0x08 };
301    static const FT_LcdFiveTapFilter  light_weights =
302                   { 0x00, 0x55, 0x56, 0x55, 0x00 };
303
304
305    if ( !library )
306      return FT_THROW( Invalid_Library_Handle );
307
308    switch ( filter )
309    {
310    case FT_LCD_FILTER_NONE:
311      library->lcd_filter_func = NULL;
312      break;
313
314    case FT_LCD_FILTER_DEFAULT:
315      ft_memcpy( library->lcd_weights,
316                 default_weights,
317                 FT_LCD_FILTER_FIVE_TAPS );
318      library->lcd_filter_func = ft_lcd_filter_fir;
319      break;
320
321    case FT_LCD_FILTER_LIGHT:
322      ft_memcpy( library->lcd_weights,
323                 light_weights,
324                 FT_LCD_FILTER_FIVE_TAPS );
325      library->lcd_filter_func = ft_lcd_filter_fir;
326      break;
327
328#ifdef USE_LEGACY
329
330    case FT_LCD_FILTER_LEGACY:
331    case FT_LCD_FILTER_LEGACY1:
332      library->lcd_filter_func = _ft_lcd_filter_legacy;
333      break;
334
335#endif
336
337    default:
338      return FT_THROW( Invalid_Argument );
339    }
340
341    return FT_Err_Ok;
342  }
343
344#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
345
346  /* add padding according to accommodate outline shifts */
347  FT_BASE_DEF (void)
348  ft_lcd_padding( FT_Pos*       Min,
349                  FT_Pos*       Max,
350                  FT_GlyphSlot  slot )
351  {
352    FT_UNUSED( slot );
353
354    *Min -= 21;
355    *Max += 21;
356  }
357
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