1 /***************************************************************************/ 2 /* */ 3 /* ftraster.c */ 4 /* */ 5 /* The FreeType glyph rasterizer (body). */ 6 /* */ 7 /* Copyright 1996-2016 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 `ftimage.h' and `ftmisc.h' into the $(incdir) */ 23 /* directory. Typically, you should do something like */ 24 /* */ 25 /* - copy `src/raster/ftraster.c' (this file) to your current directory */ 26 /* */ 27 /* - copy `include/freetype/ftimage.h' and `src/raster/ftmisc.h' to your */ 28 /* current directory */ 29 /* */ 30 /* - compile `ftraster' with the STANDALONE_ macro defined, as in */ 31 /* */ 32 /* cc -c -DSTANDALONE_ ftraster.c */ 33 /* */ 34 /* The renderer can be initialized with a call to */ 35 /* `ft_standard_raster.raster_new'; a bitmap can be generated */ 36 /* with a call to `ft_standard_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 /* */ 46 /* This is a rewrite of the FreeType 1.x scan-line converter */ 47 /* */ 48 /*************************************************************************/ 49 50 #ifdef STANDALONE_ 51 52 /* The size in bytes of the render pool used by the scan-line converter */ 53 /* to do all of its work. */ 54 #define FT_RENDER_POOL_SIZE 16384L 55 56 #define FT_CONFIG_STANDARD_LIBRARY_H <stdlib.h> 57 58 #include <string.h> /* for memset */ 59 60 #include "ftmisc.h" 61 #include "ftimage.h" 62 63 #else /* !STANDALONE_ */ 64 65 #include <ft2build.h> 66 #include "ftraster.h" 67 #include FT_INTERNAL_CALC_H /* for FT_MulDiv and FT_MulDiv_No_Round */ 68 69 #include "rastpic.h" 70 71 #endif /* !STANDALONE_ */ 72 73 74 /*************************************************************************/ 75 /* */ 76 /* A simple technical note on how the raster works */ 77 /* ----------------------------------------------- */ 78 /* */ 79 /* Converting an outline into a bitmap is achieved in several steps: */ 80 /* */ 81 /* 1 - Decomposing the outline into successive `profiles'. Each */ 82 /* profile is simply an array of scanline intersections on a given */ 83 /* dimension. A profile's main attributes are */ 84 /* */ 85 /* o its scanline position boundaries, i.e. `Ymin' and `Ymax' */ 86 /* */ 87 /* o an array of intersection coordinates for each scanline */ 88 /* between `Ymin' and `Ymax' */ 89 /* */ 90 /* o a direction, indicating whether it was built going `up' or */ 91 /* `down', as this is very important for filling rules */ 92 /* */ 93 /* o its drop-out mode */ 94 /* */ 95 /* 2 - Sweeping the target map's scanlines in order to compute segment */ 96 /* `spans' which are then filled. Additionally, this pass */ 97 /* performs drop-out control. */ 98 /* */ 99 /* The outline data is parsed during step 1 only. The profiles are */ 100 /* built from the bottom of the render pool, used as a stack. The */ 101 /* following graphics shows the profile list under construction: */ 102 /* */ 103 /* __________________________________________________________ _ _ */ 104 /* | | | | | */ 105 /* | profile | coordinates for | profile | coordinates for |--> */ 106 /* | 1 | profile 1 | 2 | profile 2 |--> */ 107 /* |_________|_________________|_________|_________________|__ _ _ */ 108 /* */ 109 /* ^ ^ */ 110 /* | | */ 111 /* start of render pool top */ 112 /* */ 113 /* The top of the profile stack is kept in the `top' variable. */ 114 /* */ 115 /* As you can see, a profile record is pushed on top of the render */ 116 /* pool, which is then followed by its coordinates/intersections. If */ 117 /* a change of direction is detected in the outline, a new profile is */ 118 /* generated until the end of the outline. */ 119 /* */ 120 /* Note that when all profiles have been generated, the function */ 121 /* Finalize_Profile_Table() is used to record, for each profile, its */ 122 /* bottom-most scanline as well as the scanline above its upmost */ 123 /* boundary. These positions are called `y-turns' because they (sort */ 124 /* of) correspond to local extrema. They are stored in a sorted list */ 125 /* built from the top of the render pool as a downwards stack: */ 126 /* */ 127 /* _ _ _______________________________________ */ 128 /* | | */ 129 /* <--| sorted list of | */ 130 /* <--| extrema scanlines | */ 131 /* _ _ __________________|____________________| */ 132 /* */ 133 /* ^ ^ */ 134 /* | | */ 135 /* maxBuff sizeBuff = end of pool */ 136 /* */ 137 /* This list is later used during the sweep phase in order to */ 138 /* optimize performance (see technical note on the sweep below). */ 139 /* */ 140 /* Of course, the raster detects whether the two stacks collide and */ 141 /* handles the situation properly. */ 142 /* */ 143 /*************************************************************************/ 144 145 146 /*************************************************************************/ 147 /*************************************************************************/ 148 /** **/ 149 /** CONFIGURATION MACROS **/ 150 /** **/ 151 /*************************************************************************/ 152 /*************************************************************************/ 153 154 /* define DEBUG_RASTER if you want to compile a debugging version */ 155 /* #define DEBUG_RASTER */ 156 157 158 /*************************************************************************/ 159 /*************************************************************************/ 160 /** **/ 161 /** OTHER MACROS (do not change) **/ 162 /** **/ 163 /*************************************************************************/ 164 /*************************************************************************/ 165 166 /*************************************************************************/ 167 /* */ 168 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ 169 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ 170 /* messages during execution. */ 171 /* */ 172 #undef FT_COMPONENT 173 #define FT_COMPONENT trace_raster 174 175 176 #ifdef STANDALONE_ 177 178 /* Auxiliary macros for token concatenation. */ 179 #define FT_ERR_XCAT( x, y ) x ## y 180 #define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y ) 181 182 /* This macro is used to indicate that a function parameter is unused. */ 183 /* Its purpose is simply to reduce compiler warnings. Note also that */ 184 /* simply defining it as `(void)x' doesn't avoid warnings with certain */ 185 /* ANSI compilers (e.g. LCC). */ 186 #define FT_UNUSED( x ) (x) = (x) 187 188 /* Disable the tracing mechanism for simplicity -- developers can */ 189 /* activate it easily by redefining these macros. */ 190 #ifndef FT_ERROR 191 #define FT_ERROR( x ) do { } while ( 0 ) /* nothing */ 192 #endif 193 194 #ifndef FT_TRACE 195 #define FT_TRACE( x ) do { } while ( 0 ) /* nothing */ 196 #define FT_TRACE1( x ) do { } while ( 0 ) /* nothing */ 197 #define FT_TRACE6( x ) do { } while ( 0 ) /* nothing */ 198 #define FT_TRACE7( x ) do { } while ( 0 ) /* nothing */ 199 #endif 200 201 #ifndef FT_THROW 202 #define FT_THROW( e ) FT_ERR_CAT( Raster_Err_, e ) 203 #endif 204 205 #define Raster_Err_None 0 206 #define Raster_Err_Not_Ini -1 207 #define Raster_Err_Overflow -2 208 #define Raster_Err_Neg_Height -3 209 #define Raster_Err_Invalid -4 210 #define Raster_Err_Unsupported -5 211 212 #define ft_memset memset 213 214 #define FT_DEFINE_RASTER_FUNCS( class_, glyph_format_, raster_new_, \ 215 raster_reset_, raster_set_mode_, \ 216 raster_render_, raster_done_ ) \ 217 const FT_Raster_Funcs class_ = \ 218 { \ 219 glyph_format_, \ 220 raster_new_, \ 221 raster_reset_, \ 222 raster_set_mode_, \ 223 raster_render_, \ 224 raster_done_ \ 225 }; 226 227 #else /* !STANDALONE_ */ 228 229 230 #include FT_INTERNAL_OBJECTS_H 231 #include FT_INTERNAL_DEBUG_H /* for FT_TRACE, FT_ERROR, and FT_THROW */ 232 233 #include "rasterrs.h" 234 235 #define Raster_Err_None FT_Err_Ok 236 #define Raster_Err_Not_Ini Raster_Err_Raster_Uninitialized 237 #define Raster_Err_Overflow Raster_Err_Raster_Overflow 238 #define Raster_Err_Neg_Height Raster_Err_Raster_Negative_Height 239 #define Raster_Err_Invalid Raster_Err_Invalid_Outline 240 #define Raster_Err_Unsupported Raster_Err_Cannot_Render_Glyph 241 242 243 #endif /* !STANDALONE_ */ 244 245 246 #ifndef FT_MEM_SET 247 #define FT_MEM_SET( d, s, c ) ft_memset( d, s, c ) 248 #endif 249 250 #ifndef FT_MEM_ZERO 251 #define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count ) 252 #endif 253 254 /* FMulDiv means `Fast MulDiv'; it is used in case where `b' is */ 255 /* typically a small value and the result of a*b is known to fit into */ 256 /* 32 bits. */ 257 #define FMulDiv( a, b, c ) ( (a) * (b) / (c) ) 258 259 /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */ 260 /* for clipping computations. It simply uses the FT_MulDiv() function */ 261 /* defined in `ftcalc.h'. */ 262 #define SMulDiv FT_MulDiv 263 #define SMulDiv_No_Round FT_MulDiv_No_Round 264 265 /* The rasterizer is a very general purpose component; please leave */ 266 /* the following redefinitions there (you never know your target */ 267 /* environment). */ 268 269 #ifndef TRUE 270 #define TRUE 1 271 #endif 272 273 #ifndef FALSE 274 #define FALSE 0 275 #endif 276 277 #ifndef NULL 278 #define NULL (void*)0 279 #endif 280 281 #ifndef SUCCESS 282 #define SUCCESS 0 283 #endif 284 285 #ifndef FAILURE 286 #define FAILURE 1 287 #endif 288 289 290 #define MaxBezier 32 /* The maximum number of stacked Bezier curves. */ 291 /* Setting this constant to more than 32 is a */ 292 /* pure waste of space. */ 293 294 #define Pixel_Bits 6 /* fractional bits of *input* coordinates */ 295 296 297 /*************************************************************************/ 298 /*************************************************************************/ 299 /** **/ 300 /** SIMPLE TYPE DECLARATIONS **/ 301 /** **/ 302 /*************************************************************************/ 303 /*************************************************************************/ 304 305 typedef int Int; 306 typedef unsigned int UInt; 307 typedef short Short; 308 typedef unsigned short UShort, *PUShort; 309 typedef long Long, *PLong; 310 typedef unsigned long ULong; 311 312 typedef unsigned char Byte, *PByte; 313 typedef char Bool; 314 315 316 typedef union Alignment_ 317 { 318 Long l; 319 void* p; 320 void (*f)(void); 321 322 } Alignment, *PAlignment; 323 324 325 typedef struct TPoint_ 326 { 327 Long x; 328 Long y; 329 330 } TPoint; 331 332 333 /* values for the `flags' bit field */ 334 #define Flow_Up 0x08U 335 #define Overshoot_Top 0x10U 336 #define Overshoot_Bottom 0x20U 337 338 339 /* States of each line, arc, and profile */ 340 typedef enum TStates_ 341 { 342 Unknown_State, 343 Ascending_State, 344 Descending_State, 345 Flat_State 346 347 } TStates; 348 349 350 typedef struct TProfile_ TProfile; 351 typedef TProfile* PProfile; 352 353 struct TProfile_ 354 { 355 FT_F26Dot6 X; /* current coordinate during sweep */ 356 PProfile link; /* link to next profile (various purposes) */ 357 PLong offset; /* start of profile's data in render pool */ 358 UShort flags; /* Bit 0-2: drop-out mode */ 359 /* Bit 3: profile orientation (up/down) */ 360 /* Bit 4: is top profile? */ 361 /* Bit 5: is bottom profile? */ 362 Long height; /* profile's height in scanlines */ 363 Long start; /* profile's starting scanline */ 364 365 Int countL; /* number of lines to step before this */ 366 /* profile becomes drawable */ 367 368 PProfile next; /* next profile in same contour, used */ 369 /* during drop-out control */ 370 }; 371 372 typedef PProfile TProfileList; 373 typedef PProfile* PProfileList; 374 375 376 /* Simple record used to implement a stack of bands, required */ 377 /* by the sub-banding mechanism */ 378 typedef struct black_TBand_ 379 { 380 Short y_min; /* band's minimum */ 381 Short y_max; /* band's maximum */ 382 383 } black_TBand; 384 385 386 #define AlignProfileSize \ 387 ( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( Long ) ) 388 389 390 #undef RAS_ARG 391 #undef RAS_ARGS 392 #undef RAS_VAR 393 #undef RAS_VARS 394 395 #ifdef FT_STATIC_RASTER 396 397 398 #define RAS_ARGS /* void */ 399 #define RAS_ARG /* void */ 400 401 #define RAS_VARS /* void */ 402 #define RAS_VAR /* void */ 403 404 #define FT_UNUSED_RASTER do { } while ( 0 ) 405 406 407 #else /* !FT_STATIC_RASTER */ 408 409 410 #define RAS_ARGS black_PWorker worker, 411 #define RAS_ARG black_PWorker worker 412 413 #define RAS_VARS worker, 414 #define RAS_VAR worker 415 416 #define FT_UNUSED_RASTER FT_UNUSED( worker ) 417 418 419 #endif /* !FT_STATIC_RASTER */ 420 421 422 typedef struct black_TWorker_ black_TWorker, *black_PWorker; 423 424 425 /* prototypes used for sweep function dispatch */ 426 typedef void 427 Function_Sweep_Init( RAS_ARGS Short* min, 428 Short* max ); 429 430 typedef void 431 Function_Sweep_Span( RAS_ARGS Short y, 432 FT_F26Dot6 x1, 433 FT_F26Dot6 x2, 434 PProfile left, 435 PProfile right ); 436 437 typedef void 438 Function_Sweep_Step( RAS_ARG ); 439 440 441 /* NOTE: These operations are only valid on 2's complement processors */ 442 #undef FLOOR 443 #undef CEILING 444 #undef TRUNC 445 #undef SCALED 446 447 #define FLOOR( x ) ( (x) & -ras.precision ) 448 #define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision ) 449 #define TRUNC( x ) ( (Long)(x) >> ras.precision_bits ) 450 #define FRAC( x ) ( (x) & ( ras.precision - 1 ) ) 451 #define SCALED( x ) ( ( (x) < 0 ? -( -(x) << ras.scale_shift ) \ 452 : ( (x) << ras.scale_shift ) ) \ 453 - ras.precision_half ) 454 455 #define IS_BOTTOM_OVERSHOOT( x ) \ 456 (Bool)( CEILING( x ) - x >= ras.precision_half ) 457 #define IS_TOP_OVERSHOOT( x ) \ 458 (Bool)( x - FLOOR( x ) >= ras.precision_half ) 459 460 #if FT_RENDER_POOL_SIZE > 2048 461 #define FT_MAX_BLACK_POOL ( FT_RENDER_POOL_SIZE / sizeof ( Long ) ) 462 #else 463 #define FT_MAX_BLACK_POOL ( 2048 / sizeof ( Long ) ) 464 #endif 465 466 /* The most used variables are positioned at the top of the structure. */ 467 /* Thus, their offset can be coded with less opcodes, resulting in a */ 468 /* smaller executable. */ 469 470 struct black_TWorker_ 471 { 472 Int precision_bits; /* precision related variables */ 473 Int precision; 474 Int precision_half; 475 Int precision_shift; 476 Int precision_step; 477 Int precision_jitter; 478 479 Int scale_shift; /* == precision_shift for bitmaps */ 480 /* == precision_shift+1 for pixmaps */ 481 482 PLong buff; /* The profiles buffer */ 483 PLong sizeBuff; /* Render pool size */ 484 PLong maxBuff; /* Profiles buffer size */ 485 PLong top; /* Current cursor in buffer */ 486 487 FT_Error error; 488 489 Int numTurns; /* number of Y-turns in outline */ 490 491 TPoint* arc; /* current Bezier arc pointer */ 492 493 UShort bWidth; /* target bitmap width */ 494 PByte bTarget; /* target bitmap buffer */ 495 PByte gTarget; /* target pixmap buffer */ 496 497 Long lastX, lastY; 498 Long minY, maxY; 499 500 UShort num_Profs; /* current number of profiles */ 501 502 Bool fresh; /* signals a fresh new profile which */ 503 /* `start' field must be completed */ 504 Bool joint; /* signals that the last arc ended */ 505 /* exactly on a scanline. Allows */ 506 /* removal of doublets */ 507 PProfile cProfile; /* current profile */ 508 PProfile fProfile; /* head of linked list of profiles */ 509 PProfile gProfile; /* contour's first profile in case */ 510 /* of impact */ 511 512 TStates state; /* rendering state */ 513 514 FT_Bitmap target; /* description of target bit/pixmap */ 515 FT_Outline outline; 516 517 Long traceOfs; /* current offset in target bitmap */ 518 Long traceG; /* current offset in target pixmap */ 519 520 Short traceIncr; /* sweep's increment in target bitmap */ 521 522 /* dispatch variables */ 523 524 Function_Sweep_Init* Proc_Sweep_Init; 525 Function_Sweep_Span* Proc_Sweep_Span; 526 Function_Sweep_Span* Proc_Sweep_Drop; 527 Function_Sweep_Step* Proc_Sweep_Step; 528 529 Byte dropOutControl; /* current drop_out control method */ 530 531 Bool second_pass; /* indicates whether a horizontal pass */ 532 /* should be performed to control */ 533 /* drop-out accurately when calling */ 534 /* Render_Glyph. */ 535 536 TPoint arcs[3 * MaxBezier + 1]; /* The Bezier stack */ 537 538 black_TBand band_stack[16]; /* band stack used for sub-banding */ 539 Int band_top; /* band stack top */ 540 541 }; 542 543 544 typedef struct black_TRaster_ 545 { 546 void* memory; 547 548 } black_TRaster, *black_PRaster; 549 550 #ifdef FT_STATIC_RASTER 551 552 static black_TWorker cur_ras; 553 #define ras cur_ras 554 555 #else /* !FT_STATIC_RASTER */ 556 557 #define ras (*worker) 558 559 #endif /* !FT_STATIC_RASTER */ 560 561 562 /*************************************************************************/ 563 /*************************************************************************/ 564 /** **/ 565 /** PROFILES COMPUTATION **/ 566 /** **/ 567 /*************************************************************************/ 568 /*************************************************************************/ 569 570 571 /*************************************************************************/ 572 /* */ 573 /* <Function> */ 574 /* Set_High_Precision */ 575 /* */ 576 /* <Description> */ 577 /* Set precision variables according to param flag. */ 578 /* */ 579 /* <Input> */ 580 /* High :: Set to True for high precision (typically for ppem < 24), */ 581 /* false otherwise. */ 582 /* */ 583 static void 584 Set_High_Precision( RAS_ARGS Int High ) 585 { 586 /* 587 * `precision_step' is used in `Bezier_Up' to decide when to split a 588 * given y-monotonous Bezier arc that crosses a scanline before 589 * approximating it as a straight segment. The default value of 32 (for 590 * low accuracy) corresponds to 591 * 592 * 32 / 64 == 0.5 pixels, 593 * 594 * while for the high accuracy case we have 595 * 596 * 256 / (1 << 12) = 0.0625 pixels. 597 * 598 * `precision_jitter' is an epsilon threshold used in 599 * `Vertical_Sweep_Span' to deal with small imperfections in the Bezier 600 * decomposition (after all, we are working with approximations only); 601 * it avoids switching on additional pixels which would cause artifacts 602 * otherwise. 603 * 604 * The value of `precision_jitter' has been determined heuristically. 605 * 606 */ 607 608 if ( High ) 609 { 610 ras.precision_bits = 12; 611 ras.precision_step = 256; 612 ras.precision_jitter = 30; 613 } 614 else 615 { 616 ras.precision_bits = 6; 617 ras.precision_step = 32; 618 ras.precision_jitter = 2; 619 } 620 621 FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" )); 622 623 ras.precision = 1 << ras.precision_bits; 624 ras.precision_half = ras.precision / 2; 625 ras.precision_shift = ras.precision_bits - Pixel_Bits; 626 } 627 628 629 /*************************************************************************/ 630 /* */ 631 /* <Function> */ 632 /* New_Profile */ 633 /* */ 634 /* <Description> */ 635 /* Create a new profile in the render pool. */ 636 /* */ 637 /* <Input> */ 638 /* aState :: The state/orientation of the new profile. */ 639 /* */ 640 /* overshoot :: Whether the profile's unrounded start position */ 641 /* differs by at least a half pixel. */ 642 /* */ 643 /* <Return> */ 644 /* SUCCESS on success. FAILURE in case of overflow or of incoherent */ 645 /* profile. */ 646 /* */ 647 static Bool 648 New_Profile( RAS_ARGS TStates aState, 649 Bool overshoot ) 650 { 651 if ( !ras.fProfile ) 652 { 653 ras.cProfile = (PProfile)ras.top; 654 ras.fProfile = ras.cProfile; 655 ras.top += AlignProfileSize; 656 } 657 658 if ( ras.top >= ras.maxBuff ) 659 { 660 ras.error = FT_THROW( Overflow ); 661 return FAILURE; 662 } 663 664 ras.cProfile->flags = 0; 665 ras.cProfile->start = 0; 666 ras.cProfile->height = 0; 667 ras.cProfile->offset = ras.top; 668 ras.cProfile->link = (PProfile)0; 669 ras.cProfile->next = (PProfile)0; 670 ras.cProfile->flags = ras.dropOutControl; 671 672 switch ( aState ) 673 { 674 case Ascending_State: 675 ras.cProfile->flags |= Flow_Up; 676 if ( overshoot ) 677 ras.cProfile->flags |= Overshoot_Bottom; 678 679 FT_TRACE6(( " new ascending profile = %p\n", ras.cProfile )); 680 break; 681 682 case Descending_State: 683 if ( overshoot ) 684 ras.cProfile->flags |= Overshoot_Top; 685 FT_TRACE6(( " new descending profile = %p\n", ras.cProfile )); 686 break; 687 688 default: 689 FT_ERROR(( "New_Profile: invalid profile direction\n" )); 690 ras.error = FT_THROW( Invalid ); 691 return FAILURE; 692 } 693 694 if ( !ras.gProfile ) 695 ras.gProfile = ras.cProfile; 696 697 ras.state = aState; 698 ras.fresh = TRUE; 699 ras.joint = FALSE; 700 701 return SUCCESS; 702 } 703 704 705 /*************************************************************************/ 706 /* */ 707 /* <Function> */ 708 /* End_Profile */ 709 /* */ 710 /* <Description> */ 711 /* Finalize the current profile. */ 712 /* */ 713 /* <Input> */ 714 /* overshoot :: Whether the profile's unrounded end position differs */ 715 /* by at least a half pixel. */ 716 /* */ 717 /* <Return> */ 718 /* SUCCESS on success. FAILURE in case of overflow or incoherency. */ 719 /* */ 720 static Bool 721 End_Profile( RAS_ARGS Bool overshoot ) 722 { 723 Long h; 724 725 726 h = (Long)( ras.top - ras.cProfile->offset ); 727 728 if ( h < 0 ) 729 { 730 FT_ERROR(( "End_Profile: negative height encountered\n" )); 731 ras.error = FT_THROW( Neg_Height ); 732 return FAILURE; 733 } 734 735 if ( h > 0 ) 736 { 737 PProfile oldProfile; 738 739 740 FT_TRACE6(( " ending profile %p, start = %ld, height = %ld\n", 741 ras.cProfile, ras.cProfile->start, h )); 742 743 ras.cProfile->height = h; 744 if ( overshoot ) 745 { 746 if ( ras.cProfile->flags & Flow_Up ) 747 ras.cProfile->flags |= Overshoot_Top; 748 else 749 ras.cProfile->flags |= Overshoot_Bottom; 750 } 751 752 oldProfile = ras.cProfile; 753 ras.cProfile = (PProfile)ras.top; 754 755 ras.top += AlignProfileSize; 756 757 ras.cProfile->height = 0; 758 ras.cProfile->offset = ras.top; 759 760 oldProfile->next = ras.cProfile; 761 ras.num_Profs++; 762 } 763 764 if ( ras.top >= ras.maxBuff ) 765 { 766 FT_TRACE1(( "overflow in End_Profile\n" )); 767 ras.error = FT_THROW( Overflow ); 768 return FAILURE; 769 } 770 771 ras.joint = FALSE; 772 773 return SUCCESS; 774 } 775 776 777 /*************************************************************************/ 778 /* */ 779 /* <Function> */ 780 /* Insert_Y_Turn */ 781 /* */ 782 /* <Description> */ 783 /* Insert a salient into the sorted list placed on top of the render */ 784 /* pool. */ 785 /* */ 786 /* <Input> */ 787 /* New y scanline position. */ 788 /* */ 789 /* <Return> */ 790 /* SUCCESS on success. FAILURE in case of overflow. */ 791 /* */ 792 static Bool 793 Insert_Y_Turn( RAS_ARGS Int y ) 794 { 795 PLong y_turns; 796 Int n; 797 798 799 n = ras.numTurns - 1; 800 y_turns = ras.sizeBuff - ras.numTurns; 801 802 /* look for first y value that is <= */ 803 while ( n >= 0 && y < y_turns[n] ) 804 n--; 805 806 /* if it is <, simply insert it, ignore if == */ 807 if ( n >= 0 && y > y_turns[n] ) 808 do 809 { 810 Int y2 = (Int)y_turns[n]; 811 812 813 y_turns[n] = y; 814 y = y2; 815 } while ( --n >= 0 ); 816 817 if ( n < 0 ) 818 { 819 ras.maxBuff--; 820 if ( ras.maxBuff <= ras.top ) 821 { 822 ras.error = FT_THROW( Overflow ); 823 return FAILURE; 824 } 825 ras.numTurns++; 826 ras.sizeBuff[-ras.numTurns] = y; 827 } 828 829 return SUCCESS; 830 } 831 832 833 /*************************************************************************/ 834 /* */ 835 /* <Function> */ 836 /* Finalize_Profile_Table */ 837 /* */ 838 /* <Description> */ 839 /* Adjust all links in the profiles list. */ 840 /* */ 841 /* <Return> */ 842 /* SUCCESS on success. FAILURE in case of overflow. */ 843 /* */ 844 static Bool 845 Finalize_Profile_Table( RAS_ARG ) 846 { 847 UShort n; 848 PProfile p; 849 850 851 n = ras.num_Profs; 852 p = ras.fProfile; 853 854 if ( n > 1 && p ) 855 { 856 do 857 { 858 Int bottom, top; 859 860 861 if ( n > 1 ) 862 p->link = (PProfile)( p->offset + p->height ); 863 else 864 p->link = NULL; 865 866 if ( p->flags & Flow_Up ) 867 { 868 bottom = (Int)p->start; 869 top = (Int)( p->start + p->height - 1 ); 870 } 871 else 872 { 873 bottom = (Int)( p->start - p->height + 1 ); 874 top = (Int)p->start; 875 p->start = bottom; 876 p->offset += p->height - 1; 877 } 878 879 if ( Insert_Y_Turn( RAS_VARS bottom ) || 880 Insert_Y_Turn( RAS_VARS top + 1 ) ) 881 return FAILURE; 882 883 p = p->link; 884 } while ( --n ); 885 } 886 else 887 ras.fProfile = NULL; 888 889 return SUCCESS; 890 } 891 892 893 /*************************************************************************/ 894 /* */ 895 /* <Function> */ 896 /* Split_Conic */ 897 /* */ 898 /* <Description> */ 899 /* Subdivide one conic Bezier into two joint sub-arcs in the Bezier */ 900 /* stack. */ 901 /* */ 902 /* <Input> */ 903 /* None (subdivided Bezier is taken from the top of the stack). */ 904 /* */ 905 /* <Note> */ 906 /* This routine is the `beef' of this component. It is _the_ inner */ 907 /* loop that should be optimized to hell to get the best performance. */ 908 /* */ 909 static void 910 Split_Conic( TPoint* base ) 911 { 912 Long a, b; 913 914 915 base[4].x = base[2].x; 916 b = base[1].x; 917 a = base[3].x = ( base[2].x + b ) / 2; 918 b = base[1].x = ( base[0].x + b ) / 2; 919 base[2].x = ( a + b ) / 2; 920 921 base[4].y = base[2].y; 922 b = base[1].y; 923 a = base[3].y = ( base[2].y + b ) / 2; 924 b = base[1].y = ( base[0].y + b ) / 2; 925 base[2].y = ( a + b ) / 2; 926 927 /* hand optimized. gcc doesn't seem to be too good at common */ 928 /* expression substitution and instruction scheduling ;-) */ 929 } 930 931 932 /*************************************************************************/ 933 /* */ 934 /* <Function> */ 935 /* Split_Cubic */ 936 /* */ 937 /* <Description> */ 938 /* Subdivide a third-order Bezier arc into two joint sub-arcs in the */ 939 /* Bezier stack. */ 940 /* */ 941 /* <Note> */ 942 /* This routine is the `beef' of the component. It is one of _the_ */ 943 /* inner loops that should be optimized like hell to get the best */ 944 /* performance. */ 945 /* */ 946 static void 947 Split_Cubic( TPoint* base ) 948 { 949 Long a, b, c, d; 950 951 952 base[6].x = base[3].x; 953 c = base[1].x; 954 d = base[2].x; 955 base[1].x = a = ( base[0].x + c + 1 ) >> 1; 956 base[5].x = b = ( base[3].x + d + 1 ) >> 1; 957 c = ( c + d + 1 ) >> 1; 958 base[2].x = a = ( a + c + 1 ) >> 1; 959 base[4].x = b = ( b + c + 1 ) >> 1; 960 base[3].x = ( a + b + 1 ) >> 1; 961 962 base[6].y = base[3].y; 963 c = base[1].y; 964 d = base[2].y; 965 base[1].y = a = ( base[0].y + c + 1 ) >> 1; 966 base[5].y = b = ( base[3].y + d + 1 ) >> 1; 967 c = ( c + d + 1 ) >> 1; 968 base[2].y = a = ( a + c + 1 ) >> 1; 969 base[4].y = b = ( b + c + 1 ) >> 1; 970 base[3].y = ( a + b + 1 ) >> 1; 971 } 972 973 974 /*************************************************************************/ 975 /* */ 976 /* <Function> */ 977 /* Line_Up */ 978 /* */ 979 /* <Description> */ 980 /* Compute the x-coordinates of an ascending line segment and store */ 981 /* them in the render pool. */ 982 /* */ 983 /* <Input> */ 984 /* x1 :: The x-coordinate of the segment's start point. */ 985 /* */ 986 /* y1 :: The y-coordinate of the segment's start point. */ 987 /* */ 988 /* x2 :: The x-coordinate of the segment's end point. */ 989 /* */ 990 /* y2 :: The y-coordinate of the segment's end point. */ 991 /* */ 992 /* miny :: A lower vertical clipping bound value. */ 993 /* */ 994 /* maxy :: An upper vertical clipping bound value. */ 995 /* */ 996 /* <Return> */ 997 /* SUCCESS on success, FAILURE on render pool overflow. */ 998 /* */ 999 static Bool 1000 Line_Up( RAS_ARGS Long x1, 1001 Long y1, 1002 Long x2, 1003 Long y2, 1004 Long miny, 1005 Long maxy ) 1006 { 1007 Long Dx, Dy; 1008 Int e1, e2, f1, f2, size; /* XXX: is `Short' sufficient? */ 1009 Long Ix, Rx, Ax; 1010 1011 PLong top; 1012 1013 1014 Dx = x2 - x1; 1015 Dy = y2 - y1; 1016 1017 if ( Dy <= 0 || y2 < miny || y1 > maxy ) 1018 return SUCCESS; 1019 1020 if ( y1 < miny ) 1021 { 1022 /* Take care: miny-y1 can be a very large value; we use */ 1023 /* a slow MulDiv function to avoid clipping bugs */ 1024 x1 += SMulDiv( Dx, miny - y1, Dy ); 1025 e1 = (Int)TRUNC( miny ); 1026 f1 = 0; 1027 } 1028 else 1029 { 1030 e1 = (Int)TRUNC( y1 ); 1031 f1 = (Int)FRAC( y1 ); 1032 } 1033 1034 if ( y2 > maxy ) 1035 { 1036 /* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */ 1037 e2 = (Int)TRUNC( maxy ); 1038 f2 = 0; 1039 } 1040 else 1041 { 1042 e2 = (Int)TRUNC( y2 ); 1043 f2 = (Int)FRAC( y2 ); 1044 } 1045 1046 if ( f1 > 0 ) 1047 { 1048 if ( e1 == e2 ) 1049 return SUCCESS; 1050 else 1051 { 1052 x1 += SMulDiv( Dx, ras.precision - f1, Dy ); 1053 e1 += 1; 1054 } 1055 } 1056 else 1057 if ( ras.joint ) 1058 { 1059 ras.top--; 1060 ras.joint = FALSE; 1061 } 1062 1063 ras.joint = (char)( f2 == 0 ); 1064 1065 if ( ras.fresh ) 1066 { 1067 ras.cProfile->start = e1; 1068 ras.fresh = FALSE; 1069 } 1070 1071 size = e2 - e1 + 1; 1072 if ( ras.top + size >= ras.maxBuff ) 1073 { 1074 ras.error = FT_THROW( Overflow ); 1075 return FAILURE; 1076 } 1077 1078 if ( Dx > 0 ) 1079 { 1080 Ix = SMulDiv_No_Round( ras.precision, Dx, Dy ); 1081 Rx = ( ras.precision * Dx ) % Dy; 1082 Dx = 1; 1083 } 1084 else 1085 { 1086 Ix = -SMulDiv_No_Round( ras.precision, -Dx, Dy ); 1087 Rx = ( ras.precision * -Dx ) % Dy; 1088 Dx = -1; 1089 } 1090 1091 Ax = -Dy; 1092 top = ras.top; 1093 1094 while ( size > 0 ) 1095 { 1096 *top++ = x1; 1097 1098 x1 += Ix; 1099 Ax += Rx; 1100 if ( Ax >= 0 ) 1101 { 1102 Ax -= Dy; 1103 x1 += Dx; 1104 } 1105 size--; 1106 } 1107 1108 ras.top = top; 1109 return SUCCESS; 1110 } 1111 1112 1113 /*************************************************************************/ 1114 /* */ 1115 /* <Function> */ 1116 /* Line_Down */ 1117 /* */ 1118 /* <Description> */ 1119 /* Compute the x-coordinates of an descending line segment and store */ 1120 /* them in the render pool. */ 1121 /* */ 1122 /* <Input> */ 1123 /* x1 :: The x-coordinate of the segment's start point. */ 1124 /* */ 1125 /* y1 :: The y-coordinate of the segment's start point. */ 1126 /* */ 1127 /* x2 :: The x-coordinate of the segment's end point. */ 1128 /* */ 1129 /* y2 :: The y-coordinate of the segment's end point. */ 1130 /* */ 1131 /* miny :: A lower vertical clipping bound value. */ 1132 /* */ 1133 /* maxy :: An upper vertical clipping bound value. */ 1134 /* */ 1135 /* <Return> */ 1136 /* SUCCESS on success, FAILURE on render pool overflow. */ 1137 /* */ 1138 static Bool 1139 Line_Down( RAS_ARGS Long x1, 1140 Long y1, 1141 Long x2, 1142 Long y2, 1143 Long miny, 1144 Long maxy ) 1145 { 1146 Bool result, fresh; 1147 1148 1149 fresh = ras.fresh; 1150 1151 result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny ); 1152 1153 if ( fresh && !ras.fresh ) 1154 ras.cProfile->start = -ras.cProfile->start; 1155 1156 return result; 1157 } 1158 1159 1160 /* A function type describing the functions used to split Bezier arcs */ 1161 typedef void (*TSplitter)( TPoint* base ); 1162 1163 1164 /*************************************************************************/ 1165 /* */ 1166 /* <Function> */ 1167 /* Bezier_Up */ 1168 /* */ 1169 /* <Description> */ 1170 /* Compute the x-coordinates of an ascending Bezier arc and store */ 1171 /* them in the render pool. */ 1172 /* */ 1173 /* <Input> */ 1174 /* degree :: The degree of the Bezier arc (either 2 or 3). */ 1175 /* */ 1176 /* splitter :: The function to split Bezier arcs. */ 1177 /* */ 1178 /* miny :: A lower vertical clipping bound value. */ 1179 /* */ 1180 /* maxy :: An upper vertical clipping bound value. */ 1181 /* */ 1182 /* <Return> */ 1183 /* SUCCESS on success, FAILURE on render pool overflow. */ 1184 /* */ 1185 static Bool 1186 Bezier_Up( RAS_ARGS Int degree, 1187 TSplitter splitter, 1188 Long miny, 1189 Long maxy ) 1190 { 1191 Long y1, y2, e, e2, e0; 1192 Short f1; 1193 1194 TPoint* arc; 1195 TPoint* start_arc; 1196 1197 PLong top; 1198 1199 1200 arc = ras.arc; 1201 y1 = arc[degree].y; 1202 y2 = arc[0].y; 1203 top = ras.top; 1204 1205 if ( y2 < miny || y1 > maxy ) 1206 goto Fin; 1207 1208 e2 = FLOOR( y2 ); 1209 1210 if ( e2 > maxy ) 1211 e2 = maxy; 1212 1213 e0 = miny; 1214 1215 if ( y1 < miny ) 1216 e = miny; 1217 else 1218 { 1219 e = CEILING( y1 ); 1220 f1 = (Short)( FRAC( y1 ) ); 1221 e0 = e; 1222 1223 if ( f1 == 0 ) 1224 { 1225 if ( ras.joint ) 1226 { 1227 top--; 1228 ras.joint = FALSE; 1229 } 1230 1231 *top++ = arc[degree].x; 1232 1233 e += ras.precision; 1234 } 1235 } 1236 1237 if ( ras.fresh ) 1238 { 1239 ras.cProfile->start = TRUNC( e0 ); 1240 ras.fresh = FALSE; 1241 } 1242 1243 if ( e2 < e ) 1244 goto Fin; 1245 1246 if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff ) 1247 { 1248 ras.top = top; 1249 ras.error = FT_THROW( Overflow ); 1250 return FAILURE; 1251 } 1252 1253 start_arc = arc; 1254 1255 do 1256 { 1257 ras.joint = FALSE; 1258 1259 y2 = arc[0].y; 1260 1261 if ( y2 > e ) 1262 { 1263 y1 = arc[degree].y; 1264 if ( y2 - y1 >= ras.precision_step ) 1265 { 1266 splitter( arc ); 1267 arc += degree; 1268 } 1269 else 1270 { 1271 *top++ = arc[degree].x + FMulDiv( arc[0].x - arc[degree].x, 1272 e - y1, y2 - y1 ); 1273 arc -= degree; 1274 e += ras.precision; 1275 } 1276 } 1277 else 1278 { 1279 if ( y2 == e ) 1280 { 1281 ras.joint = TRUE; 1282 *top++ = arc[0].x; 1283 1284 e += ras.precision; 1285 } 1286 arc -= degree; 1287 } 1288 } while ( arc >= start_arc && e <= e2 ); 1289 1290 Fin: 1291 ras.top = top; 1292 ras.arc -= degree; 1293 return SUCCESS; 1294 } 1295 1296 1297 /*************************************************************************/ 1298 /* */ 1299 /* <Function> */ 1300 /* Bezier_Down */ 1301 /* */ 1302 /* <Description> */ 1303 /* Compute the x-coordinates of an descending Bezier arc and store */ 1304 /* them in the render pool. */ 1305 /* */ 1306 /* <Input> */ 1307 /* degree :: The degree of the Bezier arc (either 2 or 3). */ 1308 /* */ 1309 /* splitter :: The function to split Bezier arcs. */ 1310 /* */ 1311 /* miny :: A lower vertical clipping bound value. */ 1312 /* */ 1313 /* maxy :: An upper vertical clipping bound value. */ 1314 /* */ 1315 /* <Return> */ 1316 /* SUCCESS on success, FAILURE on render pool overflow. */ 1317 /* */ 1318 static Bool 1319 Bezier_Down( RAS_ARGS Int degree, 1320 TSplitter splitter, 1321 Long miny, 1322 Long maxy ) 1323 { 1324 TPoint* arc = ras.arc; 1325 Bool result, fresh; 1326 1327 1328 arc[0].y = -arc[0].y; 1329 arc[1].y = -arc[1].y; 1330 arc[2].y = -arc[2].y; 1331 if ( degree > 2 ) 1332 arc[3].y = -arc[3].y; 1333 1334 fresh = ras.fresh; 1335 1336 result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny ); 1337 1338 if ( fresh && !ras.fresh ) 1339 ras.cProfile->start = -ras.cProfile->start; 1340 1341 arc[0].y = -arc[0].y; 1342 return result; 1343 } 1344 1345 1346 /*************************************************************************/ 1347 /* */ 1348 /* <Function> */ 1349 /* Line_To */ 1350 /* */ 1351 /* <Description> */ 1352 /* Inject a new line segment and adjust the Profiles list. */ 1353 /* */ 1354 /* <Input> */ 1355 /* x :: The x-coordinate of the segment's end point (its start point */ 1356 /* is stored in `lastX'). */ 1357 /* */ 1358 /* y :: The y-coordinate of the segment's end point (its start point */ 1359 /* is stored in `lastY'). */ 1360 /* */ 1361 /* <Return> */ 1362 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */ 1363 /* profile. */ 1364 /* */ 1365 static Bool 1366 Line_To( RAS_ARGS Long x, 1367 Long y ) 1368 { 1369 /* First, detect a change of direction */ 1370 1371 switch ( ras.state ) 1372 { 1373 case Unknown_State: 1374 if ( y > ras.lastY ) 1375 { 1376 if ( New_Profile( RAS_VARS Ascending_State, 1377 IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ) 1378 return FAILURE; 1379 } 1380 else 1381 { 1382 if ( y < ras.lastY ) 1383 if ( New_Profile( RAS_VARS Descending_State, 1384 IS_TOP_OVERSHOOT( ras.lastY ) ) ) 1385 return FAILURE; 1386 } 1387 break; 1388 1389 case Ascending_State: 1390 if ( y < ras.lastY ) 1391 { 1392 if ( End_Profile( RAS_VARS IS_TOP_OVERSHOOT( ras.lastY ) ) || 1393 New_Profile( RAS_VARS Descending_State, 1394 IS_TOP_OVERSHOOT( ras.lastY ) ) ) 1395 return FAILURE; 1396 } 1397 break; 1398 1399 case Descending_State: 1400 if ( y > ras.lastY ) 1401 { 1402 if ( End_Profile( RAS_VARS IS_BOTTOM_OVERSHOOT( ras.lastY ) ) || 1403 New_Profile( RAS_VARS Ascending_State, 1404 IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ) 1405 return FAILURE; 1406 } 1407 break; 1408 1409 default: 1410 ; 1411 } 1412 1413 /* Then compute the lines */ 1414 1415 switch ( ras.state ) 1416 { 1417 case Ascending_State: 1418 if ( Line_Up( RAS_VARS ras.lastX, ras.lastY, 1419 x, y, ras.minY, ras.maxY ) ) 1420 return FAILURE; 1421 break; 1422 1423 case Descending_State: 1424 if ( Line_Down( RAS_VARS ras.lastX, ras.lastY, 1425 x, y, ras.minY, ras.maxY ) ) 1426 return FAILURE; 1427 break; 1428 1429 default: 1430 ; 1431 } 1432 1433 ras.lastX = x; 1434 ras.lastY = y; 1435 1436 return SUCCESS; 1437 } 1438 1439 1440 /*************************************************************************/ 1441 /* */ 1442 /* <Function> */ 1443 /* Conic_To */ 1444 /* */ 1445 /* <Description> */ 1446 /* Inject a new conic arc and adjust the profile list. */ 1447 /* */ 1448 /* <Input> */ 1449 /* cx :: The x-coordinate of the arc's new control point. */ 1450 /* */ 1451 /* cy :: The y-coordinate of the arc's new control point. */ 1452 /* */ 1453 /* x :: The x-coordinate of the arc's end point (its start point is */ 1454 /* stored in `lastX'). */ 1455 /* */ 1456 /* y :: The y-coordinate of the arc's end point (its start point is */ 1457 /* stored in `lastY'). */ 1458 /* */ 1459 /* <Return> */ 1460 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */ 1461 /* profile. */ 1462 /* */ 1463 static Bool 1464 Conic_To( RAS_ARGS Long cx, 1465 Long cy, 1466 Long x, 1467 Long y ) 1468 { 1469 Long y1, y2, y3, x3, ymin, ymax; 1470 TStates state_bez; 1471 1472 1473 ras.arc = ras.arcs; 1474 ras.arc[2].x = ras.lastX; 1475 ras.arc[2].y = ras.lastY; 1476 ras.arc[1].x = cx; 1477 ras.arc[1].y = cy; 1478 ras.arc[0].x = x; 1479 ras.arc[0].y = y; 1480 1481 do 1482 { 1483 y1 = ras.arc[2].y; 1484 y2 = ras.arc[1].y; 1485 y3 = ras.arc[0].y; 1486 x3 = ras.arc[0].x; 1487 1488 /* first, categorize the Bezier arc */ 1489 1490 if ( y1 <= y3 ) 1491 { 1492 ymin = y1; 1493 ymax = y3; 1494 } 1495 else 1496 { 1497 ymin = y3; 1498 ymax = y1; 1499 } 1500 1501 if ( y2 < ymin || y2 > ymax ) 1502 { 1503 /* this arc has no given direction, split it! */ 1504 Split_Conic( ras.arc ); 1505 ras.arc += 2; 1506 } 1507 else if ( y1 == y3 ) 1508 { 1509 /* this arc is flat, ignore it and pop it from the Bezier stack */ 1510 ras.arc -= 2; 1511 } 1512 else 1513 { 1514 /* the arc is y-monotonous, either ascending or descending */ 1515 /* detect a change of direction */ 1516 state_bez = y1 < y3 ? Ascending_State : Descending_State; 1517 if ( ras.state != state_bez ) 1518 { 1519 Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 ) 1520 : IS_TOP_OVERSHOOT( y1 ); 1521 1522 1523 /* finalize current profile if any */ 1524 if ( ras.state != Unknown_State && 1525 End_Profile( RAS_VARS o ) ) 1526 goto Fail; 1527 1528 /* create a new profile */ 1529 if ( New_Profile( RAS_VARS state_bez, o ) ) 1530 goto Fail; 1531 } 1532 1533 /* now call the appropriate routine */ 1534 if ( state_bez == Ascending_State ) 1535 { 1536 if ( Bezier_Up( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) ) 1537 goto Fail; 1538 } 1539 else 1540 if ( Bezier_Down( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) ) 1541 goto Fail; 1542 } 1543 1544 } while ( ras.arc >= ras.arcs ); 1545 1546 ras.lastX = x3; 1547 ras.lastY = y3; 1548 1549 return SUCCESS; 1550 1551 Fail: 1552 return FAILURE; 1553 } 1554 1555 1556 /*************************************************************************/ 1557 /* */ 1558 /* <Function> */ 1559 /* Cubic_To */ 1560 /* */ 1561 /* <Description> */ 1562 /* Inject a new cubic arc and adjust the profile list. */ 1563 /* */ 1564 /* <Input> */ 1565 /* cx1 :: The x-coordinate of the arc's first new control point. */ 1566 /* */ 1567 /* cy1 :: The y-coordinate of the arc's first new control point. */ 1568 /* */ 1569 /* cx2 :: The x-coordinate of the arc's second new control point. */ 1570 /* */ 1571 /* cy2 :: The y-coordinate of the arc's second new control point. */ 1572 /* */ 1573 /* x :: The x-coordinate of the arc's end point (its start point is */ 1574 /* stored in `lastX'). */ 1575 /* */ 1576 /* y :: The y-coordinate of the arc's end point (its start point is */ 1577 /* stored in `lastY'). */ 1578 /* */ 1579 /* <Return> */ 1580 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */ 1581 /* profile. */ 1582 /* */ 1583 static Bool 1584 Cubic_To( RAS_ARGS Long cx1, 1585 Long cy1, 1586 Long cx2, 1587 Long cy2, 1588 Long x, 1589 Long y ) 1590 { 1591 Long y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2; 1592 TStates state_bez; 1593 1594 1595 ras.arc = ras.arcs; 1596 ras.arc[3].x = ras.lastX; 1597 ras.arc[3].y = ras.lastY; 1598 ras.arc[2].x = cx1; 1599 ras.arc[2].y = cy1; 1600 ras.arc[1].x = cx2; 1601 ras.arc[1].y = cy2; 1602 ras.arc[0].x = x; 1603 ras.arc[0].y = y; 1604 1605 do 1606 { 1607 y1 = ras.arc[3].y; 1608 y2 = ras.arc[2].y; 1609 y3 = ras.arc[1].y; 1610 y4 = ras.arc[0].y; 1611 x4 = ras.arc[0].x; 1612 1613 /* first, categorize the Bezier arc */ 1614 1615 if ( y1 <= y4 ) 1616 { 1617 ymin1 = y1; 1618 ymax1 = y4; 1619 } 1620 else 1621 { 1622 ymin1 = y4; 1623 ymax1 = y1; 1624 } 1625 1626 if ( y2 <= y3 ) 1627 { 1628 ymin2 = y2; 1629 ymax2 = y3; 1630 } 1631 else 1632 { 1633 ymin2 = y3; 1634 ymax2 = y2; 1635 } 1636 1637 if ( ymin2 < ymin1 || ymax2 > ymax1 ) 1638 { 1639 /* this arc has no given direction, split it! */ 1640 Split_Cubic( ras.arc ); 1641 ras.arc += 3; 1642 } 1643 else if ( y1 == y4 ) 1644 { 1645 /* this arc is flat, ignore it and pop it from the Bezier stack */ 1646 ras.arc -= 3; 1647 } 1648 else 1649 { 1650 state_bez = ( y1 <= y4 ) ? Ascending_State : Descending_State; 1651 1652 /* detect a change of direction */ 1653 if ( ras.state != state_bez ) 1654 { 1655 Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 ) 1656 : IS_TOP_OVERSHOOT( y1 ); 1657 1658 1659 /* finalize current profile if any */ 1660 if ( ras.state != Unknown_State && 1661 End_Profile( RAS_VARS o ) ) 1662 goto Fail; 1663 1664 if ( New_Profile( RAS_VARS state_bez, o ) ) 1665 goto Fail; 1666 } 1667 1668 /* compute intersections */ 1669 if ( state_bez == Ascending_State ) 1670 { 1671 if ( Bezier_Up( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) ) 1672 goto Fail; 1673 } 1674 else 1675 if ( Bezier_Down( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) ) 1676 goto Fail; 1677 } 1678 1679 } while ( ras.arc >= ras.arcs ); 1680 1681 ras.lastX = x4; 1682 ras.lastY = y4; 1683 1684 return SUCCESS; 1685 1686 Fail: 1687 return FAILURE; 1688 } 1689 1690 1691 #undef SWAP_ 1692 #define SWAP_( x, y ) do \ 1693 { \ 1694 Long swap = x; \ 1695 \ 1696 \ 1697 x = y; \ 1698 y = swap; \ 1699 } while ( 0 ) 1700 1701 1702 /*************************************************************************/ 1703 /* */ 1704 /* <Function> */ 1705 /* Decompose_Curve */ 1706 /* */ 1707 /* <Description> */ 1708 /* Scan the outline arrays in order to emit individual segments and */ 1709 /* Beziers by calling Line_To() and Bezier_To(). It handles all */ 1710 /* weird cases, like when the first point is off the curve, or when */ 1711 /* there are simply no `on' points in the contour! */ 1712 /* */ 1713 /* <Input> */ 1714 /* first :: The index of the first point in the contour. */ 1715 /* */ 1716 /* last :: The index of the last point in the contour. */ 1717 /* */ 1718 /* flipped :: If set, flip the direction of the curve. */ 1719 /* */ 1720 /* <Return> */ 1721 /* SUCCESS on success, FAILURE on error. */ 1722 /* */ 1723 static Bool 1724 Decompose_Curve( RAS_ARGS UShort first, 1725 UShort last, 1726 Int flipped ) 1727 { 1728 FT_Vector v_last; 1729 FT_Vector v_control; 1730 FT_Vector v_start; 1731 1732 FT_Vector* points; 1733 FT_Vector* point; 1734 FT_Vector* limit; 1735 char* tags; 1736 1737 UInt tag; /* current point's state */ 1738 1739 1740 points = ras.outline.points; 1741 limit = points + last; 1742 1743 v_start.x = SCALED( points[first].x ); 1744 v_start.y = SCALED( points[first].y ); 1745 v_last.x = SCALED( points[last].x ); 1746 v_last.y = SCALED( points[last].y ); 1747 1748 if ( flipped ) 1749 { 1750 SWAP_( v_start.x, v_start.y ); 1751 SWAP_( v_last.x, v_last.y ); 1752 } 1753 1754 v_control = v_start; 1755 1756 point = points + first; 1757 tags = ras.outline.tags + first; 1758 1759 /* set scan mode if necessary */ 1760 if ( tags[0] & FT_CURVE_TAG_HAS_SCANMODE ) 1761 ras.dropOutControl = (Byte)tags[0] >> 5; 1762 1763 tag = FT_CURVE_TAG( tags[0] ); 1764 1765 /* A contour cannot start with a cubic control point! */ 1766 if ( tag == FT_CURVE_TAG_CUBIC ) 1767 goto Invalid_Outline; 1768 1769 /* check first point to determine origin */ 1770 if ( tag == FT_CURVE_TAG_CONIC ) 1771 { 1772 /* first point is conic control. Yes, this happens. */ 1773 if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_CURVE_TAG_ON ) 1774 { 1775 /* start at last point if it is on the curve */ 1776 v_start = v_last; 1777 limit--; 1778 } 1779 else 1780 { 1781 /* if both first and last points are conic, */ 1782 /* start at their middle and record its position */ 1783 /* for closure */ 1784 v_start.x = ( v_start.x + v_last.x ) / 2; 1785 v_start.y = ( v_start.y + v_last.y ) / 2; 1786 1787 /* v_last = v_start; */ 1788 } 1789 point--; 1790 tags--; 1791 } 1792 1793 ras.lastX = v_start.x; 1794 ras.lastY = v_start.y; 1795 1796 while ( point < limit ) 1797 { 1798 point++; 1799 tags++; 1800 1801 tag = FT_CURVE_TAG( tags[0] ); 1802 1803 switch ( tag ) 1804 { 1805 case FT_CURVE_TAG_ON: /* emit a single line_to */ 1806 { 1807 Long x, y; 1808 1809 1810 x = SCALED( point->x ); 1811 y = SCALED( point->y ); 1812 if ( flipped ) 1813 SWAP_( x, y ); 1814 1815 if ( Line_To( RAS_VARS x, y ) ) 1816 goto Fail; 1817 continue; 1818 } 1819 1820 case FT_CURVE_TAG_CONIC: /* consume conic arcs */ 1821 v_control.x = SCALED( point[0].x ); 1822 v_control.y = SCALED( point[0].y ); 1823 1824 if ( flipped ) 1825 SWAP_( v_control.x, v_control.y ); 1826 1827 Do_Conic: 1828 if ( point < limit ) 1829 { 1830 FT_Vector v_middle; 1831 Long x, y; 1832 1833 1834 point++; 1835 tags++; 1836 tag = FT_CURVE_TAG( tags[0] ); 1837 1838 x = SCALED( point[0].x ); 1839 y = SCALED( point[0].y ); 1840 1841 if ( flipped ) 1842 SWAP_( x, y ); 1843 1844 if ( tag == FT_CURVE_TAG_ON ) 1845 { 1846 if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) ) 1847 goto Fail; 1848 continue; 1849 } 1850 1851 if ( tag != FT_CURVE_TAG_CONIC ) 1852 goto Invalid_Outline; 1853 1854 v_middle.x = ( v_control.x + x ) / 2; 1855 v_middle.y = ( v_control.y + y ) / 2; 1856 1857 if ( Conic_To( RAS_VARS v_control.x, v_control.y, 1858 v_middle.x, v_middle.y ) ) 1859 goto Fail; 1860 1861 v_control.x = x; 1862 v_control.y = y; 1863 1864 goto Do_Conic; 1865 } 1866 1867 if ( Conic_To( RAS_VARS v_control.x, v_control.y, 1868 v_start.x, v_start.y ) ) 1869 goto Fail; 1870 1871 goto Close; 1872 1873 default: /* FT_CURVE_TAG_CUBIC */ 1874 { 1875 Long x1, y1, x2, y2, x3, y3; 1876 1877 1878 if ( point + 1 > limit || 1879 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) 1880 goto Invalid_Outline; 1881 1882 point += 2; 1883 tags += 2; 1884 1885 x1 = SCALED( point[-2].x ); 1886 y1 = SCALED( point[-2].y ); 1887 x2 = SCALED( point[-1].x ); 1888 y2 = SCALED( point[-1].y ); 1889 1890 if ( flipped ) 1891 { 1892 SWAP_( x1, y1 ); 1893 SWAP_( x2, y2 ); 1894 } 1895 1896 if ( point <= limit ) 1897 { 1898 x3 = SCALED( point[0].x ); 1899 y3 = SCALED( point[0].y ); 1900 1901 if ( flipped ) 1902 SWAP_( x3, y3 ); 1903 1904 if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) ) 1905 goto Fail; 1906 continue; 1907 } 1908 1909 if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) ) 1910 goto Fail; 1911 goto Close; 1912 } 1913 } 1914 } 1915 1916 /* close the contour with a line segment */ 1917 if ( Line_To( RAS_VARS v_start.x, v_start.y ) ) 1918 goto Fail; 1919 1920 Close: 1921 return SUCCESS; 1922 1923 Invalid_Outline: 1924 ras.error = FT_THROW( Invalid ); 1925 1926 Fail: 1927 return FAILURE; 1928 } 1929 1930 1931 /*************************************************************************/ 1932 /* */ 1933 /* <Function> */ 1934 /* Convert_Glyph */ 1935 /* */ 1936 /* <Description> */ 1937 /* Convert a glyph into a series of segments and arcs and make a */ 1938 /* profiles list with them. */ 1939 /* */ 1940 /* <Input> */ 1941 /* flipped :: If set, flip the direction of curve. */ 1942 /* */ 1943 /* <Return> */ 1944 /* SUCCESS on success, FAILURE if any error was encountered during */ 1945 /* rendering. */ 1946 /* */ 1947 static Bool 1948 Convert_Glyph( RAS_ARGS Int flipped ) 1949 { 1950 Int i; 1951 UInt start; 1952 1953 1954 ras.fProfile = NULL; 1955 ras.joint = FALSE; 1956 ras.fresh = FALSE; 1957 1958 ras.maxBuff = ras.sizeBuff - AlignProfileSize; 1959 1960 ras.numTurns = 0; 1961 1962 ras.cProfile = (PProfile)ras.top; 1963 ras.cProfile->offset = ras.top; 1964 ras.num_Profs = 0; 1965 1966 start = 0; 1967 1968 for ( i = 0; i < ras.outline.n_contours; i++ ) 1969 { 1970 PProfile lastProfile; 1971 Bool o; 1972 1973 1974 ras.state = Unknown_State; 1975 ras.gProfile = NULL; 1976 1977 if ( Decompose_Curve( RAS_VARS (UShort)start, 1978 (UShort)ras.outline.contours[i], 1979 flipped ) ) 1980 return FAILURE; 1981 1982 start = (UShort)ras.outline.contours[i] + 1; 1983 1984 /* we must now check whether the extreme arcs join or not */ 1985 if ( FRAC( ras.lastY ) == 0 && 1986 ras.lastY >= ras.minY && 1987 ras.lastY <= ras.maxY ) 1988 if ( ras.gProfile && 1989 ( ras.gProfile->flags & Flow_Up ) == 1990 ( ras.cProfile->flags & Flow_Up ) ) 1991 ras.top--; 1992 /* Note that ras.gProfile can be nil if the contour was too small */ 1993 /* to be drawn. */ 1994 1995 lastProfile = ras.cProfile; 1996 if ( ras.top != ras.cProfile->offset && 1997 ( ras.cProfile->flags & Flow_Up ) ) 1998 o = IS_TOP_OVERSHOOT( ras.lastY ); 1999 else 2000 o = IS_BOTTOM_OVERSHOOT( ras.lastY ); 2001 if ( End_Profile( RAS_VARS o ) ) 2002 return FAILURE; 2003 2004 /* close the `next profile in contour' linked list */ 2005 if ( ras.gProfile ) 2006 lastProfile->next = ras.gProfile; 2007 } 2008 2009 if ( Finalize_Profile_Table( RAS_VAR ) ) 2010 return FAILURE; 2011 2012 return (Bool)( ras.top < ras.maxBuff ? SUCCESS : FAILURE ); 2013 } 2014 2015 2016 /*************************************************************************/ 2017 /*************************************************************************/ 2018 /** **/ 2019 /** SCAN-LINE SWEEPS AND DRAWING **/ 2020 /** **/ 2021 /*************************************************************************/ 2022 /*************************************************************************/ 2023 2024 2025 /*************************************************************************/ 2026 /* */ 2027 /* Init_Linked */ 2028 /* */ 2029 /* Initializes an empty linked list. */ 2030 /* */ 2031 static void 2032 Init_Linked( TProfileList* l ) 2033 { 2034 *l = NULL; 2035 } 2036 2037 2038 /*************************************************************************/ 2039 /* */ 2040 /* InsNew */ 2041 /* */ 2042 /* Inserts a new profile in a linked list. */ 2043 /* */ 2044 static void 2045 InsNew( PProfileList list, 2046 PProfile profile ) 2047 { 2048 PProfile *old, current; 2049 Long x; 2050 2051 2052 old = list; 2053 current = *old; 2054 x = profile->X; 2055 2056 while ( current ) 2057 { 2058 if ( x < current->X ) 2059 break; 2060 old = ¤t->link; 2061 current = *old; 2062 } 2063 2064 profile->link = current; 2065 *old = profile; 2066 } 2067 2068 2069 /*************************************************************************/ 2070 /* */ 2071 /* DelOld */ 2072 /* */ 2073 /* Removes an old profile from a linked list. */ 2074 /* */ 2075 static void 2076 DelOld( PProfileList list, 2077 PProfile profile ) 2078 { 2079 PProfile *old, current; 2080 2081 2082 old = list; 2083 current = *old; 2084 2085 while ( current ) 2086 { 2087 if ( current == profile ) 2088 { 2089 *old = current->link; 2090 return; 2091 } 2092 2093 old = ¤t->link; 2094 current = *old; 2095 } 2096 2097 /* we should never get there, unless the profile was not part of */ 2098 /* the list. */ 2099 } 2100 2101 2102 /*************************************************************************/ 2103 /* */ 2104 /* Sort */ 2105 /* */ 2106 /* Sorts a trace list. In 95%, the list is already sorted. We need */ 2107 /* an algorithm which is fast in this case. Bubble sort is enough */ 2108 /* and simple. */ 2109 /* */ 2110 static void 2111 Sort( PProfileList list ) 2112 { 2113 PProfile *old, current, next; 2114 2115 2116 /* First, set the new X coordinate of each profile */ 2117 current = *list; 2118 while ( current ) 2119 { 2120 current->X = *current->offset; 2121 current->offset += ( current->flags & Flow_Up ) ? 1 : -1; 2122 current->height--; 2123 current = current->link; 2124 } 2125 2126 /* Then sort them */ 2127 old = list; 2128 current = *old; 2129 2130 if ( !current ) 2131 return; 2132 2133 next = current->link; 2134 2135 while ( next ) 2136 { 2137 if ( current->X <= next->X ) 2138 { 2139 old = ¤t->link; 2140 current = *old; 2141 2142 if ( !current ) 2143 return; 2144 } 2145 else 2146 { 2147 *old = next; 2148 current->link = next->link; 2149 next->link = current; 2150 2151 old = list; 2152 current = *old; 2153 } 2154 2155 next = current->link; 2156 } 2157 } 2158 2159 2160 /*************************************************************************/ 2161 /* */ 2162 /* Vertical Sweep Procedure Set */ 2163 /* */ 2164 /* These four routines are used during the vertical black/white sweep */ 2165 /* phase by the generic Draw_Sweep() function. */ 2166 /* */ 2167 /*************************************************************************/ 2168 2169 static void 2170 Vertical_Sweep_Init( RAS_ARGS Short* min, 2171 Short* max ) 2172 { 2173 Long pitch = ras.target.pitch; 2174 2175 FT_UNUSED( max ); 2176 2177 2178 ras.traceIncr = (Short)-pitch; 2179 ras.traceOfs = -*min * pitch; 2180 if ( pitch > 0 ) 2181 ras.traceOfs += (Long)( ras.target.rows - 1 ) * pitch; 2182 } 2183 2184 2185 static void 2186 Vertical_Sweep_Span( RAS_ARGS Short y, 2187 FT_F26Dot6 x1, 2188 FT_F26Dot6 x2, 2189 PProfile left, 2190 PProfile right ) 2191 { 2192 Long e1, e2; 2193 Byte* target; 2194 2195 Int dropOutControl = left->flags & 7; 2196 2197 FT_UNUSED( y ); 2198 FT_UNUSED( left ); 2199 FT_UNUSED( right ); 2200 2201 2202 /* in high-precision mode, we need 12 digits after the comma to */ 2203 /* represent multiples of 1/(1<<12) = 1/4096 */ 2204 FT_TRACE7(( " y=%d x=[%.12f;%.12f], drop-out=%d", 2205 y, 2206 x1 / (double)ras.precision, 2207 x2 / (double)ras.precision, 2208 dropOutControl )); 2209 2210 /* Drop-out control */ 2211 2212 e1 = TRUNC( CEILING( x1 ) ); 2213 2214 if ( dropOutControl != 2 && 2215 x2 - x1 - ras.precision <= ras.precision_jitter ) 2216 e2 = e1; 2217 else 2218 e2 = TRUNC( FLOOR( x2 ) ); 2219 2220 if ( e2 >= 0 && e1 < ras.bWidth ) 2221 { 2222 Int c1, c2; 2223 Byte f1, f2; 2224 2225 2226 if ( e1 < 0 ) 2227 e1 = 0; 2228 if ( e2 >= ras.bWidth ) 2229 e2 = ras.bWidth - 1; 2230 2231 FT_TRACE7(( " -> x=[%d;%d]", e1, e2 )); 2232 2233 c1 = (Short)( e1 >> 3 ); 2234 c2 = (Short)( e2 >> 3 ); 2235 2236 f1 = (Byte) ( 0xFF >> ( e1 & 7 ) ); 2237 f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) ); 2238 2239 target = ras.bTarget + ras.traceOfs + c1; 2240 c2 -= c1; 2241 2242 if ( c2 > 0 ) 2243 { 2244 target[0] |= f1; 2245 2246 /* memset() is slower than the following code on many platforms. */ 2247 /* This is due to the fact that, in the vast majority of cases, */ 2248 /* the span length in bytes is relatively small. */ 2249 c2--; 2250 while ( c2 > 0 ) 2251 { 2252 *(++target) = 0xFF; 2253 c2--; 2254 } 2255 target[1] |= f2; 2256 } 2257 else 2258 *target |= ( f1 & f2 ); 2259 } 2260 2261 FT_TRACE7(( "\n" )); 2262 } 2263 2264 2265 static void 2266 Vertical_Sweep_Drop( RAS_ARGS Short y, 2267 FT_F26Dot6 x1, 2268 FT_F26Dot6 x2, 2269 PProfile left, 2270 PProfile right ) 2271 { 2272 Long e1, e2, pxl; 2273 Short c1, f1; 2274 2275 2276 FT_TRACE7(( " y=%d x=[%.12f;%.12f]", 2277 y, 2278 x1 / (double)ras.precision, 2279 x2 / (double)ras.precision )); 2280 2281 /* Drop-out control */ 2282 2283 /* e2 x2 x1 e1 */ 2284 /* */ 2285 /* ^ | */ 2286 /* | | */ 2287 /* +-------------+---------------------+------------+ */ 2288 /* | | */ 2289 /* | v */ 2290 /* */ 2291 /* pixel contour contour pixel */ 2292 /* center center */ 2293 2294 /* drop-out mode scan conversion rules (as defined in OpenType) */ 2295 /* --------------------------------------------------------------- */ 2296 /* 0 1, 2, 3 */ 2297 /* 1 1, 2, 4 */ 2298 /* 2 1, 2 */ 2299 /* 3 same as mode 2 */ 2300 /* 4 1, 2, 5 */ 2301 /* 5 1, 2, 6 */ 2302 /* 6, 7 same as mode 2 */ 2303 2304 e1 = CEILING( x1 ); 2305 e2 = FLOOR ( x2 ); 2306 pxl = e1; 2307 2308 if ( e1 > e2 ) 2309 { 2310 Int dropOutControl = left->flags & 7; 2311 2312 2313 FT_TRACE7(( ", drop-out=%d", dropOutControl )); 2314 2315 if ( e1 == e2 + ras.precision ) 2316 { 2317 switch ( dropOutControl ) 2318 { 2319 case 0: /* simple drop-outs including stubs */ 2320 pxl = e2; 2321 break; 2322 2323 case 4: /* smart drop-outs including stubs */ 2324 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); 2325 break; 2326 2327 case 1: /* simple drop-outs excluding stubs */ 2328 case 5: /* smart drop-outs excluding stubs */ 2329 2330 /* Drop-out Control Rules #4 and #6 */ 2331 2332 /* The specification neither provides an exact definition */ 2333 /* of a `stub' nor gives exact rules to exclude them. */ 2334 /* */ 2335 /* Here the constraints we use to recognize a stub. */ 2336 /* */ 2337 /* upper stub: */ 2338 /* */ 2339 /* - P_Left and P_Right are in the same contour */ 2340 /* - P_Right is the successor of P_Left in that contour */ 2341 /* - y is the top of P_Left and P_Right */ 2342 /* */ 2343 /* lower stub: */ 2344 /* */ 2345 /* - P_Left and P_Right are in the same contour */ 2346 /* - P_Left is the successor of P_Right in that contour */ 2347 /* - y is the bottom of P_Left */ 2348 /* */ 2349 /* We draw a stub if the following constraints are met. */ 2350 /* */ 2351 /* - for an upper or lower stub, there is top or bottom */ 2352 /* overshoot, respectively */ 2353 /* - the covered interval is greater or equal to a half */ 2354 /* pixel */ 2355 2356 /* upper stub test */ 2357 if ( left->next == right && 2358 left->height <= 0 && 2359 !( left->flags & Overshoot_Top && 2360 x2 - x1 >= ras.precision_half ) ) 2361 goto Exit; 2362 2363 /* lower stub test */ 2364 if ( right->next == left && 2365 left->start == y && 2366 !( left->flags & Overshoot_Bottom && 2367 x2 - x1 >= ras.precision_half ) ) 2368 goto Exit; 2369 2370 if ( dropOutControl == 1 ) 2371 pxl = e2; 2372 else 2373 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); 2374 break; 2375 2376 default: /* modes 2, 3, 6, 7 */ 2377 goto Exit; /* no drop-out control */ 2378 } 2379 2380 /* undocumented but confirmed: If the drop-out would result in a */ 2381 /* pixel outside of the bounding box, use the pixel inside of the */ 2382 /* bounding box instead */ 2383 if ( pxl < 0 ) 2384 pxl = e1; 2385 else if ( TRUNC( pxl ) >= ras.bWidth ) 2386 pxl = e2; 2387 2388 /* check that the other pixel isn't set */ 2389 e1 = pxl == e1 ? e2 : e1; 2390 2391 e1 = TRUNC( e1 ); 2392 2393 c1 = (Short)( e1 >> 3 ); 2394 f1 = (Short)( e1 & 7 ); 2395 2396 if ( e1 >= 0 && e1 < ras.bWidth && 2397 ras.bTarget[ras.traceOfs + c1] & ( 0x80 >> f1 ) ) 2398 goto Exit; 2399 } 2400 else 2401 goto Exit; 2402 } 2403 2404 e1 = TRUNC( pxl ); 2405 2406 if ( e1 >= 0 && e1 < ras.bWidth ) 2407 { 2408 FT_TRACE7(( " -> x=%d (drop-out)", e1 )); 2409 2410 c1 = (Short)( e1 >> 3 ); 2411 f1 = (Short)( e1 & 7 ); 2412 2413 ras.bTarget[ras.traceOfs + c1] |= (char)( 0x80 >> f1 ); 2414 } 2415 2416 Exit: 2417 FT_TRACE7(( "\n" )); 2418 } 2419 2420 2421 static void 2422 Vertical_Sweep_Step( RAS_ARG ) 2423 { 2424 ras.traceOfs += ras.traceIncr; 2425 } 2426 2427 2428 /***********************************************************************/ 2429 /* */ 2430 /* Horizontal Sweep Procedure Set */ 2431 /* */ 2432 /* These four routines are used during the horizontal black/white */ 2433 /* sweep phase by the generic Draw_Sweep() function. */ 2434 /* */ 2435 /***********************************************************************/ 2436 2437 static void 2438 Horizontal_Sweep_Init( RAS_ARGS Short* min, 2439 Short* max ) 2440 { 2441 /* nothing, really */ 2442 FT_UNUSED_RASTER; 2443 FT_UNUSED( min ); 2444 FT_UNUSED( max ); 2445 } 2446 2447 2448 static void 2449 Horizontal_Sweep_Span( RAS_ARGS Short y, 2450 FT_F26Dot6 x1, 2451 FT_F26Dot6 x2, 2452 PProfile left, 2453 PProfile right ) 2454 { 2455 FT_UNUSED( left ); 2456 FT_UNUSED( right ); 2457 2458 2459 if ( x2 - x1 < ras.precision ) 2460 { 2461 Long e1, e2; 2462 2463 2464 FT_TRACE7(( " x=%d y=[%.12f;%.12f]", 2465 y, 2466 x1 / (double)ras.precision, 2467 x2 / (double)ras.precision )); 2468 2469 e1 = CEILING( x1 ); 2470 e2 = FLOOR ( x2 ); 2471 2472 if ( e1 == e2 ) 2473 { 2474 e1 = TRUNC( e1 ); 2475 2476 if ( e1 >= 0 && (ULong)e1 < ras.target.rows ) 2477 { 2478 Byte f1; 2479 PByte bits; 2480 PByte p; 2481 2482 2483 FT_TRACE7(( " -> y=%d (drop-out)", e1 )); 2484 2485 bits = ras.bTarget + ( y >> 3 ); 2486 f1 = (Byte)( 0x80 >> ( y & 7 ) ); 2487 p = bits - e1 * ras.target.pitch; 2488 2489 if ( ras.target.pitch > 0 ) 2490 p += (Long)( ras.target.rows - 1 ) * ras.target.pitch; 2491 2492 p[0] |= f1; 2493 } 2494 } 2495 2496 FT_TRACE7(( "\n" )); 2497 } 2498 } 2499 2500 2501 static void 2502 Horizontal_Sweep_Drop( RAS_ARGS Short y, 2503 FT_F26Dot6 x1, 2504 FT_F26Dot6 x2, 2505 PProfile left, 2506 PProfile right ) 2507 { 2508 Long e1, e2, pxl; 2509 PByte bits; 2510 Byte f1; 2511 2512 2513 FT_TRACE7(( " x=%d y=[%.12f;%.12f]", 2514 y, 2515 x1 / (double)ras.precision, 2516 x2 / (double)ras.precision )); 2517 2518 /* During the horizontal sweep, we only take care of drop-outs */ 2519 2520 /* e1 + <-- pixel center */ 2521 /* | */ 2522 /* x1 ---+--> <-- contour */ 2523 /* | */ 2524 /* | */ 2525 /* x2 <--+--- <-- contour */ 2526 /* | */ 2527 /* | */ 2528 /* e2 + <-- pixel center */ 2529 2530 e1 = CEILING( x1 ); 2531 e2 = FLOOR ( x2 ); 2532 pxl = e1; 2533 2534 if ( e1 > e2 ) 2535 { 2536 Int dropOutControl = left->flags & 7; 2537 2538 2539 FT_TRACE7(( ", dropout=%d", dropOutControl )); 2540 2541 if ( e1 == e2 + ras.precision ) 2542 { 2543 switch ( dropOutControl ) 2544 { 2545 case 0: /* simple drop-outs including stubs */ 2546 pxl = e2; 2547 break; 2548 2549 case 4: /* smart drop-outs including stubs */ 2550 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); 2551 break; 2552 2553 case 1: /* simple drop-outs excluding stubs */ 2554 case 5: /* smart drop-outs excluding stubs */ 2555 /* see Vertical_Sweep_Drop for details */ 2556 2557 /* rightmost stub test */ 2558 if ( left->next == right && 2559 left->height <= 0 && 2560 !( left->flags & Overshoot_Top && 2561 x2 - x1 >= ras.precision_half ) ) 2562 goto Exit; 2563 2564 /* leftmost stub test */ 2565 if ( right->next == left && 2566 left->start == y && 2567 !( left->flags & Overshoot_Bottom && 2568 x2 - x1 >= ras.precision_half ) ) 2569 goto Exit; 2570 2571 if ( dropOutControl == 1 ) 2572 pxl = e2; 2573 else 2574 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); 2575 break; 2576 2577 default: /* modes 2, 3, 6, 7 */ 2578 goto Exit; /* no drop-out control */ 2579 } 2580 2581 /* undocumented but confirmed: If the drop-out would result in a */ 2582 /* pixel outside of the bounding box, use the pixel inside of the */ 2583 /* bounding box instead */ 2584 if ( pxl < 0 ) 2585 pxl = e1; 2586 else if ( (ULong)( TRUNC( pxl ) ) >= ras.target.rows ) 2587 pxl = e2; 2588 2589 /* check that the other pixel isn't set */ 2590 e1 = pxl == e1 ? e2 : e1; 2591 2592 e1 = TRUNC( e1 ); 2593 2594 bits = ras.bTarget + ( y >> 3 ); 2595 f1 = (Byte)( 0x80 >> ( y & 7 ) ); 2596 2597 bits -= e1 * ras.target.pitch; 2598 if ( ras.target.pitch > 0 ) 2599 bits += (Long)( ras.target.rows - 1 ) * ras.target.pitch; 2600 2601 if ( e1 >= 0 && 2602 (ULong)e1 < ras.target.rows && 2603 *bits & f1 ) 2604 goto Exit; 2605 } 2606 else 2607 goto Exit; 2608 } 2609 2610 e1 = TRUNC( pxl ); 2611 2612 if ( e1 >= 0 && (ULong)e1 < ras.target.rows ) 2613 { 2614 FT_TRACE7(( " -> y=%d (drop-out)", e1 )); 2615 2616 bits = ras.bTarget + ( y >> 3 ); 2617 f1 = (Byte)( 0x80 >> ( y & 7 ) ); 2618 bits -= e1 * ras.target.pitch; 2619 2620 if ( ras.target.pitch > 0 ) 2621 bits += (Long)( ras.target.rows - 1 ) * ras.target.pitch; 2622 2623 bits[0] |= f1; 2624 } 2625 2626 Exit: 2627 FT_TRACE7(( "\n" )); 2628 } 2629 2630 2631 static void 2632 Horizontal_Sweep_Step( RAS_ARG ) 2633 { 2634 /* Nothing, really */ 2635 FT_UNUSED_RASTER; 2636 } 2637 2638 2639 /*************************************************************************/ 2640 /* */ 2641 /* Generic Sweep Drawing routine */ 2642 /* */ 2643 /*************************************************************************/ 2644 2645 static Bool 2646 Draw_Sweep( RAS_ARG ) 2647 { 2648 Short y, y_change, y_height; 2649 2650 PProfile P, Q, P_Left, P_Right; 2651 2652 Short min_Y, max_Y, top, bottom, dropouts; 2653 2654 Long x1, x2, xs, e1, e2; 2655 2656 TProfileList waiting; 2657 TProfileList draw_left, draw_right; 2658 2659 2660 /* initialize empty linked lists */ 2661 2662 Init_Linked( &waiting ); 2663 2664 Init_Linked( &draw_left ); 2665 Init_Linked( &draw_right ); 2666 2667 /* first, compute min and max Y */ 2668 2669 P = ras.fProfile; 2670 max_Y = (Short)TRUNC( ras.minY ); 2671 min_Y = (Short)TRUNC( ras.maxY ); 2672 2673 while ( P ) 2674 { 2675 Q = P->link; 2676 2677 bottom = (Short)P->start; 2678 top = (Short)( P->start + P->height - 1 ); 2679 2680 if ( min_Y > bottom ) 2681 min_Y = bottom; 2682 if ( max_Y < top ) 2683 max_Y = top; 2684 2685 P->X = 0; 2686 InsNew( &waiting, P ); 2687 2688 P = Q; 2689 } 2690 2691 /* check the Y-turns */ 2692 if ( ras.numTurns == 0 ) 2693 { 2694 ras.error = FT_THROW( Invalid ); 2695 return FAILURE; 2696 } 2697 2698 /* now initialize the sweep */ 2699 2700 ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y ); 2701 2702 /* then compute the distance of each profile from min_Y */ 2703 2704 P = waiting; 2705 2706 while ( P ) 2707 { 2708 P->countL = P->start - min_Y; 2709 P = P->link; 2710 } 2711 2712 /* let's go */ 2713 2714 y = min_Y; 2715 y_height = 0; 2716 2717 if ( ras.numTurns > 0 && 2718 ras.sizeBuff[-ras.numTurns] == min_Y ) 2719 ras.numTurns--; 2720 2721 while ( ras.numTurns > 0 ) 2722 { 2723 /* check waiting list for new activations */ 2724 2725 P = waiting; 2726 2727 while ( P ) 2728 { 2729 Q = P->link; 2730 P->countL -= y_height; 2731 if ( P->countL == 0 ) 2732 { 2733 DelOld( &waiting, P ); 2734 2735 if ( P->flags & Flow_Up ) 2736 InsNew( &draw_left, P ); 2737 else 2738 InsNew( &draw_right, P ); 2739 } 2740 2741 P = Q; 2742 } 2743 2744 /* sort the drawing lists */ 2745 2746 Sort( &draw_left ); 2747 Sort( &draw_right ); 2748 2749 y_change = (Short)ras.sizeBuff[-ras.numTurns--]; 2750 y_height = (Short)( y_change - y ); 2751 2752 while ( y < y_change ) 2753 { 2754 /* let's trace */ 2755 2756 dropouts = 0; 2757 2758 P_Left = draw_left; 2759 P_Right = draw_right; 2760 2761 while ( P_Left ) 2762 { 2763 x1 = P_Left ->X; 2764 x2 = P_Right->X; 2765 2766 if ( x1 > x2 ) 2767 { 2768 xs = x1; 2769 x1 = x2; 2770 x2 = xs; 2771 } 2772 2773 e1 = FLOOR( x1 ); 2774 e2 = CEILING( x2 ); 2775 2776 if ( x2 - x1 <= ras.precision && 2777 e1 != x1 && e2 != x2 ) 2778 { 2779 if ( e1 > e2 || e2 == e1 + ras.precision ) 2780 { 2781 Int dropOutControl = P_Left->flags & 7; 2782 2783 2784 if ( dropOutControl != 2 ) 2785 { 2786 /* a drop-out was detected */ 2787 2788 P_Left ->X = x1; 2789 P_Right->X = x2; 2790 2791 /* mark profile for drop-out processing */ 2792 P_Left->countL = 1; 2793 dropouts++; 2794 } 2795 2796 goto Skip_To_Next; 2797 } 2798 } 2799 2800 ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right ); 2801 2802 Skip_To_Next: 2803 2804 P_Left = P_Left->link; 2805 P_Right = P_Right->link; 2806 } 2807 2808 /* handle drop-outs _after_ the span drawing -- */ 2809 /* drop-out processing has been moved out of the loop */ 2810 /* for performance tuning */ 2811 if ( dropouts > 0 ) 2812 goto Scan_DropOuts; 2813 2814 Next_Line: 2815 2816 ras.Proc_Sweep_Step( RAS_VAR ); 2817 2818 y++; 2819 2820 if ( y < y_change ) 2821 { 2822 Sort( &draw_left ); 2823 Sort( &draw_right ); 2824 } 2825 } 2826 2827 /* now finalize the profiles that need it */ 2828 2829 P = draw_left; 2830 while ( P ) 2831 { 2832 Q = P->link; 2833 if ( P->height == 0 ) 2834 DelOld( &draw_left, P ); 2835 P = Q; 2836 } 2837 2838 P = draw_right; 2839 while ( P ) 2840 { 2841 Q = P->link; 2842 if ( P->height == 0 ) 2843 DelOld( &draw_right, P ); 2844 P = Q; 2845 } 2846 } 2847 2848 /* for gray-scaling, flush the bitmap scanline cache */ 2849 while ( y <= max_Y ) 2850 { 2851 ras.Proc_Sweep_Step( RAS_VAR ); 2852 y++; 2853 } 2854 2855 return SUCCESS; 2856 2857 Scan_DropOuts: 2858 2859 P_Left = draw_left; 2860 P_Right = draw_right; 2861 2862 while ( P_Left ) 2863 { 2864 if ( P_Left->countL ) 2865 { 2866 P_Left->countL = 0; 2867 #if 0 2868 dropouts--; /* -- this is useful when debugging only */ 2869 #endif 2870 ras.Proc_Sweep_Drop( RAS_VARS y, 2871 P_Left->X, 2872 P_Right->X, 2873 P_Left, 2874 P_Right ); 2875 } 2876 2877 P_Left = P_Left->link; 2878 P_Right = P_Right->link; 2879 } 2880 2881 goto Next_Line; 2882 } 2883 2884 2885 /*************************************************************************/ 2886 /* */ 2887 /* <Function> */ 2888 /* Render_Single_Pass */ 2889 /* */ 2890 /* <Description> */ 2891 /* Perform one sweep with sub-banding. */ 2892 /* */ 2893 /* <Input> */ 2894 /* flipped :: If set, flip the direction of the outline. */ 2895 /* */ 2896 /* <Return> */ 2897 /* Renderer error code. */ 2898 /* */ 2899 static int 2900 Render_Single_Pass( RAS_ARGS Bool flipped ) 2901 { 2902 Short i, j, k; 2903 2904 2905 while ( ras.band_top >= 0 ) 2906 { 2907 ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision; 2908 ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision; 2909 2910 ras.top = ras.buff; 2911 2912 ras.error = Raster_Err_None; 2913 2914 if ( Convert_Glyph( RAS_VARS flipped ) ) 2915 { 2916 if ( ras.error != Raster_Err_Overflow ) 2917 return FAILURE; 2918 2919 ras.error = Raster_Err_None; 2920 2921 /* sub-banding */ 2922 2923 #ifdef DEBUG_RASTER 2924 ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) ); 2925 #endif 2926 2927 i = ras.band_stack[ras.band_top].y_min; 2928 j = ras.band_stack[ras.band_top].y_max; 2929 2930 k = (Short)( ( i + j ) / 2 ); 2931 2932 if ( ras.band_top >= 7 || k < i ) 2933 { 2934 ras.band_top = 0; 2935 ras.error = FT_THROW( Invalid ); 2936 2937 return ras.error; 2938 } 2939 2940 ras.band_stack[ras.band_top + 1].y_min = k; 2941 ras.band_stack[ras.band_top + 1].y_max = j; 2942 2943 ras.band_stack[ras.band_top].y_max = (Short)( k - 1 ); 2944 2945 ras.band_top++; 2946 } 2947 else 2948 { 2949 if ( ras.fProfile ) 2950 if ( Draw_Sweep( RAS_VAR ) ) 2951 return ras.error; 2952 ras.band_top--; 2953 } 2954 } 2955 2956 return SUCCESS; 2957 } 2958 2959 2960 /*************************************************************************/ 2961 /* */ 2962 /* <Function> */ 2963 /* Render_Glyph */ 2964 /* */ 2965 /* <Description> */ 2966 /* Render a glyph in a bitmap. Sub-banding if needed. */ 2967 /* */ 2968 /* <Return> */ 2969 /* FreeType error code. 0 means success. */ 2970 /* */ 2971 static FT_Error 2972 Render_Glyph( RAS_ARG ) 2973 { 2974 FT_Error error; 2975 2976 2977 Set_High_Precision( RAS_VARS ras.outline.flags & 2978 FT_OUTLINE_HIGH_PRECISION ); 2979 ras.scale_shift = ras.precision_shift; 2980 2981 if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS ) 2982 ras.dropOutControl = 2; 2983 else 2984 { 2985 if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS ) 2986 ras.dropOutControl = 4; 2987 else 2988 ras.dropOutControl = 0; 2989 2990 if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) ) 2991 ras.dropOutControl += 1; 2992 } 2993 2994 ras.second_pass = (Bool)( !( ras.outline.flags & 2995 FT_OUTLINE_SINGLE_PASS ) ); 2996 2997 /* Vertical Sweep */ 2998 FT_TRACE7(( "Vertical pass (ftraster)\n" )); 2999 3000 ras.Proc_Sweep_Init = Vertical_Sweep_Init; 3001 ras.Proc_Sweep_Span = Vertical_Sweep_Span; 3002 ras.Proc_Sweep_Drop = Vertical_Sweep_Drop; 3003 ras.Proc_Sweep_Step = Vertical_Sweep_Step; 3004 3005 ras.band_top = 0; 3006 ras.band_stack[0].y_min = 0; 3007 ras.band_stack[0].y_max = (Short)( ras.target.rows - 1 ); 3008 3009 ras.bWidth = (UShort)ras.target.width; 3010 ras.bTarget = (Byte*)ras.target.buffer; 3011 3012 if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 ) 3013 return error; 3014 3015 /* Horizontal Sweep */ 3016 if ( ras.second_pass && ras.dropOutControl != 2 ) 3017 { 3018 FT_TRACE7(( "Horizontal pass (ftraster)\n" )); 3019 3020 ras.Proc_Sweep_Init = Horizontal_Sweep_Init; 3021 ras.Proc_Sweep_Span = Horizontal_Sweep_Span; 3022 ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop; 3023 ras.Proc_Sweep_Step = Horizontal_Sweep_Step; 3024 3025 ras.band_top = 0; 3026 ras.band_stack[0].y_min = 0; 3027 ras.band_stack[0].y_max = (Short)( ras.target.width - 1 ); 3028 3029 if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 ) 3030 return error; 3031 } 3032 3033 return Raster_Err_None; 3034 } 3035 3036 3037 static void 3038 ft_black_init( black_PRaster raster ) 3039 { 3040 FT_UNUSED( raster ); 3041 } 3042 3043 3044 /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/ 3045 /**** a static object. *****/ 3046 3047 3048 #ifdef STANDALONE_ 3049 3050 3051 static int 3052 ft_black_new( void* memory, 3053 FT_Raster *araster ) 3054 { 3055 static black_TRaster the_raster; 3056 FT_UNUSED( memory ); 3057 3058 3059 *araster = (FT_Raster)&the_raster; 3060 FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) ); 3061 ft_black_init( &the_raster ); 3062 3063 return 0; 3064 } 3065 3066 3067 static void 3068 ft_black_done( FT_Raster raster ) 3069 { 3070 /* nothing */ 3071 FT_UNUSED( raster ); 3072 } 3073 3074 3075 #else /* !STANDALONE_ */ 3076 3077 3078 static int 3079 ft_black_new( FT_Memory memory, 3080 black_PRaster *araster ) 3081 { 3082 FT_Error error; 3083 black_PRaster raster = NULL; 3084 3085 3086 *araster = 0; 3087 if ( !FT_NEW( raster ) ) 3088 { 3089 raster->memory = memory; 3090 ft_black_init( raster ); 3091 3092 *araster = raster; 3093 } 3094 3095 return error; 3096 } 3097 3098 3099 static void 3100 ft_black_done( black_PRaster raster ) 3101 { 3102 FT_Memory memory = (FT_Memory)raster->memory; 3103 3104 3105 FT_FREE( raster ); 3106 } 3107 3108 3109 #endif /* !STANDALONE_ */ 3110 3111 3112 static void 3113 ft_black_reset( FT_Raster raster, 3114 PByte pool_base, 3115 ULong pool_size ) 3116 { 3117 FT_UNUSED( raster ); 3118 FT_UNUSED( pool_base ); 3119 FT_UNUSED( pool_size ); 3120 } 3121 3122 3123 static int 3124 ft_black_set_mode( FT_Raster raster, 3125 ULong mode, 3126 void* args ) 3127 { 3128 FT_UNUSED( raster ); 3129 FT_UNUSED( mode ); 3130 FT_UNUSED( args ); 3131 3132 return 0; 3133 } 3134 3135 3136 static int 3137 ft_black_render( FT_Raster raster, 3138 const FT_Raster_Params* params ) 3139 { 3140 const FT_Outline* outline = (const FT_Outline*)params->source; 3141 const FT_Bitmap* target_map = params->target; 3142 3143 black_TWorker worker[1]; 3144 3145 Long buffer[FT_MAX_BLACK_POOL]; 3146 3147 3148 if ( !raster ) 3149 return FT_THROW( Not_Ini ); 3150 3151 if ( !outline ) 3152 return FT_THROW( Invalid ); 3153 3154 /* return immediately if the outline is empty */ 3155 if ( outline->n_points == 0 || outline->n_contours <= 0 ) 3156 return Raster_Err_None; 3157 3158 if ( !outline->contours || !outline->points ) 3159 return FT_THROW( Invalid ); 3160 3161 if ( outline->n_points != 3162 outline->contours[outline->n_contours - 1] + 1 ) 3163 return FT_THROW( Invalid ); 3164 3165 /* this version of the raster does not support direct rendering, sorry */ 3166 if ( params->flags & FT_RASTER_FLAG_DIRECT ) 3167 return FT_THROW( Unsupported ); 3168 3169 if ( params->flags & FT_RASTER_FLAG_AA ) 3170 return FT_THROW( Unsupported ); 3171 3172 if ( !target_map ) 3173 return FT_THROW( Invalid ); 3174 3175 /* nothing to do */ 3176 if ( !target_map->width || !target_map->rows ) 3177 return Raster_Err_None; 3178 3179 if ( !target_map->buffer ) 3180 return FT_THROW( Invalid ); 3181 3182 /* reject too large outline coordinates */ 3183 { 3184 FT_Vector* vec = outline->points; 3185 FT_Vector* limit = vec + outline->n_points; 3186 3187 3188 for ( ; vec < limit; vec++ ) 3189 { 3190 if ( vec->x < -0x1000000L || vec->x > 0x1000000L || 3191 vec->y < -0x1000000L || vec->y > 0x1000000L ) 3192 return FT_THROW( Invalid ); 3193 } 3194 } 3195 3196 ras.outline = *outline; 3197 ras.target = *target_map; 3198 3199 worker->buff = buffer; 3200 worker->sizeBuff = (&buffer)[1]; /* Points to right after buffer. */ 3201 3202 return Render_Glyph( RAS_VAR ); 3203 } 3204 3205 3206 FT_DEFINE_RASTER_FUNCS( 3207 ft_standard_raster, 3208 3209 FT_GLYPH_FORMAT_OUTLINE, 3210 3211 (FT_Raster_New_Func) ft_black_new, 3212 (FT_Raster_Reset_Func) ft_black_reset, 3213 (FT_Raster_Set_Mode_Func)ft_black_set_mode, 3214 (FT_Raster_Render_Func) ft_black_render, 3215 (FT_Raster_Done_Func) ft_black_done ) 3216 3217 3218 /* END */ 3219