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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 Int dropOutControl = left->flags & 7; 2288 2289 FT_UNUSED( y ); 2290 FT_UNUSED( left ); 2291 FT_UNUSED( right ); 2292 2293 2294 /* Drop-out control */ 2295 2296 e1 = TRUNC( CEILING( x1 ) ); 2297 2298 if ( dropOutControl != 2 && 2299 x2 - x1 - ras.precision <= ras.precision_jitter ) 2300 e2 = e1; 2301 else 2302 e2 = TRUNC( FLOOR( x2 ) ); 2303 2304 if ( e2 >= 0 && e1 < ras.bWidth ) 2305 { 2306 int c1, c2; 2307 Byte f1, f2; 2308 2309 2310 if ( e1 < 0 ) 2311 e1 = 0; 2312 if ( e2 >= ras.bWidth ) 2313 e2 = ras.bWidth - 1; 2314 2315 c1 = (Short)( e1 >> 3 ); 2316 c2 = (Short)( e2 >> 3 ); 2317 2318 f1 = (Byte) ( 0xFF >> ( e1 & 7 ) ); 2319 f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) ); 2320 2321 if ( ras.gray_min_x > c1 ) 2322 ras.gray_min_x = (short)c1; 2323 if ( ras.gray_max_x < c2 ) 2324 ras.gray_max_x = (short)c2; 2325 2326 target = ras.bTarget + ras.traceOfs + c1; 2327 c2 -= c1; 2328 2329 if ( c2 > 0 ) 2330 { 2331 target[0] |= f1; 2332 2333 /* memset() is slower than the following code on many platforms. */ 2334 /* This is due to the fact that, in the vast majority of cases, */ 2335 /* the span length in bytes is relatively small. */ 2336 c2--; 2337 while ( c2 > 0 ) 2338 { 2339 *(++target) = 0xFF; 2340 c2--; 2341 } 2342 target[1] |= f2; 2343 } 2344 else 2345 *target |= ( f1 & f2 ); 2346 } 2347 } 2348 2349 2350 static void 2351 Vertical_Sweep_Drop( RAS_ARGS Short y, 2352 FT_F26Dot6 x1, 2353 FT_F26Dot6 x2, 2354 PProfile left, 2355 PProfile right ) 2356 { 2357 Long e1, e2, pxl; 2358 Short c1, f1; 2359 2360 2361 /* Drop-out control */ 2362 2363 /* e2 x2 x1 e1 */ 2364 /* */ 2365 /* ^ | */ 2366 /* | | */ 2367 /* +-------------+---------------------+------------+ */ 2368 /* | | */ 2369 /* | v */ 2370 /* */ 2371 /* pixel contour contour pixel */ 2372 /* center center */ 2373 2374 /* drop-out mode scan conversion rules (as defined in OpenType) */ 2375 /* --------------------------------------------------------------- */ 2376 /* 0 1, 2, 3 */ 2377 /* 1 1, 2, 4 */ 2378 /* 2 1, 2 */ 2379 /* 3 same as mode 2 */ 2380 /* 4 1, 2, 5 */ 2381 /* 5 1, 2, 6 */ 2382 /* 6, 7 same as mode 2 */ 2383 2384 e1 = CEILING( x1 ); 2385 e2 = FLOOR ( x2 ); 2386 pxl = e1; 2387 2388 if ( e1 > e2 ) 2389 { 2390 Int dropOutControl = left->flags & 7; 2391 2392 2393 if ( e1 == e2 + ras.precision ) 2394 { 2395 switch ( dropOutControl ) 2396 { 2397 case 0: /* simple drop-outs including stubs */ 2398 pxl = e2; 2399 break; 2400 2401 case 4: /* smart drop-outs including stubs */ 2402 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); 2403 break; 2404 2405 case 1: /* simple drop-outs excluding stubs */ 2406 case 5: /* smart drop-outs excluding stubs */ 2407 2408 /* Drop-out Control Rules #4 and #6 */ 2409 2410 /* The specification neither provides an exact definition */ 2411 /* of a `stub' nor gives exact rules to exclude them. */ 2412 /* */ 2413 /* Here the constraints we use to recognize a stub. */ 2414 /* */ 2415 /* upper stub: */ 2416 /* */ 2417 /* - P_Left and P_Right are in the same contour */ 2418 /* - P_Right is the successor of P_Left in that contour */ 2419 /* - y is the top of P_Left and P_Right */ 2420 /* */ 2421 /* lower stub: */ 2422 /* */ 2423 /* - P_Left and P_Right are in the same contour */ 2424 /* - P_Left is the successor of P_Right in that contour */ 2425 /* - y is the bottom of P_Left */ 2426 /* */ 2427 /* We draw a stub if the following constraints are met. */ 2428 /* */ 2429 /* - for an upper or lower stub, there is top or bottom */ 2430 /* overshoot, respectively */ 2431 /* - the covered interval is greater or equal to a half */ 2432 /* pixel */ 2433 2434 /* upper stub test */ 2435 if ( left->next == right && 2436 left->height <= 0 && 2437 !( left->flags & Overshoot_Top && 2438 x2 - x1 >= ras.precision_half ) ) 2439 return; 2440 2441 /* lower stub test */ 2442 if ( right->next == left && 2443 left->start == y && 2444 !( left->flags & Overshoot_Bottom && 2445 x2 - x1 >= ras.precision_half ) ) 2446 return; 2447 2448 if ( dropOutControl == 1 ) 2449 pxl = e2; 2450 else 2451 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); 2452 break; 2453 2454 default: /* modes 2, 3, 6, 7 */ 2455 return; /* no drop-out control */ 2456 } 2457 2458 /* undocumented but confirmed: If the drop-out would result in a */ 2459 /* pixel outside of the bounding box, use the pixel inside of the */ 2460 /* bounding box instead */ 2461 if ( pxl < 0 ) 2462 pxl = e1; 2463 else if ( TRUNC( pxl ) >= ras.bWidth ) 2464 pxl = e2; 2465 2466 /* check that the other pixel isn't set */ 2467 e1 = pxl == e1 ? e2 : e1; 2468 2469 e1 = TRUNC( e1 ); 2470 2471 c1 = (Short)( e1 >> 3 ); 2472 f1 = (Short)( e1 & 7 ); 2473 2474 if ( e1 >= 0 && e1 < ras.bWidth && 2475 ras.bTarget[ras.traceOfs + c1] & ( 0x80 >> f1 ) ) 2476 return; 2477 } 2478 else 2479 return; 2480 } 2481 2482 e1 = TRUNC( pxl ); 2483 2484 if ( e1 >= 0 && e1 < ras.bWidth ) 2485 { 2486 c1 = (Short)( e1 >> 3 ); 2487 f1 = (Short)( e1 & 7 ); 2488 2489 if ( ras.gray_min_x > c1 ) 2490 ras.gray_min_x = c1; 2491 if ( ras.gray_max_x < c1 ) 2492 ras.gray_max_x = c1; 2493 2494 ras.bTarget[ras.traceOfs + c1] |= (char)( 0x80 >> f1 ); 2495 } 2496 } 2497 2498 2499 static void 2500 Vertical_Sweep_Step( RAS_ARG ) 2501 { 2502 ras.traceOfs += ras.traceIncr; 2503 } 2504 2505 2506 /***********************************************************************/ 2507 /* */ 2508 /* Horizontal Sweep Procedure Set */ 2509 /* */ 2510 /* These four routines are used during the horizontal black/white */ 2511 /* sweep phase by the generic Draw_Sweep() function. */ 2512 /* */ 2513 /***********************************************************************/ 2514 2515 static void 2516 Horizontal_Sweep_Init( RAS_ARGS Short* min, 2517 Short* max ) 2518 { 2519 /* nothing, really */ 2520 FT_UNUSED_RASTER; 2521 FT_UNUSED( min ); 2522 FT_UNUSED( max ); 2523 } 2524 2525 2526 static void 2527 Horizontal_Sweep_Span( RAS_ARGS Short y, 2528 FT_F26Dot6 x1, 2529 FT_F26Dot6 x2, 2530 PProfile left, 2531 PProfile right ) 2532 { 2533 FT_UNUSED( left ); 2534 FT_UNUSED( right ); 2535 2536 2537 if ( x2 - x1 < ras.precision ) 2538 { 2539 Long e1, e2; 2540 2541 2542 e1 = CEILING( x1 ); 2543 e2 = FLOOR ( x2 ); 2544 2545 if ( e1 == e2 ) 2546 { 2547 Byte f1; 2548 PByte bits; 2549 2550 2551 bits = ras.bTarget + ( y >> 3 ); 2552 f1 = (Byte)( 0x80 >> ( y & 7 ) ); 2553 2554 e1 = TRUNC( e1 ); 2555 2556 if ( e1 >= 0 && e1 < ras.target.rows ) 2557 { 2558 PByte p; 2559 2560 2561 p = bits - e1 * ras.target.pitch; 2562 if ( ras.target.pitch > 0 ) 2563 p += ( ras.target.rows - 1 ) * ras.target.pitch; 2564 2565 p[0] |= f1; 2566 } 2567 } 2568 } 2569 } 2570 2571 2572 static void 2573 Horizontal_Sweep_Drop( RAS_ARGS Short y, 2574 FT_F26Dot6 x1, 2575 FT_F26Dot6 x2, 2576 PProfile left, 2577 PProfile right ) 2578 { 2579 Long e1, e2, pxl; 2580 PByte bits; 2581 Byte f1; 2582 2583 2584 /* During the horizontal sweep, we only take care of drop-outs */ 2585 2586 /* e1 + <-- pixel center */ 2587 /* | */ 2588 /* x1 ---+--> <-- contour */ 2589 /* | */ 2590 /* | */ 2591 /* x2 <--+--- <-- contour */ 2592 /* | */ 2593 /* | */ 2594 /* e2 + <-- pixel center */ 2595 2596 e1 = CEILING( x1 ); 2597 e2 = FLOOR ( x2 ); 2598 pxl = e1; 2599 2600 if ( e1 > e2 ) 2601 { 2602 Int dropOutControl = left->flags & 7; 2603 2604 2605 if ( e1 == e2 + ras.precision ) 2606 { 2607 switch ( dropOutControl ) 2608 { 2609 case 0: /* simple drop-outs including stubs */ 2610 pxl = e2; 2611 break; 2612 2613 case 4: /* smart drop-outs including stubs */ 2614 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); 2615 break; 2616 2617 case 1: /* simple drop-outs excluding stubs */ 2618 case 5: /* smart drop-outs excluding stubs */ 2619 /* see Vertical_Sweep_Drop for details */ 2620 2621 /* rightmost stub test */ 2622 if ( left->next == right && 2623 left->height <= 0 && 2624 !( left->flags & Overshoot_Top && 2625 x2 - x1 >= ras.precision_half ) ) 2626 return; 2627 2628 /* leftmost stub test */ 2629 if ( right->next == left && 2630 left->start == y && 2631 !( left->flags & Overshoot_Bottom && 2632 x2 - x1 >= ras.precision_half ) ) 2633 return; 2634 2635 if ( dropOutControl == 1 ) 2636 pxl = e2; 2637 else 2638 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); 2639 break; 2640 2641 default: /* modes 2, 3, 6, 7 */ 2642 return; /* no drop-out control */ 2643 } 2644 2645 /* undocumented but confirmed: If the drop-out would result in a */ 2646 /* pixel outside of the bounding box, use the pixel inside of the */ 2647 /* bounding box instead */ 2648 if ( pxl < 0 ) 2649 pxl = e1; 2650 else if ( TRUNC( pxl ) >= ras.target.rows ) 2651 pxl = e2; 2652 2653 /* check that the other pixel isn't set */ 2654 e1 = pxl == e1 ? e2 : e1; 2655 2656 e1 = TRUNC( e1 ); 2657 2658 bits = ras.bTarget + ( y >> 3 ); 2659 f1 = (Byte)( 0x80 >> ( y & 7 ) ); 2660 2661 bits -= e1 * ras.target.pitch; 2662 if ( ras.target.pitch > 0 ) 2663 bits += ( ras.target.rows - 1 ) * ras.target.pitch; 2664 2665 if ( e1 >= 0 && 2666 e1 < ras.target.rows && 2667 *bits & f1 ) 2668 return; 2669 } 2670 else 2671 return; 2672 } 2673 2674 bits = ras.bTarget + ( y >> 3 ); 2675 f1 = (Byte)( 0x80 >> ( y & 7 ) ); 2676 2677 e1 = TRUNC( pxl ); 2678 2679 if ( e1 >= 0 && e1 < ras.target.rows ) 2680 { 2681 bits -= e1 * ras.target.pitch; 2682 if ( ras.target.pitch > 0 ) 2683 bits += ( ras.target.rows - 1 ) * ras.target.pitch; 2684 2685 bits[0] |= f1; 2686 } 2687 } 2688 2689 2690 static void 2691 Horizontal_Sweep_Step( RAS_ARG ) 2692 { 2693 /* Nothing, really */ 2694 FT_UNUSED_RASTER; 2695 } 2696 2697 2698 #ifdef FT_RASTER_OPTION_ANTI_ALIASING 2699 2700 2701 /*************************************************************************/ 2702 /* */ 2703 /* Vertical Gray Sweep Procedure Set */ 2704 /* */ 2705 /* These two routines are used during the vertical gray-levels sweep */ 2706 /* phase by the generic Draw_Sweep() function. */ 2707 /* */ 2708 /* NOTES */ 2709 /* */ 2710 /* - The target pixmap's width *must* be a multiple of 4. */ 2711 /* */ 2712 /* - You have to use the function Vertical_Sweep_Span() for the gray */ 2713 /* span call. */ 2714 /* */ 2715 /*************************************************************************/ 2716 2717 static void 2718 Vertical_Gray_Sweep_Init( RAS_ARGS Short* min, 2719 Short* max ) 2720 { 2721 Long pitch, byte_len; 2722 2723 2724 *min = *min & -2; 2725 *max = ( *max + 3 ) & -2; 2726 2727 ras.traceOfs = 0; 2728 pitch = ras.target.pitch; 2729 byte_len = -pitch; 2730 ras.traceIncr = (Short)byte_len; 2731 ras.traceG = ( *min / 2 ) * byte_len; 2732 2733 if ( pitch > 0 ) 2734 { 2735 ras.traceG += ( ras.target.rows - 1 ) * pitch; 2736 byte_len = -byte_len; 2737 } 2738 2739 ras.gray_min_x = (Short)byte_len; 2740 ras.gray_max_x = -(Short)byte_len; 2741 } 2742 2743 2744 static void 2745 Vertical_Gray_Sweep_Step( RAS_ARG ) 2746 { 2747 short* count = (short*)count_table; 2748 Byte* grays; 2749 2750 2751 ras.traceOfs += ras.gray_width; 2752 2753 if ( ras.traceOfs > ras.gray_width ) 2754 { 2755 PByte pix; 2756 2757 2758 pix = ras.gTarget + ras.traceG + ras.gray_min_x * 4; 2759 grays = ras.grays; 2760 2761 if ( ras.gray_max_x >= 0 ) 2762 { 2763 Long last_pixel = ras.target.width - 1; 2764 Int last_cell = last_pixel >> 2; 2765 Int last_bit = last_pixel & 3; 2766 Bool over = 0; 2767 2768 Int c1, c2; 2769 PByte bit, bit2; 2770 2771 2772 if ( ras.gray_max_x >= last_cell && last_bit != 3 ) 2773 { 2774 ras.gray_max_x = last_cell - 1; 2775 over = 1; 2776 } 2777 2778 if ( ras.gray_min_x < 0 ) 2779 ras.gray_min_x = 0; 2780 2781 bit = ras.bTarget + ras.gray_min_x; 2782 bit2 = bit + ras.gray_width; 2783 2784 c1 = ras.gray_max_x - ras.gray_min_x; 2785 2786 while ( c1 >= 0 ) 2787 { 2788 c2 = count[*bit] + count[*bit2]; 2789 2790 if ( c2 ) 2791 { 2792 pix[0] = grays[(c2 >> 12) & 0x000F]; 2793 pix[1] = grays[(c2 >> 8 ) & 0x000F]; 2794 pix[2] = grays[(c2 >> 4 ) & 0x000F]; 2795 pix[3] = grays[ c2 & 0x000F]; 2796 2797 *bit = 0; 2798 *bit2 = 0; 2799 } 2800 2801 bit++; 2802 bit2++; 2803 pix += 4; 2804 c1--; 2805 } 2806 2807 if ( over ) 2808 { 2809 c2 = count[*bit] + count[*bit2]; 2810 if ( c2 ) 2811 { 2812 switch ( last_bit ) 2813 { 2814 case 2: 2815 pix[2] = grays[(c2 >> 4 ) & 0x000F]; 2816 case 1: 2817 pix[1] = grays[(c2 >> 8 ) & 0x000F]; 2818 default: 2819 pix[0] = grays[(c2 >> 12) & 0x000F]; 2820 } 2821 2822 *bit = 0; 2823 *bit2 = 0; 2824 } 2825 } 2826 } 2827 2828 ras.traceOfs = 0; 2829 ras.traceG += ras.traceIncr; 2830 2831 ras.gray_min_x = 32000; 2832 ras.gray_max_x = -32000; 2833 } 2834 } 2835 2836 2837 static void 2838 Horizontal_Gray_Sweep_Span( RAS_ARGS Short y, 2839 FT_F26Dot6 x1, 2840 FT_F26Dot6 x2, 2841 PProfile left, 2842 PProfile right ) 2843 { 2844 /* nothing, really */ 2845 FT_UNUSED_RASTER; 2846 FT_UNUSED( y ); 2847 FT_UNUSED( x1 ); 2848 FT_UNUSED( x2 ); 2849 FT_UNUSED( left ); 2850 FT_UNUSED( right ); 2851 } 2852 2853 2854 static void 2855 Horizontal_Gray_Sweep_Drop( RAS_ARGS Short y, 2856 FT_F26Dot6 x1, 2857 FT_F26Dot6 x2, 2858 PProfile left, 2859 PProfile right ) 2860 { 2861 Long e1, e2; 2862 PByte pixel; 2863 2864 2865 /* During the horizontal sweep, we only take care of drop-outs */ 2866 2867 e1 = CEILING( x1 ); 2868 e2 = FLOOR ( x2 ); 2869 2870 if ( e1 > e2 ) 2871 { 2872 Int dropOutControl = left->flags & 7; 2873 2874 2875 if ( e1 == e2 + ras.precision ) 2876 { 2877 switch ( dropOutControl ) 2878 { 2879 case 0: /* simple drop-outs including stubs */ 2880 e1 = e2; 2881 break; 2882 2883 case 4: /* smart drop-outs including stubs */ 2884 e1 = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); 2885 break; 2886 2887 case 1: /* simple drop-outs excluding stubs */ 2888 case 5: /* smart drop-outs excluding stubs */ 2889 /* see Vertical_Sweep_Drop for details */ 2890 2891 /* rightmost stub test */ 2892 if ( left->next == right && left->height <= 0 ) 2893 return; 2894 2895 /* leftmost stub test */ 2896 if ( right->next == left && left->start == y ) 2897 return; 2898 2899 if ( dropOutControl == 1 ) 2900 e1 = e2; 2901 else 2902 e1 = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); 2903 2904 break; 2905 2906 default: /* modes 2, 3, 6, 7 */ 2907 return; /* no drop-out control */ 2908 } 2909 } 2910 else 2911 return; 2912 } 2913 2914 if ( e1 >= 0 ) 2915 { 2916 Byte color; 2917 2918 2919 if ( x2 - x1 >= ras.precision_half ) 2920 color = ras.grays[2]; 2921 else 2922 color = ras.grays[1]; 2923 2924 e1 = TRUNC( e1 ) / 2; 2925 if ( e1 < ras.target.rows ) 2926 { 2927 pixel = ras.gTarget - e1 * ras.target.pitch + y / 2; 2928 if ( ras.target.pitch > 0 ) 2929 pixel += ( ras.target.rows - 1 ) * ras.target.pitch; 2930 2931 if ( pixel[0] == ras.grays[0] ) 2932 pixel[0] = color; 2933 } 2934 } 2935 } 2936 2937 2938 #endif /* FT_RASTER_OPTION_ANTI_ALIASING */ 2939 2940 2941 /*************************************************************************/ 2942 /* */ 2943 /* Generic Sweep Drawing routine */ 2944 /* */ 2945 /*************************************************************************/ 2946 2947 static Bool 2948 Draw_Sweep( RAS_ARG ) 2949 { 2950 Short y, y_change, y_height; 2951 2952 PProfile P, Q, P_Left, P_Right; 2953 2954 Short min_Y, max_Y, top, bottom, dropouts; 2955 2956 Long x1, x2, xs, e1, e2; 2957 2958 TProfileList waiting; 2959 TProfileList draw_left, draw_right; 2960 2961 2962 /* initialize empty linked lists */ 2963 2964 Init_Linked( &waiting ); 2965 2966 Init_Linked( &draw_left ); 2967 Init_Linked( &draw_right ); 2968 2969 /* first, compute min and max Y */ 2970 2971 P = ras.fProfile; 2972 max_Y = (Short)TRUNC( ras.minY ); 2973 min_Y = (Short)TRUNC( ras.maxY ); 2974 2975 while ( P ) 2976 { 2977 Q = P->link; 2978 2979 bottom = (Short)P->start; 2980 top = (Short)( P->start + P->height - 1 ); 2981 2982 if ( min_Y > bottom ) 2983 min_Y = bottom; 2984 if ( max_Y < top ) 2985 max_Y = top; 2986 2987 P->X = 0; 2988 InsNew( &waiting, P ); 2989 2990 P = Q; 2991 } 2992 2993 /* check the Y-turns */ 2994 if ( ras.numTurns == 0 ) 2995 { 2996 ras.error = FT_THROW( Invalid ); 2997 return FAILURE; 2998 } 2999 3000 /* now initialize the sweep */ 3001 3002 ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y ); 3003 3004 /* then compute the distance of each profile from min_Y */ 3005 3006 P = waiting; 3007 3008 while ( P ) 3009 { 3010 P->countL = (UShort)( P->start - min_Y ); 3011 P = P->link; 3012 } 3013 3014 /* let's go */ 3015 3016 y = min_Y; 3017 y_height = 0; 3018 3019 if ( ras.numTurns > 0 && 3020 ras.sizeBuff[-ras.numTurns] == min_Y ) 3021 ras.numTurns--; 3022 3023 while ( ras.numTurns > 0 ) 3024 { 3025 /* check waiting list for new activations */ 3026 3027 P = waiting; 3028 3029 while ( P ) 3030 { 3031 Q = P->link; 3032 P->countL -= y_height; 3033 if ( P->countL == 0 ) 3034 { 3035 DelOld( &waiting, P ); 3036 3037 if ( P->flags & Flow_Up ) 3038 InsNew( &draw_left, P ); 3039 else 3040 InsNew( &draw_right, P ); 3041 } 3042 3043 P = Q; 3044 } 3045 3046 /* sort the drawing lists */ 3047 3048 Sort( &draw_left ); 3049 Sort( &draw_right ); 3050 3051 y_change = (Short)ras.sizeBuff[-ras.numTurns--]; 3052 y_height = (Short)( y_change - y ); 3053 3054 while ( y < y_change ) 3055 { 3056 /* let's trace */ 3057 3058 dropouts = 0; 3059 3060 P_Left = draw_left; 3061 P_Right = draw_right; 3062 3063 while ( P_Left ) 3064 { 3065 x1 = P_Left ->X; 3066 x2 = P_Right->X; 3067 3068 if ( x1 > x2 ) 3069 { 3070 xs = x1; 3071 x1 = x2; 3072 x2 = xs; 3073 } 3074 3075 e1 = FLOOR( x1 ); 3076 e2 = CEILING( x2 ); 3077 3078 if ( x2 - x1 <= ras.precision && 3079 e1 != x1 && e2 != x2 ) 3080 { 3081 if ( e1 > e2 || e2 == e1 + ras.precision ) 3082 { 3083 Int dropOutControl = P_Left->flags & 7; 3084 3085 3086 if ( dropOutControl != 2 ) 3087 { 3088 /* a drop-out was detected */ 3089 3090 P_Left ->X = x1; 3091 P_Right->X = x2; 3092 3093 /* mark profile for drop-out processing */ 3094 P_Left->countL = 1; 3095 dropouts++; 3096 } 3097 3098 goto Skip_To_Next; 3099 } 3100 } 3101 3102 ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right ); 3103 3104 Skip_To_Next: 3105 3106 P_Left = P_Left->link; 3107 P_Right = P_Right->link; 3108 } 3109 3110 /* handle drop-outs _after_ the span drawing -- */ 3111 /* drop-out processing has been moved out of the loop */ 3112 /* for performance tuning */ 3113 if ( dropouts > 0 ) 3114 goto Scan_DropOuts; 3115 3116 Next_Line: 3117 3118 ras.Proc_Sweep_Step( RAS_VAR ); 3119 3120 y++; 3121 3122 if ( y < y_change ) 3123 { 3124 Sort( &draw_left ); 3125 Sort( &draw_right ); 3126 } 3127 } 3128 3129 /* now finalize the profiles that need it */ 3130 3131 P = draw_left; 3132 while ( P ) 3133 { 3134 Q = P->link; 3135 if ( P->height == 0 ) 3136 DelOld( &draw_left, P ); 3137 P = Q; 3138 } 3139 3140 P = draw_right; 3141 while ( P ) 3142 { 3143 Q = P->link; 3144 if ( P->height == 0 ) 3145 DelOld( &draw_right, P ); 3146 P = Q; 3147 } 3148 } 3149 3150 /* for gray-scaling, flush the bitmap scanline cache */ 3151 while ( y <= max_Y ) 3152 { 3153 ras.Proc_Sweep_Step( RAS_VAR ); 3154 y++; 3155 } 3156 3157 return SUCCESS; 3158 3159 Scan_DropOuts: 3160 3161 P_Left = draw_left; 3162 P_Right = draw_right; 3163 3164 while ( P_Left ) 3165 { 3166 if ( P_Left->countL ) 3167 { 3168 P_Left->countL = 0; 3169 #if 0 3170 dropouts--; /* -- this is useful when debugging only */ 3171 #endif 3172 ras.Proc_Sweep_Drop( RAS_VARS y, 3173 P_Left->X, 3174 P_Right->X, 3175 P_Left, 3176 P_Right ); 3177 } 3178 3179 P_Left = P_Left->link; 3180 P_Right = P_Right->link; 3181 } 3182 3183 goto Next_Line; 3184 } 3185 3186 3187 /*************************************************************************/ 3188 /* */ 3189 /* <Function> */ 3190 /* Render_Single_Pass */ 3191 /* */ 3192 /* <Description> */ 3193 /* Perform one sweep with sub-banding. */ 3194 /* */ 3195 /* <Input> */ 3196 /* flipped :: If set, flip the direction of the outline. */ 3197 /* */ 3198 /* <Return> */ 3199 /* Renderer error code. */ 3200 /* */ 3201 static int 3202 Render_Single_Pass( RAS_ARGS Bool flipped ) 3203 { 3204 Short i, j, k; 3205 3206 3207 while ( ras.band_top >= 0 ) 3208 { 3209 ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision; 3210 ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision; 3211 3212 ras.top = ras.buff; 3213 3214 ras.error = Raster_Err_None; 3215 3216 if ( Convert_Glyph( RAS_VARS flipped ) ) 3217 { 3218 if ( ras.error != Raster_Err_Overflow ) 3219 return FAILURE; 3220 3221 ras.error = Raster_Err_None; 3222 3223 /* sub-banding */ 3224 3225 #ifdef DEBUG_RASTER 3226 ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) ); 3227 #endif 3228 3229 i = ras.band_stack[ras.band_top].y_min; 3230 j = ras.band_stack[ras.band_top].y_max; 3231 3232 k = (Short)( ( i + j ) / 2 ); 3233 3234 if ( ras.band_top >= 7 || k < i ) 3235 { 3236 ras.band_top = 0; 3237 ras.error = FT_THROW( Invalid ); 3238 3239 return ras.error; 3240 } 3241 3242 ras.band_stack[ras.band_top + 1].y_min = k; 3243 ras.band_stack[ras.band_top + 1].y_max = j; 3244 3245 ras.band_stack[ras.band_top].y_max = (Short)( k - 1 ); 3246 3247 ras.band_top++; 3248 } 3249 else 3250 { 3251 if ( ras.fProfile ) 3252 if ( Draw_Sweep( RAS_VAR ) ) 3253 return ras.error; 3254 ras.band_top--; 3255 } 3256 } 3257 3258 return SUCCESS; 3259 } 3260 3261 3262 /*************************************************************************/ 3263 /* */ 3264 /* <Function> */ 3265 /* Render_Glyph */ 3266 /* */ 3267 /* <Description> */ 3268 /* Render a glyph in a bitmap. Sub-banding if needed. */ 3269 /* */ 3270 /* <Return> */ 3271 /* FreeType error code. 0 means success. */ 3272 /* */ 3273 FT_LOCAL_DEF( FT_Error ) 3274 Render_Glyph( RAS_ARG ) 3275 { 3276 FT_Error error; 3277 3278 3279 Set_High_Precision( RAS_VARS ras.outline.flags & 3280 FT_OUTLINE_HIGH_PRECISION ); 3281 ras.scale_shift = ras.precision_shift; 3282 3283 if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS ) 3284 ras.dropOutControl = 2; 3285 else 3286 { 3287 if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS ) 3288 ras.dropOutControl = 4; 3289 else 3290 ras.dropOutControl = 0; 3291 3292 if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) ) 3293 ras.dropOutControl += 1; 3294 } 3295 3296 ras.second_pass = (FT_Byte)( !( ras.outline.flags & 3297 FT_OUTLINE_SINGLE_PASS ) ); 3298 3299 /* Vertical Sweep */ 3300 ras.Proc_Sweep_Init = Vertical_Sweep_Init; 3301 ras.Proc_Sweep_Span = Vertical_Sweep_Span; 3302 ras.Proc_Sweep_Drop = Vertical_Sweep_Drop; 3303 ras.Proc_Sweep_Step = Vertical_Sweep_Step; 3304 3305 ras.band_top = 0; 3306 ras.band_stack[0].y_min = 0; 3307 ras.band_stack[0].y_max = (short)( ras.target.rows - 1 ); 3308 3309 ras.bWidth = (unsigned short)ras.target.width; 3310 ras.bTarget = (Byte*)ras.target.buffer; 3311 3312 if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 ) 3313 return error; 3314 3315 /* Horizontal Sweep */ 3316 if ( ras.second_pass && ras.dropOutControl != 2 ) 3317 { 3318 ras.Proc_Sweep_Init = Horizontal_Sweep_Init; 3319 ras.Proc_Sweep_Span = Horizontal_Sweep_Span; 3320 ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop; 3321 ras.Proc_Sweep_Step = Horizontal_Sweep_Step; 3322 3323 ras.band_top = 0; 3324 ras.band_stack[0].y_min = 0; 3325 ras.band_stack[0].y_max = (short)( ras.target.width - 1 ); 3326 3327 if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 ) 3328 return error; 3329 } 3330 3331 return Raster_Err_None; 3332 } 3333 3334 3335 #ifdef FT_RASTER_OPTION_ANTI_ALIASING 3336 3337 /*************************************************************************/ 3338 /* */ 3339 /* <Function> */ 3340 /* Render_Gray_Glyph */ 3341 /* */ 3342 /* <Description> */ 3343 /* Render a glyph with grayscaling. Sub-banding if needed. */ 3344 /* */ 3345 /* <Return> */ 3346 /* FreeType error code. 0 means success. */ 3347 /* */ 3348 FT_LOCAL_DEF( FT_Error ) 3349 Render_Gray_Glyph( RAS_ARG ) 3350 { 3351 Long pixel_width; 3352 FT_Error error; 3353 3354 3355 Set_High_Precision( RAS_VARS ras.outline.flags & 3356 FT_OUTLINE_HIGH_PRECISION ); 3357 ras.scale_shift = ras.precision_shift + 1; 3358 3359 if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS ) 3360 ras.dropOutControl = 2; 3361 else 3362 { 3363 if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS ) 3364 ras.dropOutControl = 4; 3365 else 3366 ras.dropOutControl = 0; 3367 3368 if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) ) 3369 ras.dropOutControl += 1; 3370 } 3371 3372 ras.second_pass = !( ras.outline.flags & FT_OUTLINE_SINGLE_PASS ); 3373 3374 /* Vertical Sweep */ 3375 3376 ras.band_top = 0; 3377 ras.band_stack[0].y_min = 0; 3378 ras.band_stack[0].y_max = 2 * ras.target.rows - 1; 3379 3380 ras.bWidth = ras.gray_width; 3381 pixel_width = 2 * ( ( ras.target.width + 3 ) >> 2 ); 3382 3383 if ( ras.bWidth > pixel_width ) 3384 ras.bWidth = pixel_width; 3385 3386 ras.bWidth = ras.bWidth * 8; 3387 ras.bTarget = (Byte*)ras.gray_lines; 3388 ras.gTarget = (Byte*)ras.target.buffer; 3389 3390 ras.Proc_Sweep_Init = Vertical_Gray_Sweep_Init; 3391 ras.Proc_Sweep_Span = Vertical_Sweep_Span; 3392 ras.Proc_Sweep_Drop = Vertical_Sweep_Drop; 3393 ras.Proc_Sweep_Step = Vertical_Gray_Sweep_Step; 3394 3395 error = Render_Single_Pass( RAS_VARS 0 ); 3396 if ( error ) 3397 return error; 3398 3399 /* Horizontal Sweep */ 3400 if ( ras.second_pass && ras.dropOutControl != 2 ) 3401 { 3402 ras.Proc_Sweep_Init = Horizontal_Sweep_Init; 3403 ras.Proc_Sweep_Span = Horizontal_Gray_Sweep_Span; 3404 ras.Proc_Sweep_Drop = Horizontal_Gray_Sweep_Drop; 3405 ras.Proc_Sweep_Step = Horizontal_Sweep_Step; 3406 3407 ras.band_top = 0; 3408 ras.band_stack[0].y_min = 0; 3409 ras.band_stack[0].y_max = ras.target.width * 2 - 1; 3410 3411 error = Render_Single_Pass( RAS_VARS 1 ); 3412 if ( error ) 3413 return error; 3414 } 3415 3416 return Raster_Err_None; 3417 } 3418 3419 #else /* !FT_RASTER_OPTION_ANTI_ALIASING */ 3420 3421 FT_LOCAL_DEF( FT_Error ) 3422 Render_Gray_Glyph( RAS_ARG ) 3423 { 3424 FT_UNUSED_RASTER; 3425 3426 return FT_THROW( Unsupported ); 3427 } 3428 3429 #endif /* !FT_RASTER_OPTION_ANTI_ALIASING */ 3430 3431 3432 static void 3433 ft_black_init( black_PRaster raster ) 3434 { 3435 #ifdef FT_RASTER_OPTION_ANTI_ALIASING 3436 FT_UInt n; 3437 3438 3439 /* set default 5-levels gray palette */ 3440 for ( n = 0; n < 5; n++ ) 3441 raster->grays[n] = n * 255 / 4; 3442 3443 raster->gray_width = RASTER_GRAY_LINES / 2; 3444 #else 3445 FT_UNUSED( raster ); 3446 #endif 3447 } 3448 3449 3450 /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/ 3451 /**** a static object. *****/ 3452 3453 3454 #ifdef _STANDALONE_ 3455 3456 3457 static int 3458 ft_black_new( void* memory, 3459 FT_Raster *araster ) 3460 { 3461 static black_TRaster the_raster; 3462 FT_UNUSED( memory ); 3463 3464 3465 *araster = (FT_Raster)&the_raster; 3466 FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) ); 3467 ft_black_init( &the_raster ); 3468 3469 return 0; 3470 } 3471 3472 3473 static void 3474 ft_black_done( FT_Raster raster ) 3475 { 3476 /* nothing */ 3477 FT_UNUSED( raster ); 3478 } 3479 3480 3481 #else /* !_STANDALONE_ */ 3482 3483 3484 static int 3485 ft_black_new( FT_Memory memory, 3486 black_PRaster *araster ) 3487 { 3488 FT_Error error; 3489 black_PRaster raster = NULL; 3490 3491 3492 *araster = 0; 3493 if ( !FT_NEW( raster ) ) 3494 { 3495 raster->memory = memory; 3496 ft_black_init( raster ); 3497 3498 *araster = raster; 3499 } 3500 3501 return error; 3502 } 3503 3504 3505 static void 3506 ft_black_done( black_PRaster raster ) 3507 { 3508 FT_Memory memory = (FT_Memory)raster->memory; 3509 3510 3511 FT_FREE( raster ); 3512 } 3513 3514 3515 #endif /* !_STANDALONE_ */ 3516 3517 3518 static void 3519 ft_black_reset( black_PRaster raster, 3520 char* pool_base, 3521 long pool_size ) 3522 { 3523 if ( raster ) 3524 { 3525 if ( pool_base && pool_size >= (long)sizeof ( black_TWorker ) + 2048 ) 3526 { 3527 black_PWorker worker = (black_PWorker)pool_base; 3528 3529 3530 raster->buffer = pool_base + ( ( sizeof ( *worker ) + 7 ) & ~7 ); 3531 raster->buffer_size = (long)( pool_base + pool_size - 3532 (char*)raster->buffer ); 3533 raster->worker = worker; 3534 } 3535 else 3536 { 3537 raster->buffer = NULL; 3538 raster->buffer_size = 0; 3539 raster->worker = NULL; 3540 } 3541 } 3542 } 3543 3544 3545 static void 3546 ft_black_set_mode( black_PRaster raster, 3547 unsigned long mode, 3548 const char* palette ) 3549 { 3550 #ifdef FT_RASTER_OPTION_ANTI_ALIASING 3551 3552 if ( mode == FT_MAKE_TAG( 'p', 'a', 'l', '5' ) ) 3553 { 3554 /* set 5-levels gray palette */ 3555 raster->grays[0] = palette[0]; 3556 raster->grays[1] = palette[1]; 3557 raster->grays[2] = palette[2]; 3558 raster->grays[3] = palette[3]; 3559 raster->grays[4] = palette[4]; 3560 } 3561 3562 #else 3563 3564 FT_UNUSED( raster ); 3565 FT_UNUSED( mode ); 3566 FT_UNUSED( palette ); 3567 3568 #endif 3569 } 3570 3571 3572 static int 3573 ft_black_render( black_PRaster raster, 3574 const FT_Raster_Params* params ) 3575 { 3576 const FT_Outline* outline = (const FT_Outline*)params->source; 3577 const FT_Bitmap* target_map = params->target; 3578 black_PWorker worker; 3579 3580 3581 if ( !raster || !raster->buffer || !raster->buffer_size ) 3582 return FT_THROW( Not_Ini ); 3583 3584 if ( !outline ) 3585 return FT_THROW( Invalid ); 3586 3587 /* return immediately if the outline is empty */ 3588 if ( outline->n_points == 0 || outline->n_contours <= 0 ) 3589 return Raster_Err_None; 3590 3591 if ( !outline->contours || !outline->points ) 3592 return FT_THROW( Invalid ); 3593 3594 if ( outline->n_points != 3595 outline->contours[outline->n_contours - 1] + 1 ) 3596 return FT_THROW( Invalid ); 3597 3598 worker = raster->worker; 3599 3600 /* this version of the raster does not support direct rendering, sorry */ 3601 if ( params->flags & FT_RASTER_FLAG_DIRECT ) 3602 return FT_THROW( Unsupported ); 3603 3604 if ( !target_map ) 3605 return FT_THROW( Invalid ); 3606 3607 /* nothing to do */ 3608 if ( !target_map->width || !target_map->rows ) 3609 return Raster_Err_None; 3610 3611 if ( !target_map->buffer ) 3612 return FT_THROW( Invalid ); 3613 3614 ras.outline = *outline; 3615 ras.target = *target_map; 3616 3617 worker->buff = (PLong) raster->buffer; 3618 worker->sizeBuff = worker->buff + 3619 raster->buffer_size / sizeof ( Long ); 3620 #ifdef FT_RASTER_OPTION_ANTI_ALIASING 3621 worker->grays = raster->grays; 3622 worker->gray_width = raster->gray_width; 3623 3624 FT_MEM_ZERO( worker->gray_lines, worker->gray_width * 2 ); 3625 #endif 3626 3627 return ( params->flags & FT_RASTER_FLAG_AA ) 3628 ? Render_Gray_Glyph( RAS_VAR ) 3629 : Render_Glyph( RAS_VAR ); 3630 } 3631 3632 3633 FT_DEFINE_RASTER_FUNCS( ft_standard_raster, 3634 FT_GLYPH_FORMAT_OUTLINE, 3635 (FT_Raster_New_Func) ft_black_new, 3636 (FT_Raster_Reset_Func) ft_black_reset, 3637 (FT_Raster_Set_Mode_Func)ft_black_set_mode, 3638 (FT_Raster_Render_Func) ft_black_render, 3639 (FT_Raster_Done_Func) ft_black_done 3640 ) 3641 3642 3643 /* END */ 3644