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) ft_lcd_padding(FT_BBox * cbox,FT_GlyphSlot slot,FT_Render_Mode mode)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 ) ft_lcd_filter_fir(FT_Bitmap * bitmap,FT_Render_Mode mode,FT_LcdFiveTapFilter weights)79 ft_lcd_filter_fir( FT_Bitmap* bitmap, 80 FT_Render_Mode mode, 81 FT_LcdFiveTapFilter weights ) 82 { 83 FT_UInt width = (FT_UInt)bitmap->width; 84 FT_UInt height = (FT_UInt)bitmap->rows; 85 FT_Int pitch = bitmap->pitch; 86 FT_Byte* origin = bitmap->buffer; 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_RENDER_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_RENDER_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 _ft_lcd_filter_legacy(FT_Bitmap * bitmap,FT_Render_Mode mode,FT_Byte * weights)185 _ft_lcd_filter_legacy( FT_Bitmap* bitmap, 186 FT_Render_Mode mode, 187 FT_Byte* weights ) 188 { 189 FT_UInt width = (FT_UInt)bitmap->width; 190 FT_UInt height = (FT_UInt)bitmap->rows; 191 FT_Int pitch = bitmap->pitch; 192 FT_Byte* origin = bitmap->buffer; 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_RENDER_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_RENDER_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 ) FT_Library_SetLcdFilterWeights(FT_Library library,unsigned char * weights)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 ) FT_Library_SetLcdFilter(FT_Library library,FT_LcdFilter filter)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 ) FT_Library_SetLcdGeometry(FT_Library library,FT_Vector * sub)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) ft_lcd_padding(FT_BBox * cbox,FT_GlyphSlot slot,FT_Render_Mode mode)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 ) FT_Library_SetLcdFilterWeights(FT_Library library,unsigned char * weights)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 ) FT_Library_SetLcdFilter(FT_Library library,FT_LcdFilter filter)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 ) FT_Library_SetLcdGeometry(FT_Library library,FT_Vector sub[3])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 */ 439