1 /****************************************************************************
   2  *
   3  * ftlcdfil.c
   4  *
   5  *   FreeType API for color filtering of subpixel bitmap glyphs (body).
   6  *
   7  * Copyright (C) 2006-2019 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_BBox*        cbox,
  38                   FT_GlyphSlot    slot,
  39                   FT_Render_Mode  mode )
  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       if ( mode == FT_RENDER_MODE_LCD )
  60       {
  61         cbox->xMin -= lcd_weights[0] ? 43 :
  62                       lcd_weights[1] ? 22 : 0;
  63         cbox->xMax += lcd_weights[4] ? 43 :
  64                       lcd_weights[3] ? 22 : 0;
  65       }
  66       else if ( mode == FT_RENDER_MODE_LCD_V )
  67       {
  68         cbox->yMin -= lcd_weights[0] ? 43 :
  69                       lcd_weights[1] ? 22 : 0;
  70         cbox->yMax += lcd_weights[4] ? 43 :
  71                       lcd_weights[3] ? 22 : 0;
  72       }
  73     }
  74   }
  75 
  76 
  77   /* FIR filter used by the default and light filters */
  78   FT_BASE_DEF( void )
  79   ft_lcd_filter_fir( FT_Bitmap*           bitmap,
  80                      FT_LcdFiveTapFilter  weights )
  81   {
  82     FT_UInt   width  = (FT_UInt)bitmap->width;
  83     FT_UInt   height = (FT_UInt)bitmap->rows;
  84     FT_Int    pitch  = bitmap->pitch;
  85     FT_Byte*  origin = bitmap->buffer;
  86     FT_Byte   mode   = bitmap->pixel_mode;
  87 
  88 
  89     /* take care of bitmap flow */
  90     if ( pitch > 0 && height > 0 )
  91       origin += pitch * (FT_Int)( height - 1 );
  92 
  93     /* horizontal in-place FIR filter */
  94     if ( mode == FT_PIXEL_MODE_LCD && width >= 2 )
  95     {
  96       FT_Byte*  line = origin;
  97 
  98 
  99       /* `fir' must be at least 32 bit wide, since the sum of */
 100       /* the values in `weights' can exceed 0xFF              */
 101 
 102       for ( ; height > 0; height--, line -= pitch )
 103       {
 104         FT_UInt  fir[5];
 105         FT_UInt  val, xx;
 106 
 107 
 108         val    = line[0];
 109         fir[2] = weights[2] * val;
 110         fir[3] = weights[3] * val;
 111         fir[4] = weights[4] * val;
 112 
 113         val    = line[1];
 114         fir[1] = fir[2] + weights[1] * val;
 115         fir[2] = fir[3] + weights[2] * val;
 116         fir[3] = fir[4] + weights[3] * val;
 117         fir[4] =          weights[4] * val;
 118 
 119         for ( xx = 2; xx < width; xx++ )
 120         {
 121           val    = line[xx];
 122           fir[0] = fir[1] + weights[0] * val;
 123           fir[1] = fir[2] + weights[1] * val;
 124           fir[2] = fir[3] + weights[2] * val;
 125           fir[3] = fir[4] + weights[3] * val;
 126           fir[4] =          weights[4] * val;
 127 
 128           line[xx - 2] = FT_SHIFTCLAMP( fir[0] );
 129         }
 130 
 131         line[xx - 2] = FT_SHIFTCLAMP( fir[1] );
 132         line[xx - 1] = FT_SHIFTCLAMP( fir[2] );
 133       }
 134     }
 135 
 136     /* vertical in-place FIR filter */
 137     else if ( mode == FT_PIXEL_MODE_LCD_V && height >= 2 )
 138     {
 139       FT_Byte*  column = origin;
 140 
 141 
 142       for ( ; width > 0; width--, column++ )
 143       {
 144         FT_Byte*  col = column;
 145         FT_UInt   fir[5];
 146         FT_UInt   val, yy;
 147 
 148 
 149         val    = col[0];
 150         fir[2] = weights[2] * val;
 151         fir[3] = weights[3] * val;
 152         fir[4] = weights[4] * val;
 153         col   -= pitch;
 154 
 155         val    = col[0];
 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         col   -= pitch;
 161 
 162         for ( yy = 2; yy < height; yy++, col -= pitch )
 163         {
 164           val    = col[0];
 165           fir[0] = fir[1] + weights[0] * val;
 166           fir[1] = fir[2] + weights[1] * val;
 167           fir[2] = fir[3] + weights[2] * val;
 168           fir[3] = fir[4] + weights[3] * val;
 169           fir[4] =          weights[4] * val;
 170 
 171           col[pitch * 2]  = FT_SHIFTCLAMP( fir[0] );
 172         }
 173 
 174         col[pitch * 2]  = FT_SHIFTCLAMP( fir[1] );
 175         col[pitch]      = FT_SHIFTCLAMP( fir[2] );
 176       }
 177     }
 178   }
 179 
 180 
 181 #ifdef USE_LEGACY
 182 
 183   /* intra-pixel filter used by the legacy filter */
 184   static void
 185   _ft_lcd_filter_legacy( FT_Bitmap*      bitmap,
 186                          FT_Byte*        weights )
 187   {
 188     FT_UInt   width  = (FT_UInt)bitmap->width;
 189     FT_UInt   height = (FT_UInt)bitmap->rows;
 190     FT_Int    pitch  = bitmap->pitch;
 191     FT_Byte*  origin = bitmap->buffer;
 192     FT_Byte   mode   = bitmap->pixel_mode;
 193 
 194     static const unsigned int  filters[3][3] =
 195     {
 196       { 65538 * 9/13, 65538 * 1/6, 65538 * 1/13 },
 197       { 65538 * 3/13, 65538 * 4/6, 65538 * 3/13 },
 198       { 65538 * 1/13, 65538 * 1/6, 65538 * 9/13 }
 199     };
 200 
 201     FT_UNUSED( weights );
 202 
 203 
 204     /* take care of bitmap flow */
 205     if ( pitch > 0 && height > 0 )
 206       origin += pitch * (FT_Int)( height - 1 );
 207 
 208     /* horizontal in-place intra-pixel filter */
 209     if ( mode == FT_PIXEL_MODE_LCD && width >= 3 )
 210     {
 211       FT_Byte*  line = origin;
 212 
 213 
 214       for ( ; height > 0; height--, line -= pitch )
 215       {
 216         FT_UInt  xx;
 217 
 218 
 219         for ( xx = 0; xx < width; xx += 3 )
 220         {
 221           FT_UInt  r, g, b;
 222           FT_UInt  p;
 223 
 224 
 225           p  = line[xx];
 226           r  = filters[0][0] * p;
 227           g  = filters[0][1] * p;
 228           b  = filters[0][2] * p;
 229 
 230           p  = line[xx + 1];
 231           r += filters[1][0] * p;
 232           g += filters[1][1] * p;
 233           b += filters[1][2] * p;
 234 
 235           p  = line[xx + 2];
 236           r += filters[2][0] * p;
 237           g += filters[2][1] * p;
 238           b += filters[2][2] * p;
 239 
 240           line[xx]     = (FT_Byte)( r / 65536 );
 241           line[xx + 1] = (FT_Byte)( g / 65536 );
 242           line[xx + 2] = (FT_Byte)( b / 65536 );
 243         }
 244       }
 245     }
 246     else if ( mode == FT_PIXEL_MODE_LCD_V && height >= 3 )
 247     {
 248       FT_Byte*  column = origin;
 249 
 250 
 251       for ( ; width > 0; width--, column++ )
 252       {
 253         FT_Byte*  col = column - 2 * pitch;
 254 
 255 
 256         for ( ; height > 0; height -= 3, col -= 3 * pitch )
 257         {
 258           FT_UInt  r, g, b;
 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[pitch * 2] = (FT_Byte)( b / 65536 );
 280         }
 281       }
 282     }
 283   }
 284 
 285 #endif /* USE_LEGACY */
 286 
 287 
 288   /* documentation in ftlcdfil.h */
 289 
 290   FT_EXPORT_DEF( FT_Error )
 291   FT_Library_SetLcdFilterWeights( FT_Library      library,
 292                                   unsigned char  *weights )
 293   {
 294     if ( !library )
 295       return FT_THROW( Invalid_Library_Handle );
 296 
 297     if ( !weights )
 298       return FT_THROW( Invalid_Argument );
 299 
 300     ft_memcpy( library->lcd_weights, weights, FT_LCD_FILTER_FIVE_TAPS );
 301     library->lcd_filter_func = ft_lcd_filter_fir;
 302 
 303     return FT_Err_Ok;
 304   }
 305 
 306 
 307   /* documentation in ftlcdfil.h */
 308 
 309   FT_EXPORT_DEF( FT_Error )
 310   FT_Library_SetLcdFilter( FT_Library    library,
 311                            FT_LcdFilter  filter )
 312   {
 313     static const FT_LcdFiveTapFilter  default_weights =
 314                    { 0x08, 0x4d, 0x56, 0x4d, 0x08 };
 315     static const FT_LcdFiveTapFilter  light_weights =
 316                    { 0x00, 0x55, 0x56, 0x55, 0x00 };
 317 
 318 
 319     if ( !library )
 320       return FT_THROW( Invalid_Library_Handle );
 321 
 322     switch ( filter )
 323     {
 324     case FT_LCD_FILTER_NONE:
 325       library->lcd_filter_func = NULL;
 326       break;
 327 
 328     case FT_LCD_FILTER_DEFAULT:
 329       ft_memcpy( library->lcd_weights,
 330                  default_weights,
 331                  FT_LCD_FILTER_FIVE_TAPS );
 332       library->lcd_filter_func = ft_lcd_filter_fir;
 333       break;
 334 
 335     case FT_LCD_FILTER_LIGHT:
 336       ft_memcpy( library->lcd_weights,
 337                  light_weights,
 338                  FT_LCD_FILTER_FIVE_TAPS );
 339       library->lcd_filter_func = ft_lcd_filter_fir;
 340       break;
 341 
 342 #ifdef USE_LEGACY
 343 
 344     case FT_LCD_FILTER_LEGACY:
 345     case FT_LCD_FILTER_LEGACY1:
 346       library->lcd_filter_func = _ft_lcd_filter_legacy;
 347       break;
 348 
 349 #endif
 350 
 351     default:
 352       return FT_THROW( Invalid_Argument );
 353     }
 354 
 355     return FT_Err_Ok;
 356   }
 357 
 358 
 359   FT_EXPORT_DEF( FT_Error )
 360   FT_Library_SetLcdGeometry( FT_Library  library,
 361                              FT_Vector*  sub )
 362   {
 363     FT_UNUSED( library );
 364     FT_UNUSED( sub );
 365 
 366     return FT_THROW( Unimplemented_Feature );
 367   }
 368 
 369 #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
 370 
 371   /* add padding to accommodate outline shifts */
 372   FT_BASE_DEF (void)
 373   ft_lcd_padding( FT_BBox*        cbox,
 374                   FT_GlyphSlot    slot,
 375                   FT_Render_Mode  mode )
 376   {
 377     FT_Vector*  sub = slot->library->lcd_geometry;
 378 
 379     if ( mode == FT_RENDER_MODE_LCD )
 380     {
 381       cbox->xMin -= FT_MAX( FT_MAX( sub[0].x, sub[1].x ), sub[2].x );
 382       cbox->xMax -= FT_MIN( FT_MIN( sub[0].x, sub[1].x ), sub[2].x );
 383       cbox->yMin -= FT_MAX( FT_MAX( sub[0].y, sub[1].y ), sub[2].y );
 384       cbox->yMax -= FT_MIN( FT_MIN( sub[0].y, sub[1].y ), sub[2].y );
 385     }
 386     else if ( mode == FT_RENDER_MODE_LCD_V )
 387     {
 388       cbox->xMin -= FT_MAX( FT_MAX( sub[0].y, sub[1].y ), sub[2].y );
 389       cbox->xMax -= FT_MIN( FT_MIN( sub[0].y, sub[1].y ), sub[2].y );
 390       cbox->yMin += FT_MIN( FT_MIN( sub[0].x, sub[1].x ), sub[2].x );
 391       cbox->yMax += FT_MAX( FT_MAX( sub[0].x, sub[1].x ), sub[2].x );
 392     }
 393   }
 394 
 395 
 396   FT_EXPORT_DEF( FT_Error )
 397   FT_Library_SetLcdFilterWeights( FT_Library      library,
 398                                   unsigned char  *weights )
 399   {
 400     FT_UNUSED( library );
 401     FT_UNUSED( weights );
 402 
 403     return FT_THROW( Unimplemented_Feature );
 404   }
 405 
 406 
 407   FT_EXPORT_DEF( FT_Error )
 408   FT_Library_SetLcdFilter( FT_Library    library,
 409                            FT_LcdFilter  filter )
 410   {
 411     FT_UNUSED( library );
 412     FT_UNUSED( filter );
 413 
 414     return FT_THROW( Unimplemented_Feature );
 415   }
 416 
 417 
 418   /* documentation in ftlcdfil.h */
 419 
 420   FT_EXPORT_DEF( FT_Error )
 421   FT_Library_SetLcdGeometry( FT_Library  library,
 422                              FT_Vector   sub[3] )
 423   {
 424     if ( !library )
 425       return FT_THROW( Invalid_Library_Handle );
 426 
 427     if ( !sub )
 428       return FT_THROW( Invalid_Argument );
 429 
 430     ft_memcpy( library->lcd_geometry, sub, 3 * sizeof( FT_Vector ) );
 431 
 432     return FT_THROW( Unimplemented_Feature );
 433   }
 434 
 435 #endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
 436 
 437 
 438 /* END */