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