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