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