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