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