1 /**************************************************************************** 2 * 3 * ftsmooth.c 4 * 5 * Anti-aliasing renderer interface (body). 6 * 7 * Copyright (C) 2000-2020 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 #include <freetype/internal/ftobjs.h> 21 #include <freetype/ftoutln.h> 22 #include "ftsmooth.h" 23 #include "ftgrays.h" 24 25 #include "ftsmerrs.h" 26 27 28 /* sets render-specific mode */ 29 static FT_Error ft_smooth_set_mode(FT_Renderer render,FT_ULong mode_tag,FT_Pointer data)30 ft_smooth_set_mode( FT_Renderer render, 31 FT_ULong mode_tag, 32 FT_Pointer data ) 33 { 34 /* we simply pass it to the raster */ 35 return render->clazz->raster_class->raster_set_mode( render->raster, 36 mode_tag, 37 data ); 38 } 39 40 /* transform a given glyph image */ 41 static FT_Error ft_smooth_transform(FT_Renderer render,FT_GlyphSlot slot,const FT_Matrix * matrix,const FT_Vector * delta)42 ft_smooth_transform( FT_Renderer render, 43 FT_GlyphSlot slot, 44 const FT_Matrix* matrix, 45 const FT_Vector* delta ) 46 { 47 FT_Error error = FT_Err_Ok; 48 49 50 if ( slot->format != render->glyph_format ) 51 { 52 error = FT_THROW( Invalid_Argument ); 53 goto Exit; 54 } 55 56 if ( matrix ) 57 FT_Outline_Transform( &slot->outline, matrix ); 58 59 if ( delta ) 60 FT_Outline_Translate( &slot->outline, delta->x, delta->y ); 61 62 Exit: 63 return error; 64 } 65 66 67 /* return the glyph's control box */ 68 static void ft_smooth_get_cbox(FT_Renderer render,FT_GlyphSlot slot,FT_BBox * cbox)69 ft_smooth_get_cbox( FT_Renderer render, 70 FT_GlyphSlot slot, 71 FT_BBox* cbox ) 72 { 73 FT_ZERO( cbox ); 74 75 if ( slot->format == render->glyph_format ) 76 FT_Outline_Get_CBox( &slot->outline, cbox ); 77 } 78 79 typedef struct TOrigin_ 80 { 81 unsigned char* origin; /* pixmap origin at the bottom-left */ 82 int pitch; /* pitch to go down one row */ 83 84 } TOrigin; 85 86 #ifndef FT_CONFIG_OPTION_SUBPIXEL_RENDERING 87 88 /* initialize renderer -- init its raster */ 89 static FT_Error ft_smooth_init(FT_Renderer render)90 ft_smooth_init( FT_Renderer render ) 91 { 92 FT_Vector* sub = render->root.library->lcd_geometry; 93 94 95 /* set up default subpixel geometry for striped RGB panels. */ 96 sub[0].x = -21; 97 sub[0].y = 0; 98 sub[1].x = 0; 99 sub[1].y = 0; 100 sub[2].x = 21; 101 sub[2].y = 0; 102 103 render->clazz->raster_class->raster_reset( render->raster, NULL, 0 ); 104 105 return 0; 106 } 107 108 109 /* This function writes every third byte in direct rendering mode */ 110 static void ft_smooth_lcd_spans(int y,int count,const FT_Span * spans,TOrigin * target)111 ft_smooth_lcd_spans( int y, 112 int count, 113 const FT_Span* spans, 114 TOrigin* target ) 115 { 116 unsigned char* dst_line = target->origin - y * target->pitch; 117 unsigned char* dst; 118 unsigned short w; 119 120 121 for ( ; count--; spans++ ) 122 for ( dst = dst_line + spans->x * 3, w = spans->len; w--; dst += 3 ) 123 *dst = spans->coverage; 124 } 125 126 127 static FT_Error ft_smooth_raster_lcd(FT_Renderer render,FT_Outline * outline,FT_Bitmap * bitmap)128 ft_smooth_raster_lcd( FT_Renderer render, 129 FT_Outline* outline, 130 FT_Bitmap* bitmap ) 131 { 132 FT_Error error = FT_Err_Ok; 133 FT_Vector* sub = render->root.library->lcd_geometry; 134 FT_Pos x, y; 135 136 FT_Raster_Params params; 137 TOrigin target; 138 139 140 /* Render 3 separate coverage bitmaps, shifting the outline. */ 141 /* Set up direct rendering to record them on each third byte. */ 142 params.source = outline; 143 params.flags = FT_RASTER_FLAG_AA | FT_RASTER_FLAG_DIRECT; 144 params.gray_spans = (FT_SpanFunc)ft_smooth_lcd_spans; 145 params.user = ⌖ 146 147 params.clip_box.xMin = 0; 148 params.clip_box.yMin = 0; 149 params.clip_box.xMax = bitmap->width; 150 params.clip_box.yMax = bitmap->rows; 151 152 if ( bitmap->pitch < 0 ) 153 target.origin = bitmap->buffer; 154 else 155 target.origin = bitmap->buffer 156 + ( bitmap->rows - 1 ) * (unsigned int)bitmap->pitch; 157 158 target.pitch = bitmap->pitch; 159 160 FT_Outline_Translate( outline, 161 -sub[0].x, 162 -sub[0].y ); 163 error = render->raster_render( render->raster, ¶ms ); 164 x = sub[0].x; 165 y = sub[0].y; 166 if ( error ) 167 goto Exit; 168 169 target.origin++; 170 FT_Outline_Translate( outline, 171 sub[0].x - sub[1].x, 172 sub[0].y - sub[1].y ); 173 error = render->raster_render( render->raster, ¶ms ); 174 x = sub[1].x; 175 y = sub[1].y; 176 if ( error ) 177 goto Exit; 178 179 target.origin++; 180 FT_Outline_Translate( outline, 181 sub[1].x - sub[2].x, 182 sub[1].y - sub[2].y ); 183 error = render->raster_render( render->raster, ¶ms ); 184 x = sub[2].x; 185 y = sub[2].y; 186 187 Exit: 188 FT_Outline_Translate( outline, x, y ); 189 190 return error; 191 } 192 193 194 static FT_Error ft_smooth_raster_lcdv(FT_Renderer render,FT_Outline * outline,FT_Bitmap * bitmap)195 ft_smooth_raster_lcdv( FT_Renderer render, 196 FT_Outline* outline, 197 FT_Bitmap* bitmap ) 198 { 199 FT_Error error = FT_Err_Ok; 200 int pitch = bitmap->pitch; 201 FT_Vector* sub = render->root.library->lcd_geometry; 202 FT_Pos x, y; 203 204 FT_Raster_Params params; 205 206 207 params.target = bitmap; 208 params.source = outline; 209 params.flags = FT_RASTER_FLAG_AA; 210 211 /* Render 3 separate coverage bitmaps, shifting the outline. */ 212 /* Notice that the subpixel geometry vectors are rotated. */ 213 /* Triple the pitch to render on each third row. */ 214 bitmap->pitch *= 3; 215 bitmap->rows /= 3; 216 217 FT_Outline_Translate( outline, 218 -sub[0].y, 219 sub[0].x ); 220 error = render->raster_render( render->raster, ¶ms ); 221 x = sub[0].y; 222 y = -sub[0].x; 223 if ( error ) 224 goto Exit; 225 226 bitmap->buffer += pitch; 227 FT_Outline_Translate( outline, 228 sub[0].y - sub[1].y, 229 sub[1].x - sub[0].x ); 230 error = render->raster_render( render->raster, ¶ms ); 231 x = sub[1].y; 232 y = -sub[1].x; 233 bitmap->buffer -= pitch; 234 if ( error ) 235 goto Exit; 236 237 bitmap->buffer += 2 * pitch; 238 FT_Outline_Translate( outline, 239 sub[1].y - sub[2].y, 240 sub[2].x - sub[1].x ); 241 error = render->raster_render( render->raster, ¶ms ); 242 x = sub[2].y; 243 y = -sub[2].x; 244 bitmap->buffer -= 2 * pitch; 245 246 Exit: 247 FT_Outline_Translate( outline, x, y ); 248 249 bitmap->pitch /= 3; 250 bitmap->rows *= 3; 251 252 return error; 253 } 254 255 #else /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ 256 257 /* initialize renderer -- init its raster */ 258 static FT_Error ft_smooth_init(FT_Renderer render)259 ft_smooth_init( FT_Renderer render ) 260 { 261 /* set up default LCD filtering */ 262 FT_Library_SetLcdFilter( render->root.library, FT_LCD_FILTER_DEFAULT ); 263 264 render->clazz->raster_class->raster_reset( render->raster, NULL, 0 ); 265 266 return 0; 267 } 268 269 270 static FT_Error ft_smooth_raster_lcd(FT_Renderer render,FT_Outline * outline,FT_Bitmap * bitmap)271 ft_smooth_raster_lcd( FT_Renderer render, 272 FT_Outline* outline, 273 FT_Bitmap* bitmap ) 274 { 275 FT_Error error = FT_Err_Ok; 276 FT_Vector* points = outline->points; 277 FT_Vector* points_end = FT_OFFSET( points, outline->n_points ); 278 FT_Vector* vec; 279 280 FT_Raster_Params params; 281 282 283 params.target = bitmap; 284 params.source = outline; 285 params.flags = FT_RASTER_FLAG_AA; 286 287 /* implode outline */ 288 for ( vec = points; vec < points_end; vec++ ) 289 vec->x *= 3; 290 291 /* render outline into the bitmap */ 292 error = render->raster_render( render->raster, ¶ms ); 293 294 /* deflate outline */ 295 for ( vec = points; vec < points_end; vec++ ) 296 vec->x /= 3; 297 298 return error; 299 } 300 301 302 static FT_Error ft_smooth_raster_lcdv(FT_Renderer render,FT_Outline * outline,FT_Bitmap * bitmap)303 ft_smooth_raster_lcdv( FT_Renderer render, 304 FT_Outline* outline, 305 FT_Bitmap* bitmap ) 306 { 307 FT_Error error = FT_Err_Ok; 308 FT_Vector* points = outline->points; 309 FT_Vector* points_end = FT_OFFSET( points, outline->n_points ); 310 FT_Vector* vec; 311 312 FT_Raster_Params params; 313 314 315 params.target = bitmap; 316 params.source = outline; 317 params.flags = FT_RASTER_FLAG_AA; 318 319 /* implode outline */ 320 for ( vec = points; vec < points_end; vec++ ) 321 vec->y *= 3; 322 323 /* render outline into the bitmap */ 324 error = render->raster_render( render->raster, ¶ms ); 325 326 /* deflate outline */ 327 for ( vec = points; vec < points_end; vec++ ) 328 vec->y /= 3; 329 330 return error; 331 } 332 333 #endif /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ 334 335 /* Oversampling scale to be used in rendering overlaps */ 336 #define SCALE ( 1 << 2 ) 337 338 /* This function averages inflated spans in direct rendering mode */ 339 static void ft_smooth_overlap_spans(int y,int count,const FT_Span * spans,TOrigin * target)340 ft_smooth_overlap_spans( int y, 341 int count, 342 const FT_Span* spans, 343 TOrigin* target ) 344 { 345 unsigned char* dst = target->origin - ( y / SCALE ) * target->pitch; 346 unsigned short x; 347 unsigned int cover, sum; 348 349 350 /* When accumulating the oversampled spans we need to assure that */ 351 /* fully covered pixels are equal to 255 and do not overflow. */ 352 /* It is important that the SCALE is a power of 2, each subpixel */ 353 /* cover can also reach a power of 2 after rounding, and the total */ 354 /* is clamped to 255 when it adds up to 256. */ 355 for ( ; count--; spans++ ) 356 { 357 cover = ( spans->coverage + SCALE * SCALE / 2 ) / ( SCALE * SCALE ); 358 for ( x = 0; x < spans->len; x++ ) 359 { 360 sum = dst[( spans->x + x ) / SCALE] + cover; 361 dst[( spans->x + x ) / SCALE] = (unsigned char)( sum - ( sum >> 8 ) ); 362 } 363 } 364 } 365 366 367 static FT_Error ft_smooth_raster_overlap(FT_Renderer render,FT_Outline * outline,FT_Bitmap * bitmap)368 ft_smooth_raster_overlap( FT_Renderer render, 369 FT_Outline* outline, 370 FT_Bitmap* bitmap ) 371 { 372 FT_Error error = FT_Err_Ok; 373 FT_Vector* points = outline->points; 374 FT_Vector* points_end = FT_OFFSET( points, outline->n_points ); 375 FT_Vector* vec; 376 377 FT_Raster_Params params; 378 TOrigin target; 379 380 381 /* Reject outlines that are too wide for 16-bit FT_Span. */ 382 /* Other limits are applied upstream with the same error code. */ 383 if ( bitmap->width * SCALE > 0x7FFF ) 384 return FT_THROW( Raster_Overflow ); 385 386 /* Set up direct rendering to average oversampled spans. */ 387 params.source = outline; 388 params.flags = FT_RASTER_FLAG_AA | FT_RASTER_FLAG_DIRECT; 389 params.gray_spans = (FT_SpanFunc)ft_smooth_overlap_spans; 390 params.user = ⌖ 391 392 params.clip_box.xMin = 0; 393 params.clip_box.yMin = 0; 394 params.clip_box.xMax = bitmap->width * SCALE; 395 params.clip_box.yMax = bitmap->rows * SCALE; 396 397 if ( bitmap->pitch < 0 ) 398 target.origin = bitmap->buffer; 399 else 400 target.origin = bitmap->buffer 401 + ( bitmap->rows - 1 ) * (unsigned int)bitmap->pitch; 402 403 target.pitch = bitmap->pitch; 404 405 /* inflate outline */ 406 for ( vec = points; vec < points_end; vec++ ) 407 { 408 vec->x *= SCALE; 409 vec->y *= SCALE; 410 } 411 412 /* render outline into the bitmap */ 413 error = render->raster_render( render->raster, ¶ms ); 414 415 /* deflate outline */ 416 for ( vec = points; vec < points_end; vec++ ) 417 { 418 vec->x /= SCALE; 419 vec->y /= SCALE; 420 } 421 422 return error; 423 } 424 425 #undef SCALE 426 427 static FT_Error ft_smooth_render(FT_Renderer render,FT_GlyphSlot slot,FT_Render_Mode mode,const FT_Vector * origin)428 ft_smooth_render( FT_Renderer render, 429 FT_GlyphSlot slot, 430 FT_Render_Mode mode, 431 const FT_Vector* origin ) 432 { 433 FT_Error error = FT_Err_Ok; 434 FT_Outline* outline = &slot->outline; 435 FT_Bitmap* bitmap = &slot->bitmap; 436 FT_Memory memory = render->root.memory; 437 FT_Pos x_shift = 0; 438 FT_Pos y_shift = 0; 439 440 441 /* check glyph image format */ 442 if ( slot->format != render->glyph_format ) 443 { 444 error = FT_THROW( Invalid_Argument ); 445 goto Exit; 446 } 447 448 /* check mode */ 449 if ( mode != FT_RENDER_MODE_NORMAL && 450 mode != FT_RENDER_MODE_LIGHT && 451 mode != FT_RENDER_MODE_LCD && 452 mode != FT_RENDER_MODE_LCD_V ) 453 { 454 error = FT_THROW( Cannot_Render_Glyph ); 455 goto Exit; 456 } 457 458 /* release old bitmap buffer */ 459 if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) 460 { 461 FT_FREE( bitmap->buffer ); 462 slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP; 463 } 464 465 if ( ft_glyphslot_preset_bitmap( slot, mode, origin ) ) 466 { 467 error = FT_THROW( Raster_Overflow ); 468 goto Exit; 469 } 470 471 if ( !bitmap->rows || !bitmap->pitch ) 472 goto Exit; 473 474 /* allocate new one */ 475 if ( FT_ALLOC_MULT( bitmap->buffer, bitmap->rows, bitmap->pitch ) ) 476 goto Exit; 477 478 slot->internal->flags |= FT_GLYPH_OWN_BITMAP; 479 480 x_shift = 64 * -slot->bitmap_left; 481 y_shift = 64 * -slot->bitmap_top; 482 if ( bitmap->pixel_mode == FT_PIXEL_MODE_LCD_V ) 483 y_shift += 64 * (FT_Int)bitmap->rows / 3; 484 else 485 y_shift += 64 * (FT_Int)bitmap->rows; 486 487 if ( origin ) 488 { 489 x_shift += origin->x; 490 y_shift += origin->y; 491 } 492 493 /* translate outline to render it into the bitmap */ 494 if ( x_shift || y_shift ) 495 FT_Outline_Translate( outline, x_shift, y_shift ); 496 497 if ( mode == FT_RENDER_MODE_NORMAL || 498 mode == FT_RENDER_MODE_LIGHT ) 499 { 500 if ( outline->flags & FT_OUTLINE_OVERLAP ) 501 error = ft_smooth_raster_overlap( render, outline, bitmap ); 502 else 503 { 504 FT_Raster_Params params; 505 506 507 params.target = bitmap; 508 params.source = outline; 509 params.flags = FT_RASTER_FLAG_AA; 510 511 error = render->raster_render( render->raster, ¶ms ); 512 } 513 } 514 else 515 { 516 if ( mode == FT_RENDER_MODE_LCD ) 517 error = ft_smooth_raster_lcd ( render, outline, bitmap ); 518 else if ( mode == FT_RENDER_MODE_LCD_V ) 519 error = ft_smooth_raster_lcdv( render, outline, bitmap ); 520 521 #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING 522 523 /* finally apply filtering */ 524 { 525 FT_Byte* lcd_weights; 526 FT_Bitmap_LcdFilterFunc lcd_filter_func; 527 528 529 /* Per-face LCD filtering takes priority if set up. */ 530 if ( slot->face && slot->face->internal->lcd_filter_func ) 531 { 532 lcd_weights = slot->face->internal->lcd_weights; 533 lcd_filter_func = slot->face->internal->lcd_filter_func; 534 } 535 else 536 { 537 lcd_weights = slot->library->lcd_weights; 538 lcd_filter_func = slot->library->lcd_filter_func; 539 } 540 541 if ( lcd_filter_func ) 542 lcd_filter_func( bitmap, lcd_weights ); 543 } 544 545 #endif /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ 546 547 } 548 549 Exit: 550 if ( !error ) 551 { 552 /* everything is fine; the glyph is now officially a bitmap */ 553 slot->format = FT_GLYPH_FORMAT_BITMAP; 554 } 555 else if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) 556 { 557 FT_FREE( bitmap->buffer ); 558 slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP; 559 } 560 561 if ( x_shift || y_shift ) 562 FT_Outline_Translate( outline, -x_shift, -y_shift ); 563 564 return error; 565 } 566 567 568 FT_DEFINE_RENDERER( 569 ft_smooth_renderer_class, 570 571 FT_MODULE_RENDERER, 572 sizeof ( FT_RendererRec ), 573 574 "smooth", 575 0x10000L, 576 0x20000L, 577 578 NULL, /* module specific interface */ 579 580 (FT_Module_Constructor)ft_smooth_init, /* module_init */ 581 (FT_Module_Destructor) NULL, /* module_done */ 582 (FT_Module_Requester) NULL, /* get_interface */ 583 584 FT_GLYPH_FORMAT_OUTLINE, 585 586 (FT_Renderer_RenderFunc) ft_smooth_render, /* render_glyph */ 587 (FT_Renderer_TransformFunc)ft_smooth_transform, /* transform_glyph */ 588 (FT_Renderer_GetCBoxFunc) ft_smooth_get_cbox, /* get_glyph_cbox */ 589 (FT_Renderer_SetModeFunc) ft_smooth_set_mode, /* set_mode */ 590 591 (FT_Raster_Funcs*)&ft_grays_raster /* raster_class */ 592 ) 593 594 595 /* END */ 596