1 /***************************************************************************/
5 /* The FreeType glyph rasterizer (body). */
7 /* Copyright 1996-2003, 2005, 2007-2012 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
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. */
16 /***************************************************************************/
18 /*************************************************************************/
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 */
25 /* - copy `src/raster/ftraster.c' (this file) to your current directory */
27 /* - copy `include/freetype/ftimage.h' and `src/raster/ftmisc.h' */
28 /* to your current directory */
30 /* - compile `ftraster' with the _STANDALONE_ macro defined, as in */
32 /* cc -c -D_STANDALONE_ ftraster.c */
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'. */
38 /* See the comments and documentation in the file `ftimage.h' for more */
39 /* details on how the raster works. */
41 /*************************************************************************/
44 /*************************************************************************/
46 /* This is a rewrite of the FreeType 1.x scan-line converter */
48 /*************************************************************************/
52 #define FT_CONFIG_STANDARD_LIBRARY_H <stdlib.h>
54 #include <string.h> /* for memset */
59 #else /* !_STANDALONE_ */
63 #include FT_INTERNAL_CALC_H /* for FT_MulDiv only */
67 #endif /* !_STANDALONE_ */
70 /*************************************************************************/
72 /* A simple technical note on how the raster works */
73 /* ----------------------------------------------- */
75 /* Converting an outline into a bitmap is achieved in several steps: */
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 */
81 /* o its scanline position boundaries, i.e. `Ymin' and `Ymax' */
83 /* o an array of intersection coordinates for each scanline */
84 /* between `Ymin' and `Ymax' */
86 /* o a direction, indicating whether it was built going `up' or */
87 /* `down', as this is very important for filling rules */
89 /* o its drop-out mode */
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. */
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: */
99 /* __________________________________________________________ _ _ */
101 /* | profile | coordinates for | profile | coordinates for |--> */
102 /* | 1 | profile 1 | 2 | profile 2 |--> */
103 /* |_________|_________________|_________|_________________|__ _ _ */
107 /* start of render pool top */
109 /* The top of the profile stack is kept in the `top' variable. */
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. */
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: */
123 /* _ _ _______________________________________ */
125 /* <--| sorted list of | */
126 /* <--| extrema scanlines | */
127 /* _ _ __________________|____________________| */
131 /* maxBuff sizeBuff = end of pool */
133 /* This list is later used during the sweep phase in order to */
134 /* optimize performance (see technical note on the sweep below). */
136 /* Of course, the raster detects whether the two stacks collide and */
137 /* handles the situation properly. */
139 /*************************************************************************/
142 /*************************************************************************/
143 /*************************************************************************/
145 /** CONFIGURATION MACROS **/
147 /*************************************************************************/
148 /*************************************************************************/
150 /* define DEBUG_RASTER if you want to compile a debugging version */
151 /* #define DEBUG_RASTER */
153 /* define FT_RASTER_OPTION_ANTI_ALIASING if you want to support */
154 /* 5-levels anti-aliasing */
155 /* #define FT_RASTER_OPTION_ANTI_ALIASING */
157 /* The size of the two-lines intermediate bitmap used */
158 /* for anti-aliasing, in bytes. */
159 #define RASTER_GRAY_LINES 2048
162 /*************************************************************************/
163 /*************************************************************************/
165 /** OTHER MACROS (do not change) **/
167 /*************************************************************************/
168 /*************************************************************************/
170 /*************************************************************************/
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. */
177 #define FT_COMPONENT trace_raster
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)
189 /* Disable the tracing mechanism for simplicity -- developers can */
190 /* activate it easily by redefining these two macros. */
192 #define FT_ERROR( x ) do { } while ( 0 ) /* nothing */
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 */
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
208 #define ft_memset memset
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_ = \
223 #else /* !_STANDALONE_ */
226 #include FT_INTERNAL_OBJECTS_H
227 #include FT_INTERNAL_DEBUG_H /* for FT_TRACE() and FT_ERROR() */
229 #include "rasterrs.h"
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
239 #endif /* !_STANDALONE_ */
243 #define FT_MEM_SET( d, s, c ) ft_memset( d, s, c )
247 #define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count )
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 */
253 #define FMulDiv( a, b, c ) ( (a) * (b) / (c) )
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
260 /* The rasterizer is a very general purpose component; please leave */
261 /* the following redefinitions there (you never know your target */
273 #define NULL (void*)0
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. */
289 #define Pixel_Bits 6 /* fractional bits of *input* coordinates */
292 /*************************************************************************/
293 /*************************************************************************/
295 /** SIMPLE TYPE DECLARATIONS **/
297 /*************************************************************************/
298 /*************************************************************************/
301 typedef unsigned int UInt;
303 typedef unsigned short UShort, *PUShort;
304 typedef long Long, *PLong;
306 typedef unsigned char Byte, *PByte;
310 typedef union Alignment_
316 } Alignment, *PAlignment;
319 typedef struct TPoint_
327 /* values for the `flags' bit field */
329 #define Overshoot_Top 0x10
330 #define Overshoot_Bottom 0x20
333 /* States of each line, arc, and profile */
334 typedef enum TStates_
344 typedef struct TProfile_ TProfile;
345 typedef TProfile* PProfile;
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 */
359 unsigned countL; /* number of lines to step before this */
360 /* profile becomes drawable */
362 PProfile next; /* next profile in same contour, used */
363 /* during drop-out control */
366 typedef PProfile TProfileList;
367 typedef PProfile* PProfileList;
370 /* Simple record used to implement a stack of bands, required */
371 /* by the sub-banding mechanism */
372 typedef struct black_TBand_
374 Short y_min; /* band's minimum */
375 Short y_max; /* band's maximum */
380 #define AlignProfileSize \
381 ( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( long ) )
389 #ifdef FT_STATIC_RASTER
392 #define RAS_ARGS /* void */
393 #define RAS_ARG /* void */
395 #define RAS_VARS /* void */
396 #define RAS_VAR /* void */
398 #define FT_UNUSED_RASTER do { } while ( 0 )
401 #else /* !FT_STATIC_RASTER */
404 #define RAS_ARGS black_PWorker worker,
405 #define RAS_ARG black_PWorker worker
407 #define RAS_VARS worker,
408 #define RAS_VAR worker
410 #define FT_UNUSED_RASTER FT_UNUSED( worker )
413 #endif /* !FT_STATIC_RASTER */
416 typedef struct black_TWorker_ black_TWorker, *black_PWorker;
419 /* prototypes used for sweep function dispatch */
421 Function_Sweep_Init( RAS_ARGS Short* min,
425 Function_Sweep_Span( RAS_ARGS Short y,
432 Function_Sweep_Step( RAS_ARG );
435 /* NOTE: These operations are only valid on 2's complement processors */
441 #define FLOOR( x ) ( (x) & -ras.precision )
442 #define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision )
443 #define TRUNC( x ) ( (signed long)(x) >> ras.precision_bits )
444 #define FRAC( x ) ( (x) & ( ras.precision - 1 ) )
445 #define SCALED( x ) ( ( (x) << ras.scale_shift ) - ras.precision_half )
447 #define IS_BOTTOM_OVERSHOOT( x ) ( CEILING( x ) - x >= ras.precision_half )
448 #define IS_TOP_OVERSHOOT( x ) ( x - FLOOR( x ) >= ras.precision_half )
450 /* The most used variables are positioned at the top of the structure. */
451 /* Thus, their offset can be coded with less opcodes, resulting in a */
452 /* smaller executable. */
454 struct black_TWorker_
456 Int precision_bits; /* precision related variables */
461 Int precision_jitter;
463 Int scale_shift; /* == precision_shift for bitmaps */
464 /* == precision_shift+1 for pixmaps */
466 PLong buff; /* The profiles buffer */
467 PLong sizeBuff; /* Render pool size */
468 PLong maxBuff; /* Profiles buffer size */
469 PLong top; /* Current cursor in buffer */
473 Int numTurns; /* number of Y-turns in outline */
475 TPoint* arc; /* current Bezier arc pointer */
477 UShort bWidth; /* target bitmap width */
478 PByte bTarget; /* target bitmap buffer */
479 PByte gTarget; /* target pixmap buffer */
484 UShort num_Profs; /* current number of profiles */
486 Bool fresh; /* signals a fresh new profile which */
487 /* `start' field must be completed */
488 Bool joint; /* signals that the last arc ended */
489 /* exactly on a scanline. Allows */
490 /* removal of doublets */
491 PProfile cProfile; /* current profile */
492 PProfile fProfile; /* head of linked list of profiles */
493 PProfile gProfile; /* contour's first profile in case */
496 TStates state; /* rendering state */
498 FT_Bitmap target; /* description of target bit/pixmap */
501 Long traceOfs; /* current offset in target bitmap */
502 Long traceG; /* current offset in target pixmap */
504 Short traceIncr; /* sweep's increment in target bitmap */
506 Short gray_min_x; /* current min x during gray rendering */
507 Short gray_max_x; /* current max x during gray rendering */
509 /* dispatch variables */
511 Function_Sweep_Init* Proc_Sweep_Init;
512 Function_Sweep_Span* Proc_Sweep_Span;
513 Function_Sweep_Span* Proc_Sweep_Drop;
514 Function_Sweep_Step* Proc_Sweep_Step;
516 Byte dropOutControl; /* current drop_out control method */
518 Bool second_pass; /* indicates whether a horizontal pass */
519 /* should be performed to control */
520 /* drop-out accurately when calling */
521 /* Render_Glyph. Note that there is */
522 /* no horizontal pass during gray */
525 TPoint arcs[3 * MaxBezier + 1]; /* The Bezier stack */
527 black_TBand band_stack[16]; /* band stack used for sub-banding */
528 Int band_top; /* band stack top */
530 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
534 Byte gray_lines[RASTER_GRAY_LINES];
535 /* Intermediate table used to render the */
536 /* graylevels pixmaps. */
537 /* gray_lines is a buffer holding two */
538 /* monochrome scanlines */
540 Short gray_width; /* width in bytes of one monochrome */
541 /* intermediate scanline of gray_lines. */
542 /* Each gray pixel takes 2 bits long there */
544 /* The gray_lines must hold 2 lines, thus with size */
545 /* in bytes of at least `gray_width*2'. */
547 #endif /* FT_RASTER_ANTI_ALIASING */
552 typedef struct black_TRaster_
557 black_PWorker worker;
561 } black_TRaster, *black_PRaster;
563 #ifdef FT_STATIC_RASTER
565 static black_TWorker cur_ras;
568 #else /* !FT_STATIC_RASTER */
570 #define ras (*worker)
572 #endif /* !FT_STATIC_RASTER */
575 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
577 /* A lookup table used to quickly count set bits in four gray 2x2 */
578 /* cells. The values of the table have been produced with the */
579 /* following code: */
581 /* for ( i = 0; i < 256; i++ ) */
586 /* for ( c = 0; c < 4; c++ ) */
590 /* if ( j & 0x80 ) l++; */
591 /* if ( j & 0x40 ) l++; */
593 /* j = ( j << 2 ) & 0xFF; */
595 /* printf( "0x%04X", l ); */
599 static const short count_table[256] =
601 0x0000, 0x0001, 0x0001, 0x0002, 0x0010, 0x0011, 0x0011, 0x0012,
602 0x0010, 0x0011, 0x0011, 0x0012, 0x0020, 0x0021, 0x0021, 0x0022,
603 0x0100, 0x0101, 0x0101, 0x0102, 0x0110, 0x0111, 0x0111, 0x0112,
604 0x0110, 0x0111, 0x0111, 0x0112, 0x0120, 0x0121, 0x0121, 0x0122,
605 0x0100, 0x0101, 0x0101, 0x0102, 0x0110, 0x0111, 0x0111, 0x0112,
606 0x0110, 0x0111, 0x0111, 0x0112, 0x0120, 0x0121, 0x0121, 0x0122,
607 0x0200, 0x0201, 0x0201, 0x0202, 0x0210, 0x0211, 0x0211, 0x0212,
608 0x0210, 0x0211, 0x0211, 0x0212, 0x0220, 0x0221, 0x0221, 0x0222,
609 0x1000, 0x1001, 0x1001, 0x1002, 0x1010, 0x1011, 0x1011, 0x1012,
610 0x1010, 0x1011, 0x1011, 0x1012, 0x1020, 0x1021, 0x1021, 0x1022,
611 0x1100, 0x1101, 0x1101, 0x1102, 0x1110, 0x1111, 0x1111, 0x1112,
612 0x1110, 0x1111, 0x1111, 0x1112, 0x1120, 0x1121, 0x1121, 0x1122,
613 0x1100, 0x1101, 0x1101, 0x1102, 0x1110, 0x1111, 0x1111, 0x1112,
614 0x1110, 0x1111, 0x1111, 0x1112, 0x1120, 0x1121, 0x1121, 0x1122,
615 0x1200, 0x1201, 0x1201, 0x1202, 0x1210, 0x1211, 0x1211, 0x1212,
616 0x1210, 0x1211, 0x1211, 0x1212, 0x1220, 0x1221, 0x1221, 0x1222,
617 0x1000, 0x1001, 0x1001, 0x1002, 0x1010, 0x1011, 0x1011, 0x1012,
618 0x1010, 0x1011, 0x1011, 0x1012, 0x1020, 0x1021, 0x1021, 0x1022,
619 0x1100, 0x1101, 0x1101, 0x1102, 0x1110, 0x1111, 0x1111, 0x1112,
620 0x1110, 0x1111, 0x1111, 0x1112, 0x1120, 0x1121, 0x1121, 0x1122,
621 0x1100, 0x1101, 0x1101, 0x1102, 0x1110, 0x1111, 0x1111, 0x1112,
622 0x1110, 0x1111, 0x1111, 0x1112, 0x1120, 0x1121, 0x1121, 0x1122,
623 0x1200, 0x1201, 0x1201, 0x1202, 0x1210, 0x1211, 0x1211, 0x1212,
624 0x1210, 0x1211, 0x1211, 0x1212, 0x1220, 0x1221, 0x1221, 0x1222,
625 0x2000, 0x2001, 0x2001, 0x2002, 0x2010, 0x2011, 0x2011, 0x2012,
626 0x2010, 0x2011, 0x2011, 0x2012, 0x2020, 0x2021, 0x2021, 0x2022,
627 0x2100, 0x2101, 0x2101, 0x2102, 0x2110, 0x2111, 0x2111, 0x2112,
628 0x2110, 0x2111, 0x2111, 0x2112, 0x2120, 0x2121, 0x2121, 0x2122,
629 0x2100, 0x2101, 0x2101, 0x2102, 0x2110, 0x2111, 0x2111, 0x2112,
630 0x2110, 0x2111, 0x2111, 0x2112, 0x2120, 0x2121, 0x2121, 0x2122,
631 0x2200, 0x2201, 0x2201, 0x2202, 0x2210, 0x2211, 0x2211, 0x2212,
632 0x2210, 0x2211, 0x2211, 0x2212, 0x2220, 0x2221, 0x2221, 0x2222
635 #endif /* FT_RASTER_OPTION_ANTI_ALIASING */
639 /*************************************************************************/
640 /*************************************************************************/
642 /** PROFILES COMPUTATION **/
644 /*************************************************************************/
645 /*************************************************************************/
648 /*************************************************************************/
651 /* Set_High_Precision */
654 /* Set precision variables according to param flag. */
657 /* High :: Set to True for high precision (typically for ppem < 18), */
658 /* false otherwise. */
661 Set_High_Precision( RAS_ARGS Int High )
664 * `precision_step' is used in `Bezier_Up' to decide when to split a
665 * given y-monotonous Bezier arc that crosses a scanline before
666 * approximating it as a straight segment. The default value of 32 (for
667 * low accuracy) corresponds to
669 * 32 / 64 == 0.5 pixels ,
671 * while for the high accuracy case we have
673 * 256/ (1 << 12) = 0.0625 pixels .
675 * `precision_jitter' is an epsilon threshold used in
676 * `Vertical_Sweep_Span' to deal with small imperfections in the Bezier
677 * decomposition (after all, we are working with approximations only);
678 * it avoids switching on additional pixels which would cause artifacts
681 * The value of `precision_jitter' has been determined heuristically.
687 ras.precision_bits = 12;
688 ras.precision_step = 256;
689 ras.precision_jitter = 30;
693 ras.precision_bits = 6;
694 ras.precision_step = 32;
695 ras.precision_jitter = 2;
698 FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" ));
700 ras.precision = 1 << ras.precision_bits;
701 ras.precision_half = ras.precision / 2;
702 ras.precision_shift = ras.precision_bits - Pixel_Bits;
706 /*************************************************************************/
712 /* Create a new profile in the render pool. */
715 /* aState :: The state/orientation of the new profile. */
717 /* overshoot :: Whether the profile's unrounded start position */
718 /* differs by at least a half pixel. */
721 /* SUCCESS on success. FAILURE in case of overflow or of incoherent */
725 New_Profile( RAS_ARGS TStates aState,
730 ras.cProfile = (PProfile)ras.top;
731 ras.fProfile = ras.cProfile;
732 ras.top += AlignProfileSize;
735 if ( ras.top >= ras.maxBuff )
737 ras.error = Raster_Err_Overflow;
741 ras.cProfile->flags = 0;
742 ras.cProfile->start = 0;
743 ras.cProfile->height = 0;
744 ras.cProfile->offset = ras.top;
745 ras.cProfile->link = (PProfile)0;
746 ras.cProfile->next = (PProfile)0;
747 ras.cProfile->flags = ras.dropOutControl;
751 case Ascending_State:
752 ras.cProfile->flags |= Flow_Up;
754 ras.cProfile->flags |= Overshoot_Bottom;
756 FT_TRACE6(( "New ascending profile = %p\n", ras.cProfile ));
759 case Descending_State:
761 ras.cProfile->flags |= Overshoot_Top;
762 FT_TRACE6(( "New descending profile = %p\n", ras.cProfile ));
766 FT_ERROR(( "New_Profile: invalid profile direction\n" ));
767 ras.error = Raster_Err_Invalid;
772 ras.gProfile = ras.cProfile;
782 /*************************************************************************/
788 /* Finalize the current profile. */
791 /* overshoot :: Whether the profile's unrounded end position differs */
792 /* by at least a half pixel. */
795 /* SUCCESS on success. FAILURE in case of overflow or incoherency. */
798 End_Profile( RAS_ARGS Bool overshoot )
804 h = (Long)( ras.top - ras.cProfile->offset );
808 FT_ERROR(( "End_Profile: negative height encountered\n" ));
809 ras.error = Raster_Err_Neg_Height;
815 FT_TRACE6(( "Ending profile %p, start = %ld, height = %ld\n",
816 ras.cProfile, ras.cProfile->start, h ));
818 ras.cProfile->height = h;
821 if ( ras.cProfile->flags & Flow_Up )
822 ras.cProfile->flags |= Overshoot_Top;
824 ras.cProfile->flags |= Overshoot_Bottom;
827 oldProfile = ras.cProfile;
828 ras.cProfile = (PProfile)ras.top;
830 ras.top += AlignProfileSize;
832 ras.cProfile->height = 0;
833 ras.cProfile->offset = ras.top;
835 oldProfile->next = ras.cProfile;
839 if ( ras.top >= ras.maxBuff )
841 FT_TRACE1(( "overflow in End_Profile\n" ));
842 ras.error = Raster_Err_Overflow;
852 /*************************************************************************/
858 /* Insert a salient into the sorted list placed on top of the render */
862 /* New y scanline position. */
865 /* SUCCESS on success. FAILURE in case of overflow. */
868 Insert_Y_Turn( RAS_ARGS Int y )
874 n = ras.numTurns - 1;
875 y_turns = ras.sizeBuff - ras.numTurns;
877 /* look for first y value that is <= */
878 while ( n >= 0 && y < y_turns[n] )
881 /* if it is <, simply insert it, ignore if == */
882 if ( n >= 0 && y > y_turns[n] )
885 y2 = (Int)y_turns[n];
894 if ( ras.maxBuff <= ras.top )
896 ras.error = Raster_Err_Overflow;
900 ras.sizeBuff[-ras.numTurns] = y;
907 /*************************************************************************/
910 /* Finalize_Profile_Table */
913 /* Adjust all links in the profiles list. */
916 /* SUCCESS on success. FAILURE in case of overflow. */
919 Finalize_Profile_Table( RAS_ARG )
934 p->link = (PProfile)( p->offset + p->height );
938 if ( p->flags & Flow_Up )
940 bottom = (Int)p->start;
941 top = (Int)( p->start + p->height - 1 );
945 bottom = (Int)( p->start - p->height + 1 );
948 p->offset += p->height - 1;
951 if ( Insert_Y_Turn( RAS_VARS bottom ) ||
952 Insert_Y_Turn( RAS_VARS top + 1 ) )
966 /*************************************************************************/
972 /* Subdivide one conic Bezier into two joint sub-arcs in the Bezier */
976 /* None (subdivided Bezier is taken from the top of the stack). */
979 /* This routine is the `beef' of this component. It is _the_ inner */
980 /* loop that should be optimized to hell to get the best performance. */
983 Split_Conic( TPoint* base )
988 base[4].x = base[2].x;
990 a = base[3].x = ( base[2].x + b ) / 2;
991 b = base[1].x = ( base[0].x + b ) / 2;
992 base[2].x = ( a + b ) / 2;
994 base[4].y = base[2].y;
996 a = base[3].y = ( base[2].y + b ) / 2;
997 b = base[1].y = ( base[0].y + b ) / 2;
998 base[2].y = ( a + b ) / 2;
1000 /* hand optimized. gcc doesn't seem to be too good at common */
1001 /* expression substitution and instruction scheduling ;-) */
1005 /*************************************************************************/
1011 /* Subdivide a third-order Bezier arc into two joint sub-arcs in the */
1015 /* This routine is the `beef' of the component. It is one of _the_ */
1016 /* inner loops that should be optimized like hell to get the best */
1020 Split_Cubic( TPoint* base )
1025 base[6].x = base[3].x;
1028 base[1].x = a = ( base[0].x + c + 1 ) >> 1;
1029 base[5].x = b = ( base[3].x + d + 1 ) >> 1;
1030 c = ( c + d + 1 ) >> 1;
1031 base[2].x = a = ( a + c + 1 ) >> 1;
1032 base[4].x = b = ( b + c + 1 ) >> 1;
1033 base[3].x = ( a + b + 1 ) >> 1;
1035 base[6].y = base[3].y;
1038 base[1].y = a = ( base[0].y + c + 1 ) >> 1;
1039 base[5].y = b = ( base[3].y + d + 1 ) >> 1;
1040 c = ( c + d + 1 ) >> 1;
1041 base[2].y = a = ( a + c + 1 ) >> 1;
1042 base[4].y = b = ( b + c + 1 ) >> 1;
1043 base[3].y = ( a + b + 1 ) >> 1;
1047 /*************************************************************************/
1053 /* Compute the x-coordinates of an ascending line segment and store */
1054 /* them in the render pool. */
1057 /* x1 :: The x-coordinate of the segment's start point. */
1059 /* y1 :: The y-coordinate of the segment's start point. */
1061 /* x2 :: The x-coordinate of the segment's end point. */
1063 /* y2 :: The y-coordinate of the segment's end point. */
1065 /* miny :: A lower vertical clipping bound value. */
1067 /* maxy :: An upper vertical clipping bound value. */
1070 /* SUCCESS on success, FAILURE on render pool overflow. */
1073 Line_Up( RAS_ARGS Long x1,
1081 Int e1, e2, f1, f2, size; /* XXX: is `Short' sufficient? */
1090 if ( Dy <= 0 || y2 < miny || y1 > maxy )
1095 /* Take care: miny-y1 can be a very large value; we use */
1096 /* a slow MulDiv function to avoid clipping bugs */
1097 x1 += SMulDiv( Dx, miny - y1, Dy );
1098 e1 = (Int)TRUNC( miny );
1103 e1 = (Int)TRUNC( y1 );
1104 f1 = (Int)FRAC( y1 );
1109 /* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */
1110 e2 = (Int)TRUNC( maxy );
1115 e2 = (Int)TRUNC( y2 );
1116 f2 = (Int)FRAC( y2 );
1125 x1 += SMulDiv( Dx, ras.precision - f1, Dy );
1136 ras.joint = (char)( f2 == 0 );
1140 ras.cProfile->start = e1;
1145 if ( ras.top + size >= ras.maxBuff )
1147 ras.error = Raster_Err_Overflow;
1153 Ix = SMulDiv( ras.precision, Dx, Dy);
1154 Rx = ( ras.precision * Dx ) % Dy;
1159 Ix = SMulDiv( ras.precision, -Dx, Dy) * -1;
1160 Rx = ( ras.precision * -Dx ) % Dy;
1186 /*************************************************************************/
1192 /* Compute the x-coordinates of an descending line segment and store */
1193 /* them in the render pool. */
1196 /* x1 :: The x-coordinate of the segment's start point. */
1198 /* y1 :: The y-coordinate of the segment's start point. */
1200 /* x2 :: The x-coordinate of the segment's end point. */
1202 /* y2 :: The y-coordinate of the segment's end point. */
1204 /* miny :: A lower vertical clipping bound value. */
1206 /* maxy :: An upper vertical clipping bound value. */
1209 /* SUCCESS on success, FAILURE on render pool overflow. */
1212 Line_Down( RAS_ARGS Long x1,
1224 result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );
1226 if ( fresh && !ras.fresh )
1227 ras.cProfile->start = -ras.cProfile->start;
1233 /* A function type describing the functions used to split Bezier arcs */
1234 typedef void (*TSplitter)( TPoint* base );
1237 /*************************************************************************/
1243 /* Compute the x-coordinates of an ascending Bezier arc and store */
1244 /* them in the render pool. */
1247 /* degree :: The degree of the Bezier arc (either 2 or 3). */
1249 /* splitter :: The function to split Bezier arcs. */
1251 /* miny :: A lower vertical clipping bound value. */
1253 /* maxy :: An upper vertical clipping bound value. */
1256 /* SUCCESS on success, FAILURE on render pool overflow. */
1259 Bezier_Up( RAS_ARGS Int degree,
1264 Long y1, y2, e, e2, e0;
1278 if ( y2 < miny || y1 > maxy )
1293 f1 = (Short)( FRAC( y1 ) );
1304 *top++ = arc[degree].x;
1312 ras.cProfile->start = TRUNC( e0 );
1319 if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff )
1322 ras.error = Raster_Err_Overflow;
1328 while ( arc >= start_arc && e <= e2 )
1337 if ( y2 - y1 >= ras.precision_step )
1344 *top++ = arc[degree].x + FMulDiv( arc[0].x - arc[degree].x,
1370 /*************************************************************************/
1376 /* Compute the x-coordinates of an descending Bezier arc and store */
1377 /* them in the render pool. */
1380 /* degree :: The degree of the Bezier arc (either 2 or 3). */
1382 /* splitter :: The function to split Bezier arcs. */
1384 /* miny :: A lower vertical clipping bound value. */
1386 /* maxy :: An upper vertical clipping bound value. */
1389 /* SUCCESS on success, FAILURE on render pool overflow. */
1392 Bezier_Down( RAS_ARGS Int degree,
1397 TPoint* arc = ras.arc;
1401 arc[0].y = -arc[0].y;
1402 arc[1].y = -arc[1].y;
1403 arc[2].y = -arc[2].y;
1405 arc[3].y = -arc[3].y;
1409 result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny );
1411 if ( fresh && !ras.fresh )
1412 ras.cProfile->start = -ras.cProfile->start;
1414 arc[0].y = -arc[0].y;
1419 /*************************************************************************/
1425 /* Inject a new line segment and adjust the Profiles list. */
1428 /* x :: The x-coordinate of the segment's end point (its start point */
1429 /* is stored in `lastX'). */
1431 /* y :: The y-coordinate of the segment's end point (its start point */
1432 /* is stored in `lastY'). */
1435 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1439 Line_To( RAS_ARGS Long x,
1442 /* First, detect a change of direction */
1444 switch ( ras.state )
1447 if ( y > ras.lastY )
1449 if ( New_Profile( RAS_VARS Ascending_State,
1450 IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
1455 if ( y < ras.lastY )
1456 if ( New_Profile( RAS_VARS Descending_State,
1457 IS_TOP_OVERSHOOT( ras.lastY ) ) )
1462 case Ascending_State:
1463 if ( y < ras.lastY )
1465 if ( End_Profile( RAS_VARS IS_TOP_OVERSHOOT( ras.lastY ) ) ||
1466 New_Profile( RAS_VARS Descending_State,
1467 IS_TOP_OVERSHOOT( ras.lastY ) ) )
1472 case Descending_State:
1473 if ( y > ras.lastY )
1475 if ( End_Profile( RAS_VARS IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ||
1476 New_Profile( RAS_VARS Ascending_State,
1477 IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
1486 /* Then compute the lines */
1488 switch ( ras.state )
1490 case Ascending_State:
1491 if ( Line_Up( RAS_VARS ras.lastX, ras.lastY,
1492 x, y, ras.minY, ras.maxY ) )
1496 case Descending_State:
1497 if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
1498 x, y, ras.minY, ras.maxY ) )
1513 /*************************************************************************/
1519 /* Inject a new conic arc and adjust the profile list. */
1522 /* cx :: The x-coordinate of the arc's new control point. */
1524 /* cy :: The y-coordinate of the arc's new control point. */
1526 /* x :: The x-coordinate of the arc's end point (its start point is */
1527 /* stored in `lastX'). */
1529 /* y :: The y-coordinate of the arc's end point (its start point is */
1530 /* stored in `lastY'). */
1533 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1537 Conic_To( RAS_ARGS Long cx,
1542 Long y1, y2, y3, x3, ymin, ymax;
1547 ras.arc[2].x = ras.lastX;
1548 ras.arc[2].y = ras.lastY;
1561 /* first, categorize the Bezier arc */
1574 if ( y2 < ymin || y2 > ymax )
1576 /* this arc has no given direction, split it! */
1577 Split_Conic( ras.arc );
1580 else if ( y1 == y3 )
1582 /* this arc is flat, ignore it and pop it from the Bezier stack */
1587 /* the arc is y-monotonous, either ascending or descending */
1588 /* detect a change of direction */
1589 state_bez = y1 < y3 ? Ascending_State : Descending_State;
1590 if ( ras.state != state_bez )
1592 Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 )
1593 : IS_TOP_OVERSHOOT( y1 );
1596 /* finalize current profile if any */
1597 if ( ras.state != Unknown_State &&
1598 End_Profile( RAS_VARS o ) )
1601 /* create a new profile */
1602 if ( New_Profile( RAS_VARS state_bez, o ) )
1606 /* now call the appropriate routine */
1607 if ( state_bez == Ascending_State )
1609 if ( Bezier_Up( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
1613 if ( Bezier_Down( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
1617 } while ( ras.arc >= ras.arcs );
1629 /*************************************************************************/
1635 /* Inject a new cubic arc and adjust the profile list. */
1638 /* cx1 :: The x-coordinate of the arc's first new control point. */
1640 /* cy1 :: The y-coordinate of the arc's first new control point. */
1642 /* cx2 :: The x-coordinate of the arc's second new control point. */
1644 /* cy2 :: The y-coordinate of the arc's second new control point. */
1646 /* x :: The x-coordinate of the arc's end point (its start point is */
1647 /* stored in `lastX'). */
1649 /* y :: The y-coordinate of the arc's end point (its start point is */
1650 /* stored in `lastY'). */
1653 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1657 Cubic_To( RAS_ARGS Long cx1,
1664 Long y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
1669 ras.arc[3].x = ras.lastX;
1670 ras.arc[3].y = ras.lastY;
1686 /* first, categorize the Bezier arc */
1710 if ( ymin2 < ymin1 || ymax2 > ymax1 )
1712 /* this arc has no given direction, split it! */
1713 Split_Cubic( ras.arc );
1716 else if ( y1 == y4 )
1718 /* this arc is flat, ignore it and pop it from the Bezier stack */
1723 state_bez = ( y1 <= y4 ) ? Ascending_State : Descending_State;
1725 /* detect a change of direction */
1726 if ( ras.state != state_bez )
1728 Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 )
1729 : IS_TOP_OVERSHOOT( y1 );
1732 /* finalize current profile if any */
1733 if ( ras.state != Unknown_State &&
1734 End_Profile( RAS_VARS o ) )
1737 if ( New_Profile( RAS_VARS state_bez, o ) )
1741 /* compute intersections */
1742 if ( state_bez == Ascending_State )
1744 if ( Bezier_Up( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
1748 if ( Bezier_Down( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
1752 } while ( ras.arc >= ras.arcs );
1765 #define SWAP_( x, y ) do \
1775 /*************************************************************************/
1778 /* Decompose_Curve */
1781 /* Scan the outline arrays in order to emit individual segments and */
1782 /* Beziers by calling Line_To() and Bezier_To(). It handles all */
1783 /* weird cases, like when the first point is off the curve, or when */
1784 /* there are simply no `on' points in the contour! */
1787 /* first :: The index of the first point in the contour. */
1789 /* last :: The index of the last point in the contour. */
1791 /* flipped :: If set, flip the direction of the curve. */
1794 /* SUCCESS on success, FAILURE on error. */
1797 Decompose_Curve( RAS_ARGS UShort first,
1802 FT_Vector v_control;
1810 unsigned tag; /* current point's state */
1813 points = ras.outline.points;
1814 limit = points + last;
1816 v_start.x = SCALED( points[first].x );
1817 v_start.y = SCALED( points[first].y );
1818 v_last.x = SCALED( points[last].x );
1819 v_last.y = SCALED( points[last].y );
1823 SWAP_( v_start.x, v_start.y );
1824 SWAP_( v_last.x, v_last.y );
1827 v_control = v_start;
1829 point = points + first;
1830 tags = ras.outline.tags + first;
1832 /* set scan mode if necessary */
1833 if ( tags[0] & FT_CURVE_TAG_HAS_SCANMODE )
1834 ras.dropOutControl = (Byte)tags[0] >> 5;
1836 tag = FT_CURVE_TAG( tags[0] );
1838 /* A contour cannot start with a cubic control point! */
1839 if ( tag == FT_CURVE_TAG_CUBIC )
1840 goto Invalid_Outline;
1842 /* check first point to determine origin */
1843 if ( tag == FT_CURVE_TAG_CONIC )
1845 /* first point is conic control. Yes, this happens. */
1846 if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_CURVE_TAG_ON )
1848 /* start at last point if it is on the curve */
1854 /* if both first and last points are conic, */
1855 /* start at their middle and record its position */
1857 v_start.x = ( v_start.x + v_last.x ) / 2;
1858 v_start.y = ( v_start.y + v_last.y ) / 2;
1866 ras.lastX = v_start.x;
1867 ras.lastY = v_start.y;
1869 while ( point < limit )
1874 tag = FT_CURVE_TAG( tags[0] );
1878 case FT_CURVE_TAG_ON: /* emit a single line_to */
1883 x = SCALED( point->x );
1884 y = SCALED( point->y );
1888 if ( Line_To( RAS_VARS x, y ) )
1893 case FT_CURVE_TAG_CONIC: /* consume conic arcs */
1894 v_control.x = SCALED( point[0].x );
1895 v_control.y = SCALED( point[0].y );
1898 SWAP_( v_control.x, v_control.y );
1901 if ( point < limit )
1909 tag = FT_CURVE_TAG( tags[0] );
1911 x = SCALED( point[0].x );
1912 y = SCALED( point[0].y );
1917 if ( tag == FT_CURVE_TAG_ON )
1919 if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) )
1924 if ( tag != FT_CURVE_TAG_CONIC )
1925 goto Invalid_Outline;
1927 v_middle.x = ( v_control.x + x ) / 2;
1928 v_middle.y = ( v_control.y + y ) / 2;
1930 if ( Conic_To( RAS_VARS v_control.x, v_control.y,
1931 v_middle.x, v_middle.y ) )
1940 if ( Conic_To( RAS_VARS v_control.x, v_control.y,
1941 v_start.x, v_start.y ) )
1946 default: /* FT_CURVE_TAG_CUBIC */
1948 Long x1, y1, x2, y2, x3, y3;
1951 if ( point + 1 > limit ||
1952 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
1953 goto Invalid_Outline;
1958 x1 = SCALED( point[-2].x );
1959 y1 = SCALED( point[-2].y );
1960 x2 = SCALED( point[-1].x );
1961 y2 = SCALED( point[-1].y );
1969 if ( point <= limit )
1971 x3 = SCALED( point[0].x );
1972 y3 = SCALED( point[0].y );
1977 if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) )
1982 if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) )
1989 /* close the contour with a line segment */
1990 if ( Line_To( RAS_VARS v_start.x, v_start.y ) )
1997 ras.error = Raster_Err_Invalid;
2004 /*************************************************************************/
2010 /* Convert a glyph into a series of segments and arcs and make a */
2011 /* profiles list with them. */
2014 /* flipped :: If set, flip the direction of curve. */
2017 /* SUCCESS on success, FAILURE if any error was encountered during */
2021 Convert_Glyph( RAS_ARGS int flipped )
2026 PProfile lastProfile;
2029 ras.fProfile = NULL;
2033 ras.maxBuff = ras.sizeBuff - AlignProfileSize;
2037 ras.cProfile = (PProfile)ras.top;
2038 ras.cProfile->offset = ras.top;
2043 for ( i = 0; i < ras.outline.n_contours; i++ )
2048 ras.state = Unknown_State;
2049 ras.gProfile = NULL;
2051 if ( Decompose_Curve( RAS_VARS (unsigned short)start,
2052 ras.outline.contours[i],
2056 start = ras.outline.contours[i] + 1;
2058 /* we must now check whether the extreme arcs join or not */
2059 if ( FRAC( ras.lastY ) == 0 &&
2060 ras.lastY >= ras.minY &&
2061 ras.lastY <= ras.maxY )
2062 if ( ras.gProfile &&
2063 ( ras.gProfile->flags & Flow_Up ) ==
2064 ( ras.cProfile->flags & Flow_Up ) )
2066 /* Note that ras.gProfile can be nil if the contour was too small */
2069 lastProfile = ras.cProfile;
2070 if ( ras.cProfile->flags & Flow_Up )
2071 o = IS_TOP_OVERSHOOT( ras.lastY );
2073 o = IS_BOTTOM_OVERSHOOT( ras.lastY );
2074 if ( End_Profile( RAS_VARS o ) )
2077 /* close the `next profile in contour' linked list */
2079 lastProfile->next = ras.gProfile;
2082 if ( Finalize_Profile_Table( RAS_VAR ) )
2085 return (Bool)( ras.top < ras.maxBuff ? SUCCESS : FAILURE );
2089 /*************************************************************************/
2090 /*************************************************************************/
2092 /** SCAN-LINE SWEEPS AND DRAWING **/
2094 /*************************************************************************/
2095 /*************************************************************************/
2098 /*************************************************************************/
2102 /* Initializes an empty linked list. */
2105 Init_Linked( TProfileList* l )
2111 /*************************************************************************/
2115 /* Inserts a new profile in a linked list. */
2118 InsNew( PProfileList list,
2121 PProfile *old, current;
2131 if ( x < current->X )
2133 old = ¤t->link;
2137 profile->link = current;
2142 /*************************************************************************/
2146 /* Removes an old profile from a linked list. */
2149 DelOld( PProfileList list,
2152 PProfile *old, current;
2160 if ( current == profile )
2162 *old = current->link;
2166 old = ¤t->link;
2170 /* we should never get there, unless the profile was not part of */
2175 /*************************************************************************/
2179 /* Sorts a trace list. In 95%, the list is already sorted. We need */
2180 /* an algorithm which is fast in this case. Bubble sort is enough */
2184 Sort( PProfileList list )
2186 PProfile *old, current, next;
2189 /* First, set the new X coordinate of each profile */
2193 current->X = *current->offset;
2194 current->offset += current->flags & Flow_Up ? 1 : -1;
2196 current = current->link;
2199 /* Then sort them */
2206 next = current->link;
2210 if ( current->X <= next->X )
2212 old = ¤t->link;
2221 current->link = next->link;
2222 next->link = current;
2228 next = current->link;
2233 /*************************************************************************/
2235 /* Vertical Sweep Procedure Set */
2237 /* These four routines are used during the vertical black/white sweep */
2238 /* phase by the generic Draw_Sweep() function. */
2240 /*************************************************************************/
2243 Vertical_Sweep_Init( RAS_ARGS Short* min,
2246 Long pitch = ras.target.pitch;
2251 ras.traceIncr = (Short)-pitch;
2252 ras.traceOfs = -*min * pitch;
2254 ras.traceOfs += ( ras.target.rows - 1 ) * pitch;
2262 Vertical_Sweep_Span( RAS_ARGS Short y,
2278 /* Drop-out control */
2280 e1 = TRUNC( CEILING( x1 ) );
2282 if ( x2 - x1 - ras.precision <= ras.precision_jitter )
2285 e2 = TRUNC( FLOOR( x2 ) );
2287 if ( e2 >= 0 && e1 < ras.bWidth )
2291 if ( e2 >= ras.bWidth )
2292 e2 = ras.bWidth - 1;
2294 c1 = (Short)( e1 >> 3 );
2295 c2 = (Short)( e2 >> 3 );
2297 f1 = (Byte) ( 0xFF >> ( e1 & 7 ) );
2298 f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) );
2300 if ( ras.gray_min_x > c1 )
2301 ras.gray_min_x = (short)c1;
2302 if ( ras.gray_max_x < c2 )
2303 ras.gray_max_x = (short)c2;
2305 target = ras.bTarget + ras.traceOfs + c1;
2312 /* memset() is slower than the following code on many platforms. */
2313 /* This is due to the fact that, in the vast majority of cases, */
2314 /* the span length in bytes is relatively small. */
2324 *target |= ( f1 & f2 );
2330 Vertical_Sweep_Drop( RAS_ARGS Short y,
2340 /* Drop-out control */
2346 /* +-------------+---------------------+------------+ */
2350 /* pixel contour contour pixel */
2353 /* drop-out mode scan conversion rules (as defined in OpenType) */
2354 /* --------------------------------------------------------------- */
2358 /* 3 same as mode 2 */
2361 /* 6, 7 same as mode 2 */
2369 Int dropOutControl = left->flags & 7;
2372 if ( e1 == e2 + ras.precision )
2374 switch ( dropOutControl )
2376 case 0: /* simple drop-outs including stubs */
2380 case 4: /* smart drop-outs including stubs */
2381 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2384 case 1: /* simple drop-outs excluding stubs */
2385 case 5: /* smart drop-outs excluding stubs */
2387 /* Drop-out Control Rules #4 and #6 */
2389 /* The specification neither provides an exact definition */
2390 /* of a `stub' nor gives exact rules to exclude them. */
2392 /* Here the constraints we use to recognize a stub. */
2396 /* - P_Left and P_Right are in the same contour */
2397 /* - P_Right is the successor of P_Left in that contour */
2398 /* - y is the top of P_Left and P_Right */
2402 /* - P_Left and P_Right are in the same contour */
2403 /* - P_Left is the successor of P_Right in that contour */
2404 /* - y is the bottom of P_Left */
2406 /* We draw a stub if the following constraints are met. */
2408 /* - for an upper or lower stub, there is top or bottom */
2409 /* overshoot, respectively */
2410 /* - the covered interval is greater or equal to a half */
2413 /* upper stub test */
2414 if ( left->next == right &&
2415 left->height <= 0 &&
2416 !( left->flags & Overshoot_Top &&
2417 x2 - x1 >= ras.precision_half ) )
2420 /* lower stub test */
2421 if ( right->next == left &&
2423 !( left->flags & Overshoot_Bottom &&
2424 x2 - x1 >= ras.precision_half ) )
2427 if ( dropOutControl == 1 )
2430 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2433 default: /* modes 2, 3, 6, 7 */
2434 return; /* no drop-out control */
2437 /* undocumented but confirmed: If the drop-out would result in a */
2438 /* pixel outside of the bounding box, use the pixel inside of the */
2439 /* bounding box instead */
2442 else if ( TRUNC( pxl ) >= ras.bWidth )
2445 /* check that the other pixel isn't set */
2446 e1 = pxl == e1 ? e2 : e1;
2450 c1 = (Short)( e1 >> 3 );
2451 f1 = (Short)( e1 & 7 );
2453 if ( e1 >= 0 && e1 < ras.bWidth &&
2454 ras.bTarget[ras.traceOfs + c1] & ( 0x80 >> f1 ) )
2463 if ( e1 >= 0 && e1 < ras.bWidth )
2465 c1 = (Short)( e1 >> 3 );
2466 f1 = (Short)( e1 & 7 );
2468 if ( ras.gray_min_x > c1 )
2469 ras.gray_min_x = c1;
2470 if ( ras.gray_max_x < c1 )
2471 ras.gray_max_x = c1;
2473 ras.bTarget[ras.traceOfs + c1] |= (char)( 0x80 >> f1 );
2479 Vertical_Sweep_Step( RAS_ARG )
2481 ras.traceOfs += ras.traceIncr;
2485 /***********************************************************************/
2487 /* Horizontal Sweep Procedure Set */
2489 /* These four routines are used during the horizontal black/white */
2490 /* sweep phase by the generic Draw_Sweep() function. */
2492 /***********************************************************************/
2495 Horizontal_Sweep_Init( RAS_ARGS Short* min,
2498 /* nothing, really */
2506 Horizontal_Sweep_Span( RAS_ARGS Short y,
2520 if ( x2 - x1 < ras.precision )
2527 bits = ras.bTarget + ( y >> 3 );
2528 f1 = (Byte)( 0x80 >> ( y & 7 ) );
2532 if ( e1 >= 0 && e1 < ras.target.rows )
2537 p = bits - e1 * ras.target.pitch;
2538 if ( ras.target.pitch > 0 )
2539 p += ( ras.target.rows - 1 ) * ras.target.pitch;
2549 Horizontal_Sweep_Drop( RAS_ARGS Short y,
2560 /* During the horizontal sweep, we only take care of drop-outs */
2562 /* e1 + <-- pixel center */
2564 /* x1 ---+--> <-- contour */
2567 /* x2 <--+--- <-- contour */
2570 /* e2 + <-- pixel center */
2578 Int dropOutControl = left->flags & 7;
2581 if ( e1 == e2 + ras.precision )
2583 switch ( dropOutControl )
2585 case 0: /* simple drop-outs including stubs */
2589 case 4: /* smart drop-outs including stubs */
2590 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2593 case 1: /* simple drop-outs excluding stubs */
2594 case 5: /* smart drop-outs excluding stubs */
2595 /* see Vertical_Sweep_Drop for details */
2597 /* rightmost stub test */
2598 if ( left->next == right &&
2599 left->height <= 0 &&
2600 !( left->flags & Overshoot_Top &&
2601 x2 - x1 >= ras.precision_half ) )
2604 /* leftmost stub test */
2605 if ( right->next == left &&
2607 !( left->flags & Overshoot_Bottom &&
2608 x2 - x1 >= ras.precision_half ) )
2611 if ( dropOutControl == 1 )
2614 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2617 default: /* modes 2, 3, 6, 7 */
2618 return; /* no drop-out control */
2621 /* undocumented but confirmed: If the drop-out would result in a */
2622 /* pixel outside of the bounding box, use the pixel inside of the */
2623 /* bounding box instead */
2626 else if ( TRUNC( pxl ) >= ras.target.rows )
2629 /* check that the other pixel isn't set */
2630 e1 = pxl == e1 ? e2 : e1;
2634 bits = ras.bTarget + ( y >> 3 );
2635 f1 = (Byte)( 0x80 >> ( y & 7 ) );
2637 bits -= e1 * ras.target.pitch;
2638 if ( ras.target.pitch > 0 )
2639 bits += ( ras.target.rows - 1 ) * ras.target.pitch;
2642 e1 < ras.target.rows &&
2650 bits = ras.bTarget + ( y >> 3 );
2651 f1 = (Byte)( 0x80 >> ( y & 7 ) );
2655 if ( e1 >= 0 && e1 < ras.target.rows )
2657 bits -= e1 * ras.target.pitch;
2658 if ( ras.target.pitch > 0 )
2659 bits += ( ras.target.rows - 1 ) * ras.target.pitch;
2667 Horizontal_Sweep_Step( RAS_ARG )
2669 /* Nothing, really */
2674 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
2677 /*************************************************************************/
2679 /* Vertical Gray Sweep Procedure Set */
2681 /* These two routines are used during the vertical gray-levels sweep */
2682 /* phase by the generic Draw_Sweep() function. */
2686 /* - The target pixmap's width *must* be a multiple of 4. */
2688 /* - You have to use the function Vertical_Sweep_Span() for the gray */
2691 /*************************************************************************/
2694 Vertical_Gray_Sweep_Init( RAS_ARGS Short* min,
2697 Long pitch, byte_len;
2701 *max = ( *max + 3 ) & -2;
2704 pitch = ras.target.pitch;
2706 ras.traceIncr = (Short)byte_len;
2707 ras.traceG = ( *min / 2 ) * byte_len;
2711 ras.traceG += ( ras.target.rows - 1 ) * pitch;
2712 byte_len = -byte_len;
2715 ras.gray_min_x = (Short)byte_len;
2716 ras.gray_max_x = -(Short)byte_len;
2721 Vertical_Gray_Sweep_Step( RAS_ARG )
2724 PByte pix, bit, bit2;
2725 short* count = (short*)count_table;
2729 ras.traceOfs += ras.gray_width;
2731 if ( ras.traceOfs > ras.gray_width )
2733 pix = ras.gTarget + ras.traceG + ras.gray_min_x * 4;
2736 if ( ras.gray_max_x >= 0 )
2738 Long last_pixel = ras.target.width - 1;
2739 Int last_cell = last_pixel >> 2;
2740 Int last_bit = last_pixel & 3;
2744 if ( ras.gray_max_x >= last_cell && last_bit != 3 )
2746 ras.gray_max_x = last_cell - 1;
2750 if ( ras.gray_min_x < 0 )
2753 bit = ras.bTarget + ras.gray_min_x;
2754 bit2 = bit + ras.gray_width;
2756 c1 = ras.gray_max_x - ras.gray_min_x;
2760 c2 = count[*bit] + count[*bit2];
2764 pix[0] = grays[(c2 >> 12) & 0x000F];
2765 pix[1] = grays[(c2 >> 8 ) & 0x000F];
2766 pix[2] = grays[(c2 >> 4 ) & 0x000F];
2767 pix[3] = grays[ c2 & 0x000F];
2781 c2 = count[*bit] + count[*bit2];
2787 pix[2] = grays[(c2 >> 4 ) & 0x000F];
2789 pix[1] = grays[(c2 >> 8 ) & 0x000F];
2791 pix[0] = grays[(c2 >> 12) & 0x000F];
2801 ras.traceG += ras.traceIncr;
2803 ras.gray_min_x = 32000;
2804 ras.gray_max_x = -32000;
2810 Horizontal_Gray_Sweep_Span( RAS_ARGS Short y,
2816 /* nothing, really */
2827 Horizontal_Gray_Sweep_Drop( RAS_ARGS Short y,
2838 /* During the horizontal sweep, we only take care of drop-outs */
2845 Int dropOutControl = left->flags & 7;
2848 if ( e1 == e2 + ras.precision )
2850 switch ( dropOutControl )
2852 case 0: /* simple drop-outs including stubs */
2856 case 4: /* smart drop-outs including stubs */
2857 e1 = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2860 case 1: /* simple drop-outs excluding stubs */
2861 case 5: /* smart drop-outs excluding stubs */
2862 /* see Vertical_Sweep_Drop for details */
2864 /* rightmost stub test */
2865 if ( left->next == right && left->height <= 0 )
2868 /* leftmost stub test */
2869 if ( right->next == left && left->start == y )
2872 if ( dropOutControl == 1 )
2875 e1 = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2879 default: /* modes 2, 3, 6, 7 */
2880 return; /* no drop-out control */
2889 if ( x2 - x1 >= ras.precision_half )
2890 color = ras.grays[2];
2892 color = ras.grays[1];
2894 e1 = TRUNC( e1 ) / 2;
2895 if ( e1 < ras.target.rows )
2897 pixel = ras.gTarget - e1 * ras.target.pitch + y / 2;
2898 if ( ras.target.pitch > 0 )
2899 pixel += ( ras.target.rows - 1 ) * ras.target.pitch;
2901 if ( pixel[0] == ras.grays[0] )
2908 #endif /* FT_RASTER_OPTION_ANTI_ALIASING */
2911 /*************************************************************************/
2913 /* Generic Sweep Drawing routine */
2915 /*************************************************************************/
2918 Draw_Sweep( RAS_ARG )
2920 Short y, y_change, y_height;
2922 PProfile P, Q, P_Left, P_Right;
2924 Short min_Y, max_Y, top, bottom, dropouts;
2926 Long x1, x2, xs, e1, e2;
2928 TProfileList waiting;
2929 TProfileList draw_left, draw_right;
2932 /* initialize empty linked lists */
2934 Init_Linked( &waiting );
2936 Init_Linked( &draw_left );
2937 Init_Linked( &draw_right );
2939 /* first, compute min and max Y */
2942 max_Y = (Short)TRUNC( ras.minY );
2943 min_Y = (Short)TRUNC( ras.maxY );
2949 bottom = (Short)P->start;
2950 top = (Short)( P->start + P->height - 1 );
2952 if ( min_Y > bottom )
2958 InsNew( &waiting, P );
2963 /* check the Y-turns */
2964 if ( ras.numTurns == 0 )
2966 ras.error = Raster_Err_Invalid;
2970 /* now initialize the sweep */
2972 ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y );
2974 /* then compute the distance of each profile from min_Y */
2980 P->countL = (UShort)( P->start - min_Y );
2989 if ( ras.numTurns > 0 &&
2990 ras.sizeBuff[-ras.numTurns] == min_Y )
2993 while ( ras.numTurns > 0 )
2995 /* check waiting list for new activations */
3002 P->countL -= y_height;
3003 if ( P->countL == 0 )
3005 DelOld( &waiting, P );
3007 if ( P->flags & Flow_Up )
3008 InsNew( &draw_left, P );
3010 InsNew( &draw_right, P );
3016 /* sort the drawing lists */
3019 Sort( &draw_right );
3021 y_change = (Short)ras.sizeBuff[-ras.numTurns--];
3022 y_height = (Short)( y_change - y );
3024 while ( y < y_change )
3031 P_Right = draw_right;
3048 if ( x2 - x1 <= ras.precision &&
3049 e1 != x1 && e2 != x2 )
3051 if ( e1 > e2 || e2 == e1 + ras.precision )
3053 Int dropOutControl = P_Left->flags & 7;
3056 if ( dropOutControl != 2 )
3058 /* a drop-out was detected */
3063 /* mark profile for drop-out processing */
3072 ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right );
3076 P_Left = P_Left->link;
3077 P_Right = P_Right->link;
3080 /* handle drop-outs _after_ the span drawing -- */
3081 /* drop-out processing has been moved out of the loop */
3082 /* for performance tuning */
3088 ras.Proc_Sweep_Step( RAS_VAR );
3095 Sort( &draw_right );
3099 /* now finalize the profiles that need it */
3105 if ( P->height == 0 )
3106 DelOld( &draw_left, P );
3114 if ( P->height == 0 )
3115 DelOld( &draw_right, P );
3120 /* for gray-scaling, flush the bitmap scanline cache */
3121 while ( y <= max_Y )
3123 ras.Proc_Sweep_Step( RAS_VAR );
3132 P_Right = draw_right;
3136 if ( P_Left->countL )
3140 dropouts--; /* -- this is useful when debugging only */
3142 ras.Proc_Sweep_Drop( RAS_VARS y,
3149 P_Left = P_Left->link;
3150 P_Right = P_Right->link;
3157 /*************************************************************************/
3160 /* Render_Single_Pass */
3163 /* Perform one sweep with sub-banding. */
3166 /* flipped :: If set, flip the direction of the outline. */
3169 /* Renderer error code. */
3172 Render_Single_Pass( RAS_ARGS Bool flipped )
3177 while ( ras.band_top >= 0 )
3179 ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision;
3180 ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision;
3184 ras.error = Raster_Err_None;
3186 if ( Convert_Glyph( RAS_VARS flipped ) )
3188 if ( ras.error != Raster_Err_Overflow )
3191 ras.error = Raster_Err_None;
3196 ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) );
3199 i = ras.band_stack[ras.band_top].y_min;
3200 j = ras.band_stack[ras.band_top].y_max;
3202 k = (Short)( ( i + j ) / 2 );
3204 if ( ras.band_top >= 7 || k < i )
3207 ras.error = Raster_Err_Invalid;
3212 ras.band_stack[ras.band_top + 1].y_min = k;
3213 ras.band_stack[ras.band_top + 1].y_max = j;
3215 ras.band_stack[ras.band_top].y_max = (Short)( k - 1 );
3222 if ( Draw_Sweep( RAS_VAR ) )
3232 /*************************************************************************/
3238 /* Render a glyph in a bitmap. Sub-banding if needed. */
3241 /* FreeType error code. 0 means success. */
3243 FT_LOCAL_DEF( FT_Error )
3244 Render_Glyph( RAS_ARG )
3249 Set_High_Precision( RAS_VARS ras.outline.flags &
3250 FT_OUTLINE_HIGH_PRECISION );
3251 ras.scale_shift = ras.precision_shift;
3253 if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )
3254 ras.dropOutControl = 2;
3257 if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS )
3258 ras.dropOutControl = 4;
3260 ras.dropOutControl = 0;
3262 if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) )
3263 ras.dropOutControl += 1;
3266 ras.second_pass = (FT_Byte)( !( ras.outline.flags &
3267 FT_OUTLINE_SINGLE_PASS ) );
3269 /* Vertical Sweep */
3270 ras.Proc_Sweep_Init = Vertical_Sweep_Init;
3271 ras.Proc_Sweep_Span = Vertical_Sweep_Span;
3272 ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
3273 ras.Proc_Sweep_Step = Vertical_Sweep_Step;
3276 ras.band_stack[0].y_min = 0;
3277 ras.band_stack[0].y_max = (short)( ras.target.rows - 1 );
3279 ras.bWidth = (unsigned short)ras.target.width;
3280 ras.bTarget = (Byte*)ras.target.buffer;
3282 if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 )
3285 /* Horizontal Sweep */
3286 if ( ras.second_pass && ras.dropOutControl != 2 )
3288 ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
3289 ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
3290 ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;
3291 ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
3294 ras.band_stack[0].y_min = 0;
3295 ras.band_stack[0].y_max = (short)( ras.target.width - 1 );
3297 if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 )
3301 return Raster_Err_None;
3305 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3307 /*************************************************************************/
3310 /* Render_Gray_Glyph */
3313 /* Render a glyph with grayscaling. Sub-banding if needed. */
3316 /* FreeType error code. 0 means success. */
3318 FT_LOCAL_DEF( FT_Error )
3319 Render_Gray_Glyph( RAS_ARG )
3325 Set_High_Precision( RAS_VARS ras.outline.flags &
3326 FT_OUTLINE_HIGH_PRECISION );
3327 ras.scale_shift = ras.precision_shift + 1;
3329 if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )
3330 ras.dropOutControl = 2;
3333 if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS )
3334 ras.dropOutControl = 4;
3336 ras.dropOutControl = 0;
3338 if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) )
3339 ras.dropOutControl += 1;
3342 ras.second_pass = !( ras.outline.flags & FT_OUTLINE_SINGLE_PASS );
3344 /* Vertical Sweep */
3347 ras.band_stack[0].y_min = 0;
3348 ras.band_stack[0].y_max = 2 * ras.target.rows - 1;
3350 ras.bWidth = ras.gray_width;
3351 pixel_width = 2 * ( ( ras.target.width + 3 ) >> 2 );
3353 if ( ras.bWidth > pixel_width )
3354 ras.bWidth = pixel_width;
3356 ras.bWidth = ras.bWidth * 8;
3357 ras.bTarget = (Byte*)ras.gray_lines;
3358 ras.gTarget = (Byte*)ras.target.buffer;
3360 ras.Proc_Sweep_Init = Vertical_Gray_Sweep_Init;
3361 ras.Proc_Sweep_Span = Vertical_Sweep_Span;
3362 ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
3363 ras.Proc_Sweep_Step = Vertical_Gray_Sweep_Step;
3365 error = Render_Single_Pass( RAS_VARS 0 );
3369 /* Horizontal Sweep */
3370 if ( ras.second_pass && ras.dropOutControl != 2 )
3372 ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
3373 ras.Proc_Sweep_Span = Horizontal_Gray_Sweep_Span;
3374 ras.Proc_Sweep_Drop = Horizontal_Gray_Sweep_Drop;
3375 ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
3378 ras.band_stack[0].y_min = 0;
3379 ras.band_stack[0].y_max = ras.target.width * 2 - 1;
3381 error = Render_Single_Pass( RAS_VARS 1 );
3386 return Raster_Err_None;
3389 #else /* !FT_RASTER_OPTION_ANTI_ALIASING */
3391 FT_LOCAL_DEF( FT_Error )
3392 Render_Gray_Glyph( RAS_ARG )
3396 return Raster_Err_Unsupported;
3399 #endif /* !FT_RASTER_OPTION_ANTI_ALIASING */
3403 ft_black_init( black_PRaster raster )
3405 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3409 /* set default 5-levels gray palette */
3410 for ( n = 0; n < 5; n++ )
3411 raster->grays[n] = n * 255 / 4;
3413 raster->gray_width = RASTER_GRAY_LINES / 2;
3415 FT_UNUSED( raster );
3420 /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
3421 /**** a static object. *****/
3428 ft_black_new( void* memory,
3429 FT_Raster *araster )
3431 static black_TRaster the_raster;
3432 FT_UNUSED( memory );
3435 *araster = (FT_Raster)&the_raster;
3436 FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) );
3437 ft_black_init( &the_raster );
3444 ft_black_done( FT_Raster raster )
3447 FT_UNUSED( raster );
3451 #else /* !_STANDALONE_ */
3455 ft_black_new( FT_Memory memory,
3456 black_PRaster *araster )
3459 black_PRaster raster = NULL;
3463 if ( !FT_NEW( raster ) )
3465 raster->memory = memory;
3466 ft_black_init( raster );
3476 ft_black_done( black_PRaster raster )
3478 FT_Memory memory = (FT_Memory)raster->memory;
3485 #endif /* !_STANDALONE_ */
3489 ft_black_reset( black_PRaster raster,
3495 if ( pool_base && pool_size >= (long)sizeof ( black_TWorker ) + 2048 )
3497 black_PWorker worker = (black_PWorker)pool_base;
3500 raster->buffer = pool_base + ( ( sizeof ( *worker ) + 7 ) & ~7 );
3501 raster->buffer_size = pool_base + pool_size - (char*)raster->buffer;
3502 raster->worker = worker;
3506 raster->buffer = NULL;
3507 raster->buffer_size = 0;
3508 raster->worker = NULL;
3515 ft_black_set_mode( black_PRaster raster,
3517 const char* palette )
3519 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3521 if ( mode == FT_MAKE_TAG( 'p', 'a', 'l', '5' ) )
3523 /* set 5-levels gray palette */
3524 raster->grays[0] = palette[0];
3525 raster->grays[1] = palette[1];
3526 raster->grays[2] = palette[2];
3527 raster->grays[3] = palette[3];
3528 raster->grays[4] = palette[4];
3533 FT_UNUSED( raster );
3535 FT_UNUSED( palette );
3542 ft_black_render( black_PRaster raster,
3543 const FT_Raster_Params* params )
3545 const FT_Outline* outline = (const FT_Outline*)params->source;
3546 const FT_Bitmap* target_map = params->target;
3547 black_PWorker worker;
3550 if ( !raster || !raster->buffer || !raster->buffer_size )
3551 return Raster_Err_Not_Ini;
3554 return Raster_Err_Invalid;
3556 /* return immediately if the outline is empty */
3557 if ( outline->n_points == 0 || outline->n_contours <= 0 )
3558 return Raster_Err_None;
3560 if ( !outline->contours || !outline->points )
3561 return Raster_Err_Invalid;
3563 if ( outline->n_points !=
3564 outline->contours[outline->n_contours - 1] + 1 )
3565 return Raster_Err_Invalid;
3567 worker = raster->worker;
3569 /* this version of the raster does not support direct rendering, sorry */
3570 if ( params->flags & FT_RASTER_FLAG_DIRECT )
3571 return Raster_Err_Unsupported;
3574 return Raster_Err_Invalid;
3577 if ( !target_map->width || !target_map->rows )
3578 return Raster_Err_None;
3580 if ( !target_map->buffer )
3581 return Raster_Err_Invalid;
3583 ras.outline = *outline;
3584 ras.target = *target_map;
3586 worker->buff = (PLong) raster->buffer;
3587 worker->sizeBuff = worker->buff +
3588 raster->buffer_size / sizeof ( Long );
3589 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3590 worker->grays = raster->grays;
3591 worker->gray_width = raster->gray_width;
3593 FT_MEM_ZERO( worker->gray_lines, worker->gray_width * 2 );
3596 return ( params->flags & FT_RASTER_FLAG_AA )
3597 ? Render_Gray_Glyph( RAS_VAR )
3598 : Render_Glyph( RAS_VAR );
3602 FT_DEFINE_RASTER_FUNCS( ft_standard_raster,
3603 FT_GLYPH_FORMAT_OUTLINE,
3604 (FT_Raster_New_Func) ft_black_new,
3605 (FT_Raster_Reset_Func) ft_black_reset,
3606 (FT_Raster_Set_Mode_Func)ft_black_set_mode,
3607 (FT_Raster_Render_Func) ft_black_render,
3608 (FT_Raster_Done_Func) ft_black_done