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