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