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