1 /**************************************************************************** 2 * 3 * ftgrays.c 4 * 5 * A new `perfect' anti-aliasing renderer (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 * 20 * This file can be compiled without the rest of the FreeType engine, by 21 * defining the STANDALONE_ macro when compiling it. You also need to 22 * put the files `ftgrays.h' and `ftimage.h' into the current 23 * compilation directory. Typically, you could do something like 24 * 25 * - copy `src/smooth/ftgrays.c' (this file) to your current directory 26 * 27 * - copy `include/freetype/ftimage.h' and `src/smooth/ftgrays.h' to the 28 * same directory 29 * 30 * - compile `ftgrays' with the STANDALONE_ macro defined, as in 31 * 32 * cc -c -DSTANDALONE_ ftgrays.c 33 * 34 * The renderer can be initialized with a call to 35 * `ft_gray_raster.raster_new'; an anti-aliased bitmap can be generated 36 * with a call to `ft_gray_raster.raster_render'. 37 * 38 * See the comments and documentation in the file `ftimage.h' for more 39 * details on how the raster works. 40 * 41 */ 42 43 /************************************************************************** 44 * 45 * This is a new anti-aliasing scan-converter for FreeType 2. The 46 * algorithm used here is _very_ different from the one in the standard 47 * `ftraster' module. Actually, `ftgrays' computes the _exact_ 48 * coverage of the outline on each pixel cell by straight segments. 49 * 50 * It is based on ideas that I initially found in Raph Levien's 51 * excellent LibArt graphics library (see https://www.levien.com/libart 52 * for more information, though the web pages do not tell anything 53 * about the renderer; you'll have to dive into the source code to 54 * understand how it works). 55 * 56 * Note, however, that this is a _very_ different implementation 57 * compared to Raph's. Coverage information is stored in a very 58 * different way, and I don't use sorted vector paths. Also, it doesn't 59 * use floating point values. 60 * 61 * Bézier segments are flattened by splitting them until their deviation 62 * from straight line becomes much smaller than a pixel. Therefore, the 63 * pixel coverage by a Bézier curve is calculated approximately. To 64 * estimate the deviation, we use the distance from the control point 65 * to the conic chord centre or the cubic chord trisection. These 66 * distances vanish fast after each split. In the conic case, they vanish 67 * predictably and the number of necessary splits can be calculated. 68 * 69 * This renderer has the following advantages: 70 * 71 * - It doesn't need an intermediate bitmap. Instead, one can supply a 72 * callback function that will be called by the renderer to draw gray 73 * spans on any target surface. You can thus do direct composition on 74 * any kind of bitmap, provided that you give the renderer the right 75 * callback. 76 * 77 * - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on 78 * each pixel cell by straight segments. 79 * 80 * - It performs a single pass on the outline (the `standard' FT2 81 * renderer makes two passes). 82 * 83 * - It can easily be modified to render to _any_ number of gray levels 84 * cheaply. 85 * 86 * - For small (< 80) pixel sizes, it is faster than the standard 87 * renderer. 88 * 89 */ 90 91 92 /************************************************************************** 93 * 94 * The macro FT_COMPONENT is used in trace mode. It is an implicit 95 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log 96 * messages during execution. 97 */ 98 #undef FT_COMPONENT 99 #define FT_COMPONENT smooth 100 101 102 #ifdef STANDALONE_ 103 104 105 /* The size in bytes of the render pool used by the scan-line converter */ 106 /* to do all of its work. */ 107 #define FT_RENDER_POOL_SIZE 16384L 108 109 110 /* Auxiliary macros for token concatenation. */ 111 #define FT_ERR_XCAT( x, y ) x ## y 112 #define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y ) 113 114 #define FT_BEGIN_STMNT do { 115 #define FT_END_STMNT } while ( 0 ) 116 117 #define FT_MIN( a, b ) ( (a) < (b) ? (a) : (b) ) 118 #define FT_MAX( a, b ) ( (a) > (b) ? (a) : (b) ) 119 #define FT_ABS( a ) ( (a) < 0 ? -(a) : (a) ) 120 121 122 /* 123 * Approximate sqrt(x*x+y*y) using the `alpha max plus beta min' 124 * algorithm. We use alpha = 1, beta = 3/8, giving us results with a 125 * largest error less than 7% compared to the exact value. 126 */ 127 #define FT_HYPOT( x, y ) \ 128 ( x = FT_ABS( x ), \ 129 y = FT_ABS( y ), \ 130 x > y ? x + ( 3 * y >> 3 ) \ 131 : y + ( 3 * x >> 3 ) ) 132 133 134 /* define this to dump debugging information */ 135 /* #define FT_DEBUG_LEVEL_TRACE */ 136 137 138 #ifdef FT_DEBUG_LEVEL_TRACE 139 #include <stdio.h> 140 #include <stdarg.h> 141 #endif 142 143 #include <stddef.h> 144 #include <string.h> 145 #include <setjmp.h> 146 #include <limits.h> 147 #define FT_CHAR_BIT CHAR_BIT 148 #define FT_UINT_MAX UINT_MAX 149 #define FT_INT_MAX INT_MAX 150 #define FT_ULONG_MAX ULONG_MAX 151 152 #define ADD_LONG( a, b ) \ 153 (long)( (unsigned long)(a) + (unsigned long)(b) ) 154 #define SUB_LONG( a, b ) \ 155 (long)( (unsigned long)(a) - (unsigned long)(b) ) 156 #define MUL_LONG( a, b ) \ 157 (long)( (unsigned long)(a) * (unsigned long)(b) ) 158 #define NEG_LONG( a ) \ 159 (long)( -(unsigned long)(a) ) 160 161 162 #define ft_memset memset 163 164 #define ft_setjmp setjmp 165 #define ft_longjmp longjmp 166 #define ft_jmp_buf jmp_buf 167 168 typedef ptrdiff_t FT_PtrDist; 169 170 171 #define ErrRaster_Invalid_Mode -2 172 #define ErrRaster_Invalid_Outline -1 173 #define ErrRaster_Invalid_Argument -3 174 #define ErrRaster_Memory_Overflow -4 175 176 #define FT_BEGIN_HEADER 177 #define FT_END_HEADER 178 179 #include "ftimage.h" 180 #include "ftgrays.h" 181 182 183 /* This macro is used to indicate that a function parameter is unused. */ 184 /* Its purpose is simply to reduce compiler warnings. Note also that */ 185 /* simply defining it as `(void)x' doesn't avoid warnings with certain */ 186 /* ANSI compilers (e.g. LCC). */ 187 #define FT_UNUSED( x ) (x) = (x) 188 189 190 /* we only use level 5 & 7 tracing messages; cf. ftdebug.h */ 191 192 #ifdef FT_DEBUG_LEVEL_TRACE 193 194 void FT_Message(const char * fmt,...)195 FT_Message( const char* fmt, 196 ... ) 197 { 198 va_list ap; 199 200 201 va_start( ap, fmt ); 202 vfprintf( stderr, fmt, ap ); 203 va_end( ap ); 204 } 205 206 207 /* empty function useful for setting a breakpoint to catch errors */ 208 int FT_Throw(int error,int line,const char * file)209 FT_Throw( int error, 210 int line, 211 const char* file ) 212 { 213 FT_UNUSED( error ); 214 FT_UNUSED( line ); 215 FT_UNUSED( file ); 216 217 return 0; 218 } 219 220 221 /* we don't handle tracing levels in stand-alone mode; */ 222 #ifndef FT_TRACE5 223 #define FT_TRACE5( varformat ) FT_Message varformat 224 #endif 225 #ifndef FT_TRACE7 226 #define FT_TRACE7( varformat ) FT_Message varformat 227 #endif 228 #ifndef FT_ERROR 229 #define FT_ERROR( varformat ) FT_Message varformat 230 #endif 231 232 #define FT_THROW( e ) \ 233 ( FT_Throw( FT_ERR_CAT( ErrRaster_, e ), \ 234 __LINE__, \ 235 __FILE__ ) | \ 236 FT_ERR_CAT( ErrRaster_, e ) ) 237 238 #else /* !FT_DEBUG_LEVEL_TRACE */ 239 240 #define FT_TRACE5( x ) do { } while ( 0 ) /* nothing */ 241 #define FT_TRACE7( x ) do { } while ( 0 ) /* nothing */ 242 #define FT_ERROR( x ) do { } while ( 0 ) /* nothing */ 243 #define FT_THROW( e ) FT_ERR_CAT( ErrRaster_, e ) 244 245 246 #endif /* !FT_DEBUG_LEVEL_TRACE */ 247 248 249 #define FT_DEFINE_OUTLINE_FUNCS( class_, \ 250 move_to_, line_to_, \ 251 conic_to_, cubic_to_, \ 252 shift_, delta_ ) \ 253 static const FT_Outline_Funcs class_ = \ 254 { \ 255 move_to_, \ 256 line_to_, \ 257 conic_to_, \ 258 cubic_to_, \ 259 shift_, \ 260 delta_ \ 261 }; 262 263 #define FT_DEFINE_RASTER_FUNCS( class_, glyph_format_, \ 264 raster_new_, raster_reset_, \ 265 raster_set_mode_, raster_render_, \ 266 raster_done_ ) \ 267 const FT_Raster_Funcs class_ = \ 268 { \ 269 glyph_format_, \ 270 raster_new_, \ 271 raster_reset_, \ 272 raster_set_mode_, \ 273 raster_render_, \ 274 raster_done_ \ 275 }; 276 277 278 #else /* !STANDALONE_ */ 279 280 281 #include "ftgrays.h" 282 #include <freetype/internal/ftobjs.h> 283 #include <freetype/internal/ftdebug.h> 284 #include <freetype/internal/ftcalc.h> 285 #include <freetype/ftoutln.h> 286 287 #include "ftsmerrs.h" 288 289 #define Smooth_Err_Invalid_Mode Smooth_Err_Cannot_Render_Glyph 290 #define Smooth_Err_Memory_Overflow Smooth_Err_Out_Of_Memory 291 #define ErrRaster_Memory_Overflow Smooth_Err_Out_Of_Memory 292 293 294 #endif /* !STANDALONE_ */ 295 296 297 #ifndef FT_MEM_SET 298 #define FT_MEM_SET( d, s, c ) ft_memset( d, s, c ) 299 #endif 300 301 #ifndef FT_MEM_ZERO 302 #define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count ) 303 #endif 304 305 #ifndef FT_ZERO 306 #define FT_ZERO( p ) FT_MEM_ZERO( p, sizeof ( *(p) ) ) 307 #endif 308 309 /* as usual, for the speed hungry :-) */ 310 311 #undef RAS_ARG 312 #undef RAS_ARG_ 313 #undef RAS_VAR 314 #undef RAS_VAR_ 315 316 #ifndef FT_STATIC_RASTER 317 318 #define RAS_ARG gray_PWorker worker 319 #define RAS_ARG_ gray_PWorker worker, 320 321 #define RAS_VAR worker 322 #define RAS_VAR_ worker, 323 324 #else /* FT_STATIC_RASTER */ 325 326 #define RAS_ARG void 327 #define RAS_ARG_ /* empty */ 328 #define RAS_VAR /* empty */ 329 #define RAS_VAR_ /* empty */ 330 331 #endif /* FT_STATIC_RASTER */ 332 333 334 /* must be at least 6 bits! */ 335 #define PIXEL_BITS 8 336 337 #define ONE_PIXEL ( 1 << PIXEL_BITS ) 338 #define TRUNC( x ) (TCoord)( (x) >> PIXEL_BITS ) 339 #define FRACT( x ) (TCoord)( (x) & ( ONE_PIXEL - 1 ) ) 340 341 #if PIXEL_BITS >= 6 342 #define UPSCALE( x ) ( (x) * ( ONE_PIXEL >> 6 ) ) 343 #define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) ) 344 #else 345 #define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) ) 346 #define DOWNSCALE( x ) ( (x) * ( 64 >> PIXEL_BITS ) ) 347 #endif 348 349 350 /* Compute `dividend / divisor' and return both its quotient and */ 351 /* remainder, cast to a specific type. This macro also ensures that */ 352 /* the remainder is always positive. We use the remainder to keep */ 353 /* track of accumulating errors and compensate for them. */ 354 #define FT_DIV_MOD( type, dividend, divisor, quotient, remainder ) \ 355 FT_BEGIN_STMNT \ 356 (quotient) = (type)( (dividend) / (divisor) ); \ 357 (remainder) = (type)( (dividend) % (divisor) ); \ 358 if ( (remainder) < 0 ) \ 359 { \ 360 (quotient)--; \ 361 (remainder) += (type)(divisor); \ 362 } \ 363 FT_END_STMNT 364 365 #ifdef __arm__ 366 /* Work around a bug specific to GCC which make the compiler fail to */ 367 /* optimize a division and modulo operation on the same parameters */ 368 /* into a single call to `__aeabi_idivmod'. See */ 369 /* */ 370 /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=43721 */ 371 #undef FT_DIV_MOD 372 #define FT_DIV_MOD( type, dividend, divisor, quotient, remainder ) \ 373 FT_BEGIN_STMNT \ 374 (quotient) = (type)( (dividend) / (divisor) ); \ 375 (remainder) = (type)( (dividend) - (quotient) * (divisor) ); \ 376 if ( (remainder) < 0 ) \ 377 { \ 378 (quotient)--; \ 379 (remainder) += (type)(divisor); \ 380 } \ 381 FT_END_STMNT 382 #endif /* __arm__ */ 383 384 385 /* These macros speed up repetitive divisions by replacing them */ 386 /* with multiplications and right shifts. */ 387 #define FT_UDIVPREP( c, b ) \ 388 long b ## _r = c ? (long)( FT_ULONG_MAX >> PIXEL_BITS ) / ( b ) \ 389 : 0 390 #define FT_UDIV( a, b ) \ 391 (TCoord)( ( (unsigned long)( a ) * (unsigned long)( b ## _r ) ) >> \ 392 ( sizeof( long ) * FT_CHAR_BIT - PIXEL_BITS ) ) 393 394 395 /************************************************************************** 396 * 397 * TYPE DEFINITIONS 398 */ 399 400 /* don't change the following types to FT_Int or FT_Pos, since we might */ 401 /* need to define them to "float" or "double" when experimenting with */ 402 /* new algorithms */ 403 404 typedef long TPos; /* subpixel coordinate */ 405 typedef int TCoord; /* integer scanline/pixel coordinate */ 406 typedef int TArea; /* cell areas, coordinate products */ 407 408 409 typedef struct TCell_* PCell; 410 411 typedef struct TCell_ 412 { 413 TCoord x; /* same with gray_TWorker.ex */ 414 TCoord cover; /* same with gray_TWorker.cover */ 415 TArea area; 416 PCell next; 417 418 } TCell; 419 420 typedef struct TPixmap_ 421 { 422 unsigned char* origin; /* pixmap origin at the bottom-left */ 423 int pitch; /* pitch to go down one row */ 424 425 } TPixmap; 426 427 /* maximum number of gray cells in the buffer */ 428 #if FT_RENDER_POOL_SIZE > 2048 429 #define FT_MAX_GRAY_POOL ( FT_RENDER_POOL_SIZE / sizeof ( TCell ) ) 430 #else 431 #define FT_MAX_GRAY_POOL ( 2048 / sizeof ( TCell ) ) 432 #endif 433 434 /* FT_Span buffer size for direct rendering only */ 435 #define FT_MAX_GRAY_SPANS 10 436 437 438 #if defined( _MSC_VER ) /* Visual C++ (and Intel C++) */ 439 /* We disable the warning `structure was padded due to */ 440 /* __declspec(align())' in order to compile cleanly with */ 441 /* the maximum level of warnings. */ 442 #pragma warning( push ) 443 #pragma warning( disable : 4324 ) 444 #endif /* _MSC_VER */ 445 446 typedef struct gray_TWorker_ 447 { 448 ft_jmp_buf jump_buffer; 449 450 TCoord ex, ey; 451 TCoord min_ex, max_ex; 452 TCoord min_ey, max_ey; 453 454 TArea area; 455 TCoord cover; 456 int invalid; 457 458 PCell* ycells; 459 PCell cells; 460 FT_PtrDist max_cells; 461 FT_PtrDist num_cells; 462 463 TPos x, y; 464 465 FT_Outline outline; 466 TPixmap target; 467 468 FT_Raster_Span_Func render_span; 469 void* render_span_data; 470 FT_Span spans[FT_MAX_GRAY_SPANS]; 471 int num_spans; 472 473 } gray_TWorker, *gray_PWorker; 474 475 #if defined( _MSC_VER ) 476 #pragma warning( pop ) 477 #endif 478 479 480 #ifndef FT_STATIC_RASTER 481 #define ras (*worker) 482 #else 483 static gray_TWorker ras; 484 #endif 485 486 487 typedef struct gray_TRaster_ 488 { 489 void* memory; 490 491 } gray_TRaster, *gray_PRaster; 492 493 494 #ifdef FT_DEBUG_LEVEL_TRACE 495 496 /* to be called while in the debugger -- */ 497 /* this function causes a compiler warning since it is unused otherwise */ 498 static void gray_dump_cells(RAS_ARG)499 gray_dump_cells( RAS_ARG ) 500 { 501 int y; 502 503 504 for ( y = ras.min_ey; y < ras.max_ey; y++ ) 505 { 506 PCell cell = ras.ycells[y - ras.min_ey]; 507 508 509 printf( "%3d:", y ); 510 511 for ( ; cell != NULL; cell = cell->next ) 512 printf( " (%3d, c:%4d, a:%6d)", 513 cell->x, cell->cover, cell->area ); 514 printf( "\n" ); 515 } 516 } 517 518 #endif /* FT_DEBUG_LEVEL_TRACE */ 519 520 521 /************************************************************************** 522 * 523 * Record the current cell in the linked list. 524 */ 525 static void gray_record_cell(RAS_ARG)526 gray_record_cell( RAS_ARG ) 527 { 528 PCell *pcell, cell; 529 TCoord x = ras.ex; 530 531 532 pcell = &ras.ycells[ras.ey - ras.min_ey]; 533 while ( ( cell = *pcell ) ) 534 { 535 if ( cell->x > x ) 536 break; 537 538 if ( cell->x == x ) 539 goto Found; 540 541 pcell = &cell->next; 542 } 543 544 if ( ras.num_cells >= ras.max_cells ) 545 ft_longjmp( ras.jump_buffer, 1 ); 546 547 /* insert new cell */ 548 cell = ras.cells + ras.num_cells++; 549 cell->x = x; 550 cell->area = ras.area; 551 cell->cover = ras.cover; 552 553 cell->next = *pcell; 554 *pcell = cell; 555 556 return; 557 558 Found: 559 /* update old cell */ 560 cell->area += ras.area; 561 cell->cover += ras.cover; 562 } 563 564 565 /************************************************************************** 566 * 567 * Set the current cell to a new position. 568 */ 569 static void gray_set_cell(RAS_ARG_ TCoord ex,TCoord ey)570 gray_set_cell( RAS_ARG_ TCoord ex, 571 TCoord ey ) 572 { 573 /* Move the cell pointer to a new position. We set the `invalid' */ 574 /* flag to indicate that the cell isn't part of those we're interested */ 575 /* in during the render phase. This means that: */ 576 /* */ 577 /* . the new vertical position must be within min_ey..max_ey-1. */ 578 /* . the new horizontal position must be strictly less than max_ex */ 579 /* */ 580 /* Note that if a cell is to the left of the clipping region, it is */ 581 /* actually set to the (min_ex-1) horizontal position. */ 582 583 /* record the current one if it is valid and substantial */ 584 if ( !ras.invalid && ( ras.area || ras.cover ) ) 585 gray_record_cell( RAS_VAR ); 586 587 ras.area = 0; 588 ras.cover = 0; 589 ras.ex = FT_MAX( ex, ras.min_ex - 1 ); 590 ras.ey = ey; 591 592 ras.invalid = ( ey >= ras.max_ey || ey < ras.min_ey || 593 ex >= ras.max_ex ); 594 } 595 596 597 #ifndef FT_LONG64 598 599 /************************************************************************** 600 * 601 * Render a scanline as one or more cells. 602 */ 603 static void gray_render_scanline(RAS_ARG_ TCoord ey,TPos x1,TCoord y1,TPos x2,TCoord y2)604 gray_render_scanline( RAS_ARG_ TCoord ey, 605 TPos x1, 606 TCoord y1, 607 TPos x2, 608 TCoord y2 ) 609 { 610 TCoord ex1, ex2, fx1, fx2, first, dy, delta, mod; 611 TPos p, dx; 612 int incr; 613 614 615 ex1 = TRUNC( x1 ); 616 ex2 = TRUNC( x2 ); 617 618 /* trivial case. Happens often */ 619 if ( y1 == y2 ) 620 { 621 gray_set_cell( RAS_VAR_ ex2, ey ); 622 return; 623 } 624 625 fx1 = FRACT( x1 ); 626 fx2 = FRACT( x2 ); 627 628 /* everything is located in a single cell. That is easy! */ 629 /* */ 630 if ( ex1 == ex2 ) 631 goto End; 632 633 /* ok, we'll have to render a run of adjacent cells on the same */ 634 /* scanline... */ 635 /* */ 636 dx = x2 - x1; 637 dy = y2 - y1; 638 639 if ( dx > 0 ) 640 { 641 p = ( ONE_PIXEL - fx1 ) * dy; 642 first = ONE_PIXEL; 643 incr = 1; 644 } 645 else 646 { 647 p = fx1 * dy; 648 first = 0; 649 incr = -1; 650 dx = -dx; 651 } 652 653 /* the fractional part of y-delta is mod/dx. It is essential to */ 654 /* keep track of its accumulation for accurate rendering. */ 655 /* XXX: y-delta and x-delta below should be related. */ 656 FT_DIV_MOD( TCoord, p, dx, delta, mod ); 657 658 ras.area += (TArea)( ( fx1 + first ) * delta ); 659 ras.cover += delta; 660 y1 += delta; 661 ex1 += incr; 662 gray_set_cell( RAS_VAR_ ex1, ey ); 663 664 if ( ex1 != ex2 ) 665 { 666 TCoord lift, rem; 667 668 669 p = ONE_PIXEL * dy; 670 FT_DIV_MOD( TCoord, p, dx, lift, rem ); 671 672 do 673 { 674 delta = lift; 675 mod += rem; 676 if ( mod >= (TCoord)dx ) 677 { 678 mod -= (TCoord)dx; 679 delta++; 680 } 681 682 ras.area += (TArea)( ONE_PIXEL * delta ); 683 ras.cover += delta; 684 y1 += delta; 685 ex1 += incr; 686 gray_set_cell( RAS_VAR_ ex1, ey ); 687 } while ( ex1 != ex2 ); 688 } 689 690 fx1 = ONE_PIXEL - first; 691 692 End: 693 dy = y2 - y1; 694 695 ras.area += (TArea)( ( fx1 + fx2 ) * dy ); 696 ras.cover += dy; 697 } 698 699 700 /************************************************************************** 701 * 702 * Render a given line as a series of scanlines. 703 */ 704 static void gray_render_line(RAS_ARG_ TPos to_x,TPos to_y)705 gray_render_line( RAS_ARG_ TPos to_x, 706 TPos to_y ) 707 { 708 TCoord ey1, ey2, fy1, fy2, first, delta, mod; 709 TPos p, dx, dy, x, x2; 710 int incr; 711 712 713 ey1 = TRUNC( ras.y ); 714 ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */ 715 716 /* perform vertical clipping */ 717 if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) || 718 ( ey1 < ras.min_ey && ey2 < ras.min_ey ) ) 719 goto End; 720 721 fy1 = FRACT( ras.y ); 722 fy2 = FRACT( to_y ); 723 724 /* everything is on a single scanline */ 725 if ( ey1 == ey2 ) 726 { 727 gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 ); 728 goto End; 729 } 730 731 dx = to_x - ras.x; 732 dy = to_y - ras.y; 733 734 /* vertical line - avoid calling gray_render_scanline */ 735 if ( dx == 0 ) 736 { 737 TCoord ex = TRUNC( ras.x ); 738 TCoord two_fx = FRACT( ras.x ) << 1; 739 TArea area; 740 741 742 if ( dy > 0) 743 { 744 first = ONE_PIXEL; 745 incr = 1; 746 } 747 else 748 { 749 first = 0; 750 incr = -1; 751 } 752 753 delta = first - fy1; 754 ras.area += (TArea)two_fx * delta; 755 ras.cover += delta; 756 ey1 += incr; 757 758 gray_set_cell( RAS_VAR_ ex, ey1 ); 759 760 delta = first + first - ONE_PIXEL; 761 area = (TArea)two_fx * delta; 762 while ( ey1 != ey2 ) 763 { 764 ras.area += area; 765 ras.cover += delta; 766 ey1 += incr; 767 768 gray_set_cell( RAS_VAR_ ex, ey1 ); 769 } 770 771 delta = fy2 - ONE_PIXEL + first; 772 ras.area += (TArea)two_fx * delta; 773 ras.cover += delta; 774 775 goto End; 776 } 777 778 /* ok, we have to render several scanlines */ 779 if ( dy > 0) 780 { 781 p = ( ONE_PIXEL - fy1 ) * dx; 782 first = ONE_PIXEL; 783 incr = 1; 784 } 785 else 786 { 787 p = fy1 * dx; 788 first = 0; 789 incr = -1; 790 dy = -dy; 791 } 792 793 /* the fractional part of x-delta is mod/dy. It is essential to */ 794 /* keep track of its accumulation for accurate rendering. */ 795 FT_DIV_MOD( TCoord, p, dy, delta, mod ); 796 797 x = ras.x + delta; 798 gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, first ); 799 800 ey1 += incr; 801 gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 ); 802 803 if ( ey1 != ey2 ) 804 { 805 TCoord lift, rem; 806 807 808 p = ONE_PIXEL * dx; 809 FT_DIV_MOD( TCoord, p, dy, lift, rem ); 810 811 do 812 { 813 delta = lift; 814 mod += rem; 815 if ( mod >= (TCoord)dy ) 816 { 817 mod -= (TCoord)dy; 818 delta++; 819 } 820 821 x2 = x + delta; 822 gray_render_scanline( RAS_VAR_ ey1, 823 x, ONE_PIXEL - first, 824 x2, first ); 825 x = x2; 826 827 ey1 += incr; 828 gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 ); 829 } while ( ey1 != ey2 ); 830 } 831 832 gray_render_scanline( RAS_VAR_ ey1, 833 x, ONE_PIXEL - first, 834 to_x, fy2 ); 835 836 End: 837 ras.x = to_x; 838 ras.y = to_y; 839 } 840 841 #else 842 843 /************************************************************************** 844 * 845 * Render a straight line across multiple cells in any direction. 846 */ 847 static void gray_render_line(RAS_ARG_ TPos to_x,TPos to_y)848 gray_render_line( RAS_ARG_ TPos to_x, 849 TPos to_y ) 850 { 851 TPos dx, dy; 852 TCoord fx1, fy1, fx2, fy2; 853 TCoord ex1, ey1, ex2, ey2; 854 855 856 ey1 = TRUNC( ras.y ); 857 ey2 = TRUNC( to_y ); 858 859 /* perform vertical clipping */ 860 if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) || 861 ( ey1 < ras.min_ey && ey2 < ras.min_ey ) ) 862 goto End; 863 864 ex1 = TRUNC( ras.x ); 865 ex2 = TRUNC( to_x ); 866 867 fx1 = FRACT( ras.x ); 868 fy1 = FRACT( ras.y ); 869 870 dx = to_x - ras.x; 871 dy = to_y - ras.y; 872 873 if ( ex1 == ex2 && ey1 == ey2 ) /* inside one cell */ 874 ; 875 else if ( dy == 0 ) /* ex1 != ex2 */ /* any horizontal line */ 876 { 877 gray_set_cell( RAS_VAR_ ex2, ey2 ); 878 goto End; 879 } 880 else if ( dx == 0 ) 881 { 882 if ( dy > 0 ) /* vertical line up */ 883 do 884 { 885 fy2 = ONE_PIXEL; 886 ras.cover += ( fy2 - fy1 ); 887 ras.area += ( fy2 - fy1 ) * fx1 * 2; 888 fy1 = 0; 889 ey1++; 890 gray_set_cell( RAS_VAR_ ex1, ey1 ); 891 } while ( ey1 != ey2 ); 892 else /* vertical line down */ 893 do 894 { 895 fy2 = 0; 896 ras.cover += ( fy2 - fy1 ); 897 ras.area += ( fy2 - fy1 ) * fx1 * 2; 898 fy1 = ONE_PIXEL; 899 ey1--; 900 gray_set_cell( RAS_VAR_ ex1, ey1 ); 901 } while ( ey1 != ey2 ); 902 } 903 else /* any other line */ 904 { 905 TPos prod = dx * (TPos)fy1 - dy * (TPos)fx1; 906 FT_UDIVPREP( ex1 != ex2, dx ); 907 FT_UDIVPREP( ey1 != ey2, dy ); 908 909 910 /* The fundamental value `prod' determines which side and the */ 911 /* exact coordinate where the line exits current cell. It is */ 912 /* also easily updated when moving from one cell to the next. */ 913 do 914 { 915 if ( prod <= 0 && 916 prod - dx * ONE_PIXEL > 0 ) /* left */ 917 { 918 fx2 = 0; 919 fy2 = FT_UDIV( -prod, -dx ); 920 prod -= dy * ONE_PIXEL; 921 ras.cover += ( fy2 - fy1 ); 922 ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); 923 fx1 = ONE_PIXEL; 924 fy1 = fy2; 925 ex1--; 926 } 927 else if ( prod - dx * ONE_PIXEL <= 0 && 928 prod - dx * ONE_PIXEL + dy * ONE_PIXEL > 0 ) /* up */ 929 { 930 prod -= dx * ONE_PIXEL; 931 fx2 = FT_UDIV( -prod, dy ); 932 fy2 = ONE_PIXEL; 933 ras.cover += ( fy2 - fy1 ); 934 ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); 935 fx1 = fx2; 936 fy1 = 0; 937 ey1++; 938 } 939 else if ( prod - dx * ONE_PIXEL + dy * ONE_PIXEL <= 0 && 940 prod + dy * ONE_PIXEL >= 0 ) /* right */ 941 { 942 prod += dy * ONE_PIXEL; 943 fx2 = ONE_PIXEL; 944 fy2 = FT_UDIV( prod, dx ); 945 ras.cover += ( fy2 - fy1 ); 946 ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); 947 fx1 = 0; 948 fy1 = fy2; 949 ex1++; 950 } 951 else /* ( prod + dy * ONE_PIXEL < 0 && 952 prod > 0 ) down */ 953 { 954 fx2 = FT_UDIV( prod, -dy ); 955 fy2 = 0; 956 prod += dx * ONE_PIXEL; 957 ras.cover += ( fy2 - fy1 ); 958 ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); 959 fx1 = fx2; 960 fy1 = ONE_PIXEL; 961 ey1--; 962 } 963 964 gray_set_cell( RAS_VAR_ ex1, ey1 ); 965 } while ( ex1 != ex2 || ey1 != ey2 ); 966 } 967 968 fx2 = FRACT( to_x ); 969 fy2 = FRACT( to_y ); 970 971 ras.cover += ( fy2 - fy1 ); 972 ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); 973 974 End: 975 ras.x = to_x; 976 ras.y = to_y; 977 } 978 979 #endif 980 981 static void gray_split_conic(FT_Vector * base)982 gray_split_conic( FT_Vector* base ) 983 { 984 TPos a, b; 985 986 987 base[4].x = base[2].x; 988 a = base[0].x + base[1].x; 989 b = base[1].x + base[2].x; 990 base[3].x = b >> 1; 991 base[2].x = ( a + b ) >> 2; 992 base[1].x = a >> 1; 993 994 base[4].y = base[2].y; 995 a = base[0].y + base[1].y; 996 b = base[1].y + base[2].y; 997 base[3].y = b >> 1; 998 base[2].y = ( a + b ) >> 2; 999 base[1].y = a >> 1; 1000 } 1001 1002 1003 static void gray_render_conic(RAS_ARG_ const FT_Vector * control,const FT_Vector * to)1004 gray_render_conic( RAS_ARG_ const FT_Vector* control, 1005 const FT_Vector* to ) 1006 { 1007 FT_Vector bez_stack[16 * 2 + 1]; /* enough to accommodate bisections */ 1008 FT_Vector* arc = bez_stack; 1009 TPos dx, dy; 1010 int draw, split; 1011 1012 1013 arc[0].x = UPSCALE( to->x ); 1014 arc[0].y = UPSCALE( to->y ); 1015 arc[1].x = UPSCALE( control->x ); 1016 arc[1].y = UPSCALE( control->y ); 1017 arc[2].x = ras.x; 1018 arc[2].y = ras.y; 1019 1020 /* short-cut the arc that crosses the current band */ 1021 if ( ( TRUNC( arc[0].y ) >= ras.max_ey && 1022 TRUNC( arc[1].y ) >= ras.max_ey && 1023 TRUNC( arc[2].y ) >= ras.max_ey ) || 1024 ( TRUNC( arc[0].y ) < ras.min_ey && 1025 TRUNC( arc[1].y ) < ras.min_ey && 1026 TRUNC( arc[2].y ) < ras.min_ey ) ) 1027 { 1028 ras.x = arc[0].x; 1029 ras.y = arc[0].y; 1030 return; 1031 } 1032 1033 dx = FT_ABS( arc[2].x + arc[0].x - 2 * arc[1].x ); 1034 dy = FT_ABS( arc[2].y + arc[0].y - 2 * arc[1].y ); 1035 if ( dx < dy ) 1036 dx = dy; 1037 1038 /* We can calculate the number of necessary bisections because */ 1039 /* each bisection predictably reduces deviation exactly 4-fold. */ 1040 /* Even 32-bit deviation would vanish after 16 bisections. */ 1041 draw = 1; 1042 while ( dx > ONE_PIXEL / 4 ) 1043 { 1044 dx >>= 2; 1045 draw <<= 1; 1046 } 1047 1048 /* We use decrement counter to count the total number of segments */ 1049 /* to draw starting from 2^level. Before each draw we split as */ 1050 /* many times as there are trailing zeros in the counter. */ 1051 do 1052 { 1053 split = draw & ( -draw ); /* isolate the rightmost 1-bit */ 1054 while ( ( split >>= 1 ) ) 1055 { 1056 gray_split_conic( arc ); 1057 arc += 2; 1058 } 1059 1060 gray_render_line( RAS_VAR_ arc[0].x, arc[0].y ); 1061 arc -= 2; 1062 1063 } while ( --draw ); 1064 } 1065 1066 1067 static void gray_split_cubic(FT_Vector * base)1068 gray_split_cubic( FT_Vector* base ) 1069 { 1070 TPos a, b, c; 1071 1072 1073 base[6].x = base[3].x; 1074 a = base[0].x + base[1].x; 1075 b = base[1].x + base[2].x; 1076 c = base[2].x + base[3].x; 1077 base[5].x = c >> 1; 1078 c += b; 1079 base[4].x = c >> 2; 1080 base[1].x = a >> 1; 1081 a += b; 1082 base[2].x = a >> 2; 1083 base[3].x = ( a + c ) >> 3; 1084 1085 base[6].y = base[3].y; 1086 a = base[0].y + base[1].y; 1087 b = base[1].y + base[2].y; 1088 c = base[2].y + base[3].y; 1089 base[5].y = c >> 1; 1090 c += b; 1091 base[4].y = c >> 2; 1092 base[1].y = a >> 1; 1093 a += b; 1094 base[2].y = a >> 2; 1095 base[3].y = ( a + c ) >> 3; 1096 } 1097 1098 1099 static void gray_render_cubic(RAS_ARG_ const FT_Vector * control1,const FT_Vector * control2,const FT_Vector * to)1100 gray_render_cubic( RAS_ARG_ const FT_Vector* control1, 1101 const FT_Vector* control2, 1102 const FT_Vector* to ) 1103 { 1104 FT_Vector bez_stack[16 * 3 + 1]; /* enough to accommodate bisections */ 1105 FT_Vector* arc = bez_stack; 1106 1107 1108 arc[0].x = UPSCALE( to->x ); 1109 arc[0].y = UPSCALE( to->y ); 1110 arc[1].x = UPSCALE( control2->x ); 1111 arc[1].y = UPSCALE( control2->y ); 1112 arc[2].x = UPSCALE( control1->x ); 1113 arc[2].y = UPSCALE( control1->y ); 1114 arc[3].x = ras.x; 1115 arc[3].y = ras.y; 1116 1117 /* short-cut the arc that crosses the current band */ 1118 if ( ( TRUNC( arc[0].y ) >= ras.max_ey && 1119 TRUNC( arc[1].y ) >= ras.max_ey && 1120 TRUNC( arc[2].y ) >= ras.max_ey && 1121 TRUNC( arc[3].y ) >= ras.max_ey ) || 1122 ( TRUNC( arc[0].y ) < ras.min_ey && 1123 TRUNC( arc[1].y ) < ras.min_ey && 1124 TRUNC( arc[2].y ) < ras.min_ey && 1125 TRUNC( arc[3].y ) < ras.min_ey ) ) 1126 { 1127 ras.x = arc[0].x; 1128 ras.y = arc[0].y; 1129 return; 1130 } 1131 1132 for (;;) 1133 { 1134 /* with each split, control points quickly converge towards */ 1135 /* chord trisection points and the vanishing distances below */ 1136 /* indicate when the segment is flat enough to draw */ 1137 if ( FT_ABS( 2 * arc[0].x - 3 * arc[1].x + arc[3].x ) > ONE_PIXEL / 2 || 1138 FT_ABS( 2 * arc[0].y - 3 * arc[1].y + arc[3].y ) > ONE_PIXEL / 2 || 1139 FT_ABS( arc[0].x - 3 * arc[2].x + 2 * arc[3].x ) > ONE_PIXEL / 2 || 1140 FT_ABS( arc[0].y - 3 * arc[2].y + 2 * arc[3].y ) > ONE_PIXEL / 2 ) 1141 goto Split; 1142 1143 gray_render_line( RAS_VAR_ arc[0].x, arc[0].y ); 1144 1145 if ( arc == bez_stack ) 1146 return; 1147 1148 arc -= 3; 1149 continue; 1150 1151 Split: 1152 gray_split_cubic( arc ); 1153 arc += 3; 1154 } 1155 } 1156 1157 1158 static int gray_move_to(const FT_Vector * to,gray_PWorker worker)1159 gray_move_to( const FT_Vector* to, 1160 gray_PWorker worker ) 1161 { 1162 TPos x, y; 1163 1164 1165 /* start to a new position */ 1166 x = UPSCALE( to->x ); 1167 y = UPSCALE( to->y ); 1168 1169 gray_set_cell( RAS_VAR_ TRUNC( x ), TRUNC( y ) ); 1170 1171 ras.x = x; 1172 ras.y = y; 1173 return 0; 1174 } 1175 1176 1177 static int gray_line_to(const FT_Vector * to,gray_PWorker worker)1178 gray_line_to( const FT_Vector* to, 1179 gray_PWorker worker ) 1180 { 1181 gray_render_line( RAS_VAR_ UPSCALE( to->x ), UPSCALE( to->y ) ); 1182 return 0; 1183 } 1184 1185 1186 static int gray_conic_to(const FT_Vector * control,const FT_Vector * to,gray_PWorker worker)1187 gray_conic_to( const FT_Vector* control, 1188 const FT_Vector* to, 1189 gray_PWorker worker ) 1190 { 1191 gray_render_conic( RAS_VAR_ control, to ); 1192 return 0; 1193 } 1194 1195 1196 static int gray_cubic_to(const FT_Vector * control1,const FT_Vector * control2,const FT_Vector * to,gray_PWorker worker)1197 gray_cubic_to( const FT_Vector* control1, 1198 const FT_Vector* control2, 1199 const FT_Vector* to, 1200 gray_PWorker worker ) 1201 { 1202 gray_render_cubic( RAS_VAR_ control1, control2, to ); 1203 return 0; 1204 } 1205 1206 1207 static void gray_hline(RAS_ARG_ TCoord x,TCoord y,TArea coverage,TCoord acount)1208 gray_hline( RAS_ARG_ TCoord x, 1209 TCoord y, 1210 TArea coverage, 1211 TCoord acount ) 1212 { 1213 /* scale the coverage from 0..(ONE_PIXEL*ONE_PIXEL*2) to 0..256 */ 1214 coverage >>= PIXEL_BITS * 2 + 1 - 8; 1215 1216 /* compute the line's coverage depending on the outline fill rule */ 1217 if ( ras.outline.flags & FT_OUTLINE_EVEN_ODD_FILL ) 1218 { 1219 coverage &= 511; 1220 1221 if ( coverage >= 256 ) 1222 coverage = 511 - coverage; 1223 } 1224 else /* default non-zero winding rule */ 1225 { 1226 if ( coverage < 0 ) 1227 coverage = ~coverage; /* the same as -coverage - 1 */ 1228 1229 if ( coverage >= 256 ) 1230 coverage = 255; 1231 } 1232 1233 if ( ras.num_spans >= 0 ) /* for FT_RASTER_FLAG_DIRECT only */ 1234 { 1235 FT_Span* span = ras.spans + ras.num_spans++; 1236 1237 1238 span->x = (short)x; 1239 span->len = (unsigned short)acount; 1240 span->coverage = (unsigned char)coverage; 1241 1242 if ( ras.num_spans == FT_MAX_GRAY_SPANS ) 1243 { 1244 /* flush the span buffer and reset the count */ 1245 ras.render_span( y, ras.num_spans, ras.spans, ras.render_span_data ); 1246 ras.num_spans = 0; 1247 } 1248 } 1249 else 1250 { 1251 unsigned char* q = ras.target.origin - ras.target.pitch * y + x; 1252 unsigned char c = (unsigned char)coverage; 1253 1254 1255 /* For small-spans it is faster to do it by ourselves than 1256 * calling `memset'. This is mainly due to the cost of the 1257 * function call. 1258 */ 1259 switch ( acount ) 1260 { 1261 case 7: 1262 *q++ = c; 1263 /* fall through */ 1264 case 6: 1265 *q++ = c; 1266 /* fall through */ 1267 case 5: 1268 *q++ = c; 1269 /* fall through */ 1270 case 4: 1271 *q++ = c; 1272 /* fall through */ 1273 case 3: 1274 *q++ = c; 1275 /* fall through */ 1276 case 2: 1277 *q++ = c; 1278 /* fall through */ 1279 case 1: 1280 *q = c; 1281 /* fall through */ 1282 case 0: 1283 break; 1284 default: 1285 FT_MEM_SET( q, c, acount ); 1286 } 1287 } 1288 } 1289 1290 1291 static void gray_sweep(RAS_ARG)1292 gray_sweep( RAS_ARG ) 1293 { 1294 int y; 1295 1296 1297 for ( y = ras.min_ey; y < ras.max_ey; y++ ) 1298 { 1299 PCell cell = ras.ycells[y - ras.min_ey]; 1300 TCoord x = ras.min_ex; 1301 TArea cover = 0; 1302 TArea area; 1303 1304 1305 for ( ; cell != NULL; cell = cell->next ) 1306 { 1307 if ( cover != 0 && cell->x > x ) 1308 gray_hline( RAS_VAR_ x, y, cover, cell->x - x ); 1309 1310 cover += (TArea)cell->cover * ( ONE_PIXEL * 2 ); 1311 area = cover - cell->area; 1312 1313 if ( area != 0 && cell->x >= ras.min_ex ) 1314 gray_hline( RAS_VAR_ cell->x, y, area, 1 ); 1315 1316 x = cell->x + 1; 1317 } 1318 1319 if ( cover != 0 ) 1320 gray_hline( RAS_VAR_ x, y, cover, ras.max_ex - x ); 1321 1322 if ( ras.num_spans > 0 ) /* for FT_RASTER_FLAG_DIRECT only */ 1323 { 1324 /* flush the span buffer and reset the count */ 1325 ras.render_span( y, ras.num_spans, ras.spans, ras.render_span_data ); 1326 ras.num_spans = 0; 1327 } 1328 } 1329 } 1330 1331 1332 #ifdef STANDALONE_ 1333 1334 /************************************************************************** 1335 * 1336 * The following functions should only compile in stand-alone mode, 1337 * i.e., when building this component without the rest of FreeType. 1338 * 1339 */ 1340 1341 /************************************************************************** 1342 * 1343 * @Function: 1344 * FT_Outline_Decompose 1345 * 1346 * @Description: 1347 * Walk over an outline's structure to decompose it into individual 1348 * segments and Bézier arcs. This function is also able to emit 1349 * `move to' and `close to' operations to indicate the start and end 1350 * of new contours in the outline. 1351 * 1352 * @Input: 1353 * outline :: 1354 * A pointer to the source target. 1355 * 1356 * func_interface :: 1357 * A table of `emitters', i.e., function pointers 1358 * called during decomposition to indicate path 1359 * operations. 1360 * 1361 * @InOut: 1362 * user :: 1363 * A typeless pointer which is passed to each 1364 * emitter during the decomposition. It can be 1365 * used to store the state during the 1366 * decomposition. 1367 * 1368 * @Return: 1369 * Error code. 0 means success. 1370 */ 1371 static int FT_Outline_Decompose(const FT_Outline * outline,const FT_Outline_Funcs * func_interface,void * user)1372 FT_Outline_Decompose( const FT_Outline* outline, 1373 const FT_Outline_Funcs* func_interface, 1374 void* user ) 1375 { 1376 #undef SCALED 1377 #define SCALED( x ) ( (x) * ( 1L << shift ) - delta ) 1378 1379 FT_Vector v_last; 1380 FT_Vector v_control; 1381 FT_Vector v_start; 1382 1383 FT_Vector* point; 1384 FT_Vector* limit; 1385 char* tags; 1386 1387 int error; 1388 1389 int n; /* index of contour in outline */ 1390 int first; /* index of first point in contour */ 1391 char tag; /* current point's state */ 1392 1393 int shift; 1394 TPos delta; 1395 1396 1397 if ( !outline ) 1398 return FT_THROW( Invalid_Outline ); 1399 1400 if ( !func_interface ) 1401 return FT_THROW( Invalid_Argument ); 1402 1403 shift = func_interface->shift; 1404 delta = func_interface->delta; 1405 first = 0; 1406 1407 for ( n = 0; n < outline->n_contours; n++ ) 1408 { 1409 int last; /* index of last point in contour */ 1410 1411 1412 FT_TRACE5(( "FT_Outline_Decompose: Outline %d\n", n )); 1413 1414 last = outline->contours[n]; 1415 if ( last < 0 ) 1416 goto Invalid_Outline; 1417 limit = outline->points + last; 1418 1419 v_start = outline->points[first]; 1420 v_start.x = SCALED( v_start.x ); 1421 v_start.y = SCALED( v_start.y ); 1422 1423 v_last = outline->points[last]; 1424 v_last.x = SCALED( v_last.x ); 1425 v_last.y = SCALED( v_last.y ); 1426 1427 v_control = v_start; 1428 1429 point = outline->points + first; 1430 tags = outline->tags + first; 1431 tag = FT_CURVE_TAG( tags[0] ); 1432 1433 /* A contour cannot start with a cubic control point! */ 1434 if ( tag == FT_CURVE_TAG_CUBIC ) 1435 goto Invalid_Outline; 1436 1437 /* check first point to determine origin */ 1438 if ( tag == FT_CURVE_TAG_CONIC ) 1439 { 1440 /* first point is conic control. Yes, this happens. */ 1441 if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON ) 1442 { 1443 /* start at last point if it is on the curve */ 1444 v_start = v_last; 1445 limit--; 1446 } 1447 else 1448 { 1449 /* if both first and last points are conic, */ 1450 /* start at their middle and record its position */ 1451 /* for closure */ 1452 v_start.x = ( v_start.x + v_last.x ) / 2; 1453 v_start.y = ( v_start.y + v_last.y ) / 2; 1454 1455 v_last = v_start; 1456 } 1457 point--; 1458 tags--; 1459 } 1460 1461 FT_TRACE5(( " move to (%.2f, %.2f)\n", 1462 v_start.x / 64.0, v_start.y / 64.0 )); 1463 error = func_interface->move_to( &v_start, user ); 1464 if ( error ) 1465 goto Exit; 1466 1467 while ( point < limit ) 1468 { 1469 point++; 1470 tags++; 1471 1472 tag = FT_CURVE_TAG( tags[0] ); 1473 switch ( tag ) 1474 { 1475 case FT_CURVE_TAG_ON: /* emit a single line_to */ 1476 { 1477 FT_Vector vec; 1478 1479 1480 vec.x = SCALED( point->x ); 1481 vec.y = SCALED( point->y ); 1482 1483 FT_TRACE5(( " line to (%.2f, %.2f)\n", 1484 vec.x / 64.0, vec.y / 64.0 )); 1485 error = func_interface->line_to( &vec, user ); 1486 if ( error ) 1487 goto Exit; 1488 continue; 1489 } 1490 1491 case FT_CURVE_TAG_CONIC: /* consume conic arcs */ 1492 v_control.x = SCALED( point->x ); 1493 v_control.y = SCALED( point->y ); 1494 1495 Do_Conic: 1496 if ( point < limit ) 1497 { 1498 FT_Vector vec; 1499 FT_Vector v_middle; 1500 1501 1502 point++; 1503 tags++; 1504 tag = FT_CURVE_TAG( tags[0] ); 1505 1506 vec.x = SCALED( point->x ); 1507 vec.y = SCALED( point->y ); 1508 1509 if ( tag == FT_CURVE_TAG_ON ) 1510 { 1511 FT_TRACE5(( " conic to (%.2f, %.2f)" 1512 " with control (%.2f, %.2f)\n", 1513 vec.x / 64.0, vec.y / 64.0, 1514 v_control.x / 64.0, v_control.y / 64.0 )); 1515 error = func_interface->conic_to( &v_control, &vec, user ); 1516 if ( error ) 1517 goto Exit; 1518 continue; 1519 } 1520 1521 if ( tag != FT_CURVE_TAG_CONIC ) 1522 goto Invalid_Outline; 1523 1524 v_middle.x = ( v_control.x + vec.x ) / 2; 1525 v_middle.y = ( v_control.y + vec.y ) / 2; 1526 1527 FT_TRACE5(( " conic to (%.2f, %.2f)" 1528 " with control (%.2f, %.2f)\n", 1529 v_middle.x / 64.0, v_middle.y / 64.0, 1530 v_control.x / 64.0, v_control.y / 64.0 )); 1531 error = func_interface->conic_to( &v_control, &v_middle, user ); 1532 if ( error ) 1533 goto Exit; 1534 1535 v_control = vec; 1536 goto Do_Conic; 1537 } 1538 1539 FT_TRACE5(( " conic to (%.2f, %.2f)" 1540 " with control (%.2f, %.2f)\n", 1541 v_start.x / 64.0, v_start.y / 64.0, 1542 v_control.x / 64.0, v_control.y / 64.0 )); 1543 error = func_interface->conic_to( &v_control, &v_start, user ); 1544 goto Close; 1545 1546 default: /* FT_CURVE_TAG_CUBIC */ 1547 { 1548 FT_Vector vec1, vec2; 1549 1550 1551 if ( point + 1 > limit || 1552 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) 1553 goto Invalid_Outline; 1554 1555 point += 2; 1556 tags += 2; 1557 1558 vec1.x = SCALED( point[-2].x ); 1559 vec1.y = SCALED( point[-2].y ); 1560 1561 vec2.x = SCALED( point[-1].x ); 1562 vec2.y = SCALED( point[-1].y ); 1563 1564 if ( point <= limit ) 1565 { 1566 FT_Vector vec; 1567 1568 1569 vec.x = SCALED( point->x ); 1570 vec.y = SCALED( point->y ); 1571 1572 FT_TRACE5(( " cubic to (%.2f, %.2f)" 1573 " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", 1574 vec.x / 64.0, vec.y / 64.0, 1575 vec1.x / 64.0, vec1.y / 64.0, 1576 vec2.x / 64.0, vec2.y / 64.0 )); 1577 error = func_interface->cubic_to( &vec1, &vec2, &vec, user ); 1578 if ( error ) 1579 goto Exit; 1580 continue; 1581 } 1582 1583 FT_TRACE5(( " cubic to (%.2f, %.2f)" 1584 " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", 1585 v_start.x / 64.0, v_start.y / 64.0, 1586 vec1.x / 64.0, vec1.y / 64.0, 1587 vec2.x / 64.0, vec2.y / 64.0 )); 1588 error = func_interface->cubic_to( &vec1, &vec2, &v_start, user ); 1589 goto Close; 1590 } 1591 } 1592 } 1593 1594 /* close the contour with a line segment */ 1595 FT_TRACE5(( " line to (%.2f, %.2f)\n", 1596 v_start.x / 64.0, v_start.y / 64.0 )); 1597 error = func_interface->line_to( &v_start, user ); 1598 1599 Close: 1600 if ( error ) 1601 goto Exit; 1602 1603 first = last + 1; 1604 } 1605 1606 FT_TRACE5(( "FT_Outline_Decompose: Done\n", n )); 1607 return 0; 1608 1609 Exit: 1610 FT_TRACE5(( "FT_Outline_Decompose: Error 0x%x\n", error )); 1611 return error; 1612 1613 Invalid_Outline: 1614 return FT_THROW( Invalid_Outline ); 1615 } 1616 1617 #endif /* STANDALONE_ */ 1618 1619 1620 FT_DEFINE_OUTLINE_FUNCS( 1621 func_interface, 1622 1623 (FT_Outline_MoveTo_Func) gray_move_to, /* move_to */ 1624 (FT_Outline_LineTo_Func) gray_line_to, /* line_to */ 1625 (FT_Outline_ConicTo_Func)gray_conic_to, /* conic_to */ 1626 (FT_Outline_CubicTo_Func)gray_cubic_to, /* cubic_to */ 1627 1628 0, /* shift */ 1629 0 /* delta */ 1630 ) 1631 1632 1633 static int gray_convert_glyph_inner(RAS_ARG,int continued)1634 gray_convert_glyph_inner( RAS_ARG, 1635 int continued ) 1636 { 1637 int error; 1638 1639 1640 if ( ft_setjmp( ras.jump_buffer ) == 0 ) 1641 { 1642 if ( continued ) 1643 FT_Trace_Disable(); 1644 error = FT_Outline_Decompose( &ras.outline, &func_interface, &ras ); 1645 if ( continued ) 1646 FT_Trace_Enable(); 1647 1648 if ( !ras.invalid ) 1649 gray_record_cell( RAS_VAR ); 1650 1651 FT_TRACE7(( "band [%d..%d]: %ld cell%s\n", 1652 ras.min_ey, 1653 ras.max_ey, 1654 ras.num_cells, 1655 ras.num_cells == 1 ? "" : "s" )); 1656 } 1657 else 1658 { 1659 error = FT_THROW( Memory_Overflow ); 1660 1661 FT_TRACE7(( "band [%d..%d]: to be bisected\n", 1662 ras.min_ey, ras.max_ey )); 1663 } 1664 1665 return error; 1666 } 1667 1668 1669 static int gray_convert_glyph(RAS_ARG)1670 gray_convert_glyph( RAS_ARG ) 1671 { 1672 const TCoord yMin = ras.min_ey; 1673 const TCoord yMax = ras.max_ey; 1674 1675 TCell buffer[FT_MAX_GRAY_POOL]; 1676 size_t height = (size_t)( yMax - yMin ); 1677 size_t n = FT_MAX_GRAY_POOL / 8; 1678 TCoord y; 1679 TCoord bands[32]; /* enough to accommodate bisections */ 1680 TCoord* band; 1681 1682 int continued = 0; 1683 1684 1685 /* set up vertical bands */ 1686 if ( height > n ) 1687 { 1688 /* two divisions rounded up */ 1689 n = ( height + n - 1 ) / n; 1690 height = ( height + n - 1 ) / n; 1691 } 1692 1693 /* memory management */ 1694 n = ( height * sizeof ( PCell ) + sizeof ( TCell ) - 1 ) / sizeof ( TCell ); 1695 1696 ras.cells = buffer + n; 1697 ras.max_cells = (FT_PtrDist)( FT_MAX_GRAY_POOL - n ); 1698 ras.ycells = (PCell*)buffer; 1699 1700 for ( y = yMin; y < yMax; ) 1701 { 1702 ras.min_ey = y; 1703 y += height; 1704 ras.max_ey = FT_MIN( y, yMax ); 1705 1706 band = bands; 1707 band[1] = ras.min_ey; 1708 band[0] = ras.max_ey; 1709 1710 do 1711 { 1712 TCoord width = band[0] - band[1]; 1713 int error; 1714 1715 1716 FT_MEM_ZERO( ras.ycells, height * sizeof ( PCell ) ); 1717 1718 ras.num_cells = 0; 1719 ras.invalid = 1; 1720 ras.min_ey = band[1]; 1721 ras.max_ey = band[0]; 1722 1723 error = gray_convert_glyph_inner( RAS_VAR, continued ); 1724 continued = 1; 1725 1726 if ( !error ) 1727 { 1728 gray_sweep( RAS_VAR ); 1729 band--; 1730 continue; 1731 } 1732 else if ( error != ErrRaster_Memory_Overflow ) 1733 return 1; 1734 1735 /* render pool overflow; we will reduce the render band by half */ 1736 width >>= 1; 1737 1738 /* this should never happen even with tiny rendering pool */ 1739 if ( width == 0 ) 1740 { 1741 FT_TRACE7(( "gray_convert_glyph: rotten glyph\n" )); 1742 return 1; 1743 } 1744 1745 band++; 1746 band[1] = band[0]; 1747 band[0] += width; 1748 } while ( band >= bands ); 1749 } 1750 1751 return 0; 1752 } 1753 1754 1755 static int gray_raster_render(FT_Raster raster,const FT_Raster_Params * params)1756 gray_raster_render( FT_Raster raster, 1757 const FT_Raster_Params* params ) 1758 { 1759 const FT_Outline* outline = (const FT_Outline*)params->source; 1760 const FT_Bitmap* target_map = params->target; 1761 1762 #ifndef FT_STATIC_RASTER 1763 gray_TWorker worker[1]; 1764 #endif 1765 1766 1767 if ( !raster ) 1768 return FT_THROW( Invalid_Argument ); 1769 1770 /* this version does not support monochrome rendering */ 1771 if ( !( params->flags & FT_RASTER_FLAG_AA ) ) 1772 return FT_THROW( Invalid_Mode ); 1773 1774 if ( !outline ) 1775 return FT_THROW( Invalid_Outline ); 1776 1777 /* return immediately if the outline is empty */ 1778 if ( outline->n_points == 0 || outline->n_contours <= 0 ) 1779 return 0; 1780 1781 if ( !outline->contours || !outline->points ) 1782 return FT_THROW( Invalid_Outline ); 1783 1784 if ( outline->n_points != 1785 outline->contours[outline->n_contours - 1] + 1 ) 1786 return FT_THROW( Invalid_Outline ); 1787 1788 ras.outline = *outline; 1789 1790 if ( params->flags & FT_RASTER_FLAG_DIRECT ) 1791 { 1792 if ( !params->gray_spans ) 1793 return 0; 1794 1795 ras.render_span = (FT_Raster_Span_Func)params->gray_spans; 1796 ras.render_span_data = params->user; 1797 ras.num_spans = 0; 1798 1799 ras.min_ex = params->clip_box.xMin; 1800 ras.min_ey = params->clip_box.yMin; 1801 ras.max_ex = params->clip_box.xMax; 1802 ras.max_ey = params->clip_box.yMax; 1803 } 1804 else 1805 { 1806 /* if direct mode is not set, we must have a target bitmap */ 1807 if ( !target_map ) 1808 return FT_THROW( Invalid_Argument ); 1809 1810 /* nothing to do */ 1811 if ( !target_map->width || !target_map->rows ) 1812 return 0; 1813 1814 if ( !target_map->buffer ) 1815 return FT_THROW( Invalid_Argument ); 1816 1817 if ( target_map->pitch < 0 ) 1818 ras.target.origin = target_map->buffer; 1819 else 1820 ras.target.origin = target_map->buffer 1821 + ( target_map->rows - 1 ) * (unsigned int)target_map->pitch; 1822 1823 ras.target.pitch = target_map->pitch; 1824 1825 ras.render_span = (FT_Raster_Span_Func)NULL; 1826 ras.render_span_data = NULL; 1827 ras.num_spans = -1; /* invalid */ 1828 1829 ras.min_ex = 0; 1830 ras.min_ey = 0; 1831 ras.max_ex = (FT_Pos)target_map->width; 1832 ras.max_ey = (FT_Pos)target_map->rows; 1833 } 1834 1835 /* exit if nothing to do */ 1836 if ( ras.max_ex <= ras.min_ex || ras.max_ey <= ras.min_ey ) 1837 return 0; 1838 1839 return gray_convert_glyph( RAS_VAR ); 1840 } 1841 1842 1843 /**** RASTER OBJECT CREATION: In stand-alone mode, we simply use *****/ 1844 /**** a static object. *****/ 1845 1846 #ifdef STANDALONE_ 1847 1848 static int gray_raster_new(void * memory,FT_Raster * araster)1849 gray_raster_new( void* memory, 1850 FT_Raster* araster ) 1851 { 1852 static gray_TRaster the_raster; 1853 1854 FT_UNUSED( memory ); 1855 1856 1857 *araster = (FT_Raster)&the_raster; 1858 FT_ZERO( &the_raster ); 1859 1860 return 0; 1861 } 1862 1863 1864 static void gray_raster_done(FT_Raster raster)1865 gray_raster_done( FT_Raster raster ) 1866 { 1867 /* nothing */ 1868 FT_UNUSED( raster ); 1869 } 1870 1871 #else /* !STANDALONE_ */ 1872 1873 static int gray_raster_new(FT_Memory memory,FT_Raster * araster)1874 gray_raster_new( FT_Memory memory, 1875 FT_Raster* araster ) 1876 { 1877 FT_Error error; 1878 gray_PRaster raster = NULL; 1879 1880 1881 *araster = 0; 1882 if ( !FT_ALLOC( raster, sizeof ( gray_TRaster ) ) ) 1883 { 1884 raster->memory = memory; 1885 *araster = (FT_Raster)raster; 1886 } 1887 1888 return error; 1889 } 1890 1891 1892 static void gray_raster_done(FT_Raster raster)1893 gray_raster_done( FT_Raster raster ) 1894 { 1895 FT_Memory memory = (FT_Memory)((gray_PRaster)raster)->memory; 1896 1897 1898 FT_FREE( raster ); 1899 } 1900 1901 #endif /* !STANDALONE_ */ 1902 1903 1904 static void gray_raster_reset(FT_Raster raster,unsigned char * pool_base,unsigned long pool_size)1905 gray_raster_reset( FT_Raster raster, 1906 unsigned char* pool_base, 1907 unsigned long pool_size ) 1908 { 1909 FT_UNUSED( raster ); 1910 FT_UNUSED( pool_base ); 1911 FT_UNUSED( pool_size ); 1912 } 1913 1914 1915 static int gray_raster_set_mode(FT_Raster raster,unsigned long mode,void * args)1916 gray_raster_set_mode( FT_Raster raster, 1917 unsigned long mode, 1918 void* args ) 1919 { 1920 FT_UNUSED( raster ); 1921 FT_UNUSED( mode ); 1922 FT_UNUSED( args ); 1923 1924 1925 return 0; /* nothing to do */ 1926 } 1927 1928 1929 FT_DEFINE_RASTER_FUNCS( 1930 ft_grays_raster, 1931 1932 FT_GLYPH_FORMAT_OUTLINE, 1933 1934 (FT_Raster_New_Func) gray_raster_new, /* raster_new */ 1935 (FT_Raster_Reset_Func) gray_raster_reset, /* raster_reset */ 1936 (FT_Raster_Set_Mode_Func)gray_raster_set_mode, /* raster_set_mode */ 1937 (FT_Raster_Render_Func) gray_raster_render, /* raster_render */ 1938 (FT_Raster_Done_Func) gray_raster_done /* raster_done */ 1939 ) 1940 1941 1942 /* END */ 1943 1944 1945 /* Local Variables: */ 1946 /* coding: utf-8 */ 1947 /* End: */ 1948