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