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