1/***************************************************************************/ 2/* */ 3/* ftlcdfil.c */ 4/* */ 5/* FreeType API for color filtering of subpixel bitmap glyphs (body). */ 6/* */ 7/* Copyright 2006, 2008-2010, 2013, 2014 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 /* FIR filter used by the default and light filters */ 33 static void 34 _ft_lcd_filter_fir( FT_Bitmap* bitmap, 35 FT_Render_Mode mode, 36 FT_Library library ) 37 { 38 FT_Byte* weights = library->lcd_weights; 39 FT_UInt width = (FT_UInt)bitmap->width; 40 FT_UInt height = (FT_UInt)bitmap->rows; 41 42 43 /* horizontal in-place FIR filter */ 44 if ( mode == FT_RENDER_MODE_LCD && width >= 4 ) 45 { 46 FT_Byte* line = bitmap->buffer; 47 48 49 /* `fir' and `pix' must be at least 32 bit wide, since the sum of */ 50 /* the values in `weights' can exceed 0xFF */ 51 52 for ( ; height > 0; height--, line += bitmap->pitch ) 53 { 54 FT_UInt fir[4]; /* below, `pix' is used as the 5th element */ 55 FT_UInt val1, xx; 56 57 58 val1 = line[0]; 59 fir[0] = weights[2] * val1; 60 fir[1] = weights[3] * val1; 61 fir[2] = weights[4] * val1; 62 fir[3] = 0; 63 64 val1 = line[1]; 65 fir[0] += weights[1] * val1; 66 fir[1] += weights[2] * val1; 67 fir[2] += weights[3] * val1; 68 fir[3] += weights[4] * val1; 69 70 for ( xx = 2; xx < width; xx++ ) 71 { 72 FT_UInt val, pix; 73 74 75 val = line[xx]; 76 pix = fir[0] + weights[0] * val; 77 fir[0] = fir[1] + weights[1] * val; 78 fir[1] = fir[2] + weights[2] * val; 79 fir[2] = fir[3] + weights[3] * val; 80 fir[3] = weights[4] * val; 81 82 pix >>= 8; 83 pix |= (FT_UInt)-(FT_Int)( pix >> 8 ); 84 line[xx - 2] = (FT_Byte)pix; 85 } 86 87 { 88 FT_UInt pix; 89 90 91 pix = fir[0] >> 8; 92 pix |= (FT_UInt)-(FT_Int)( pix >> 8 ); 93 line[xx - 2] = (FT_Byte)pix; 94 95 pix = fir[1] >> 8; 96 pix |= (FT_UInt)-(FT_Int)( pix >> 8 ); 97 line[xx - 1] = (FT_Byte)pix; 98 } 99 } 100 } 101 102 /* vertical in-place FIR filter */ 103 else if ( mode == FT_RENDER_MODE_LCD_V && height >= 4 ) 104 { 105 FT_Byte* column = bitmap->buffer; 106 FT_Int pitch = bitmap->pitch; 107 108 109 for ( ; width > 0; width--, column++ ) 110 { 111 FT_Byte* col = column; 112 FT_UInt fir[4]; /* below, `pix' is used as the 5th element */ 113 FT_UInt val1, yy; 114 115 116 val1 = col[0]; 117 fir[0] = weights[2] * val1; 118 fir[1] = weights[3] * val1; 119 fir[2] = weights[4] * val1; 120 fir[3] = 0; 121 col += pitch; 122 123 val1 = col[0]; 124 fir[0] += weights[1] * val1; 125 fir[1] += weights[2] * val1; 126 fir[2] += weights[3] * val1; 127 fir[3] += weights[4] * val1; 128 col += pitch; 129 130 for ( yy = 2; yy < height; yy++ ) 131 { 132 FT_UInt val, pix; 133 134 135 val = col[0]; 136 pix = fir[0] + weights[0] * val; 137 fir[0] = fir[1] + weights[1] * val; 138 fir[1] = fir[2] + weights[2] * val; 139 fir[2] = fir[3] + weights[3] * val; 140 fir[3] = weights[4] * val; 141 142 pix >>= 8; 143 pix |= (FT_UInt)-(FT_Int)( pix >> 8 ); 144 col[-2 * pitch] = (FT_Byte)pix; 145 col += pitch; 146 } 147 148 { 149 FT_UInt pix; 150 151 152 pix = fir[0] >> 8; 153 pix |= (FT_UInt)-(FT_Int)( pix >> 8 ); 154 col[-2 * pitch] = (FT_Byte)pix; 155 156 pix = fir[1] >> 8; 157 pix |= (FT_UInt)-(FT_Int)( pix >> 8 ); 158 col[-pitch] = (FT_Byte)pix; 159 } 160 } 161 } 162 } 163 164 165#ifdef USE_LEGACY 166 167 /* intra-pixel filter used by the legacy filter */ 168 static void 169 _ft_lcd_filter_legacy( FT_Bitmap* bitmap, 170 FT_Render_Mode mode, 171 FT_Library library ) 172 { 173 FT_UInt width = (FT_UInt)bitmap->width; 174 FT_UInt height = (FT_UInt)bitmap->rows; 175 FT_Int pitch = bitmap->pitch; 176 177 static const int filters[3][3] = 178 { 179 { 65538 * 9/13, 65538 * 1/6, 65538 * 1/13 }, 180 { 65538 * 3/13, 65538 * 4/6, 65538 * 3/13 }, 181 { 65538 * 1/13, 65538 * 1/6, 65538 * 9/13 } 182 }; 183 184 FT_UNUSED( library ); 185 186 187 /* horizontal in-place intra-pixel filter */ 188 if ( mode == FT_RENDER_MODE_LCD && width >= 3 ) 189 { 190 FT_Byte* line = bitmap->buffer; 191 192 193 for ( ; height > 0; height--, line += pitch ) 194 { 195 FT_UInt xx; 196 197 198 for ( xx = 0; xx < width; xx += 3 ) 199 { 200 FT_UInt r = 0; 201 FT_UInt g = 0; 202 FT_UInt b = 0; 203 FT_UInt p; 204 205 206 p = line[xx]; 207 r += filters[0][0] * p; 208 g += filters[0][1] * p; 209 b += filters[0][2] * p; 210 211 p = line[xx + 1]; 212 r += filters[1][0] * p; 213 g += filters[1][1] * p; 214 b += filters[1][2] * p; 215 216 p = line[xx + 2]; 217 r += filters[2][0] * p; 218 g += filters[2][1] * p; 219 b += filters[2][2] * p; 220 221 line[xx] = (FT_Byte)( r / 65536 ); 222 line[xx + 1] = (FT_Byte)( g / 65536 ); 223 line[xx + 2] = (FT_Byte)( b / 65536 ); 224 } 225 } 226 } 227 else if ( mode == FT_RENDER_MODE_LCD_V && height >= 3 ) 228 { 229 FT_Byte* column = bitmap->buffer; 230 231 232 for ( ; width > 0; width--, column++ ) 233 { 234 FT_Byte* col = column; 235 FT_Byte* col_end = col + height * pitch; 236 237 238 for ( ; col < col_end; col += 3 * pitch ) 239 { 240 FT_UInt r = 0; 241 FT_UInt g = 0; 242 FT_UInt b = 0; 243 FT_UInt p; 244 245 246 p = col[0]; 247 r += filters[0][0] * p; 248 g += filters[0][1] * p; 249 b += filters[0][2] * p; 250 251 p = col[pitch]; 252 r += filters[1][0] * p; 253 g += filters[1][1] * p; 254 b += filters[1][2] * p; 255 256 p = col[pitch * 2]; 257 r += filters[2][0] * p; 258 g += filters[2][1] * p; 259 b += filters[2][2] * p; 260 261 col[0] = (FT_Byte)( r / 65536 ); 262 col[pitch] = (FT_Byte)( g / 65536 ); 263 col[2 * pitch] = (FT_Byte)( b / 65536 ); 264 } 265 } 266 } 267 } 268 269#endif /* USE_LEGACY */ 270 271 272 FT_EXPORT_DEF( FT_Error ) 273 FT_Library_SetLcdFilterWeights( FT_Library library, 274 unsigned char *weights ) 275 { 276 if ( !library || !weights ) 277 return FT_THROW( Invalid_Argument ); 278 279 ft_memcpy( library->lcd_weights, weights, 5 ); 280 281 return FT_Err_Ok; 282 } 283 284 285 FT_EXPORT_DEF( FT_Error ) 286 FT_Library_SetLcdFilter( FT_Library library, 287 FT_LcdFilter filter ) 288 { 289 static const FT_Byte light_filter[5] = 290 { 0x00, 0x55, 0x56, 0x55, 0x00 }; 291 /* the values here sum up to a value larger than 256, */ 292 /* providing a cheap gamma correction */ 293 static const FT_Byte default_filter[5] = 294 { 0x10, 0x40, 0x70, 0x40, 0x10 }; 295 296 297 if ( !library ) 298 return FT_THROW( Invalid_Argument ); 299 300 switch ( filter ) 301 { 302 case FT_LCD_FILTER_NONE: 303 library->lcd_filter_func = NULL; 304 library->lcd_extra = 0; 305 break; 306 307 case FT_LCD_FILTER_DEFAULT: 308#if defined( FT_FORCE_LEGACY_LCD_FILTER ) 309 310 library->lcd_filter_func = _ft_lcd_filter_legacy; 311 library->lcd_extra = 0; 312 313#elif defined( FT_FORCE_LIGHT_LCD_FILTER ) 314 315 ft_memcpy( library->lcd_weights, light_filter, 5 ); 316 library->lcd_filter_func = _ft_lcd_filter_fir; 317 library->lcd_extra = 2; 318 319#else 320 321 ft_memcpy( library->lcd_weights, default_filter, 5 ); 322 library->lcd_filter_func = _ft_lcd_filter_fir; 323 library->lcd_extra = 2; 324 325#endif 326 327 break; 328 329 case FT_LCD_FILTER_LIGHT: 330 ft_memcpy( library->lcd_weights, light_filter, 5 ); 331 library->lcd_filter_func = _ft_lcd_filter_fir; 332 library->lcd_extra = 2; 333 break; 334 335#ifdef USE_LEGACY 336 337 case FT_LCD_FILTER_LEGACY: 338 library->lcd_filter_func = _ft_lcd_filter_legacy; 339 library->lcd_extra = 0; 340 break; 341 342#endif 343 344 default: 345 return FT_THROW( Invalid_Argument ); 346 } 347 348 library->lcd_filter = filter; 349 350 return FT_Err_Ok; 351 } 352 353#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ 354 355 FT_EXPORT_DEF( FT_Error ) 356 FT_Library_SetLcdFilterWeights( FT_Library library, 357 unsigned char *weights ) 358 { 359 FT_UNUSED( library ); 360 FT_UNUSED( weights ); 361 362 return FT_THROW( Unimplemented_Feature ); 363 } 364 365 366 FT_EXPORT_DEF( FT_Error ) 367 FT_Library_SetLcdFilter( FT_Library library, 368 FT_LcdFilter filter ) 369 { 370 FT_UNUSED( library ); 371 FT_UNUSED( filter ); 372 373 return FT_THROW( Unimplemented_Feature ); 374 } 375 376#endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ 377 378 379/* END */ 380