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