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 */