1 /***************************************************************************/
5 /* The FreeType glyph rasterizer (body). */
7 /* Copyright 1996-2003, 2005, 2007-2014 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/ftimage.h' and `src/raster/ftmisc.h' to your current */
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 and FT_MulDiv_No_Round */
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
182 /* Auxiliary macros for token concatenation. */
183 #define FT_ERR_XCAT( x, y ) x ## y
184 #define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y )
186 /* This macro is used to indicate that a function parameter is unused. */
187 /* Its purpose is simply to reduce compiler warnings. Note also that */
188 /* simply defining it as `(void)x' doesn't avoid warnings with certain */
189 /* ANSI compilers (e.g. LCC). */
190 #define FT_UNUSED( x ) (x) = (x)
192 /* Disable the tracing mechanism for simplicity -- developers can */
193 /* activate it easily by redefining these macros. */
195 #define FT_ERROR( x ) do { } while ( 0 ) /* nothing */
199 #define FT_TRACE( x ) do { } while ( 0 ) /* nothing */
200 #define FT_TRACE1( x ) do { } while ( 0 ) /* nothing */
201 #define FT_TRACE6( x ) do { } while ( 0 ) /* nothing */
205 #define FT_THROW( e ) FT_ERR_CAT( Raster_Err_, e )
208 #define Raster_Err_None 0
209 #define Raster_Err_Not_Ini -1
210 #define Raster_Err_Overflow -2
211 #define Raster_Err_Neg_Height -3
212 #define Raster_Err_Invalid -4
213 #define Raster_Err_Unsupported -5
215 #define ft_memset memset
217 #define FT_DEFINE_RASTER_FUNCS( class_, glyph_format_, raster_new_, \
218 raster_reset_, raster_set_mode_, \
219 raster_render_, raster_done_ ) \
220 const FT_Raster_Funcs class_ = \
230 #else /* !_STANDALONE_ */
233 #include FT_INTERNAL_OBJECTS_H
234 #include FT_INTERNAL_DEBUG_H /* for FT_TRACE, FT_ERROR, and FT_THROW */
236 #include "rasterrs.h"
238 #define Raster_Err_None FT_Err_Ok
239 #define Raster_Err_Not_Ini Raster_Err_Raster_Uninitialized
240 #define Raster_Err_Overflow Raster_Err_Raster_Overflow
241 #define Raster_Err_Neg_Height Raster_Err_Raster_Negative_Height
242 #define Raster_Err_Invalid Raster_Err_Invalid_Outline
243 #define Raster_Err_Unsupported Raster_Err_Cannot_Render_Glyph
246 #endif /* !_STANDALONE_ */
250 #define FT_MEM_SET( d, s, c ) ft_memset( d, s, c )
254 #define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count )
257 /* FMulDiv means `Fast MulDiv'; it is used in case where `b' is */
258 /* typically a small value and the result of a*b is known to fit into */
260 #define FMulDiv( a, b, c ) ( (a) * (b) / (c) )
262 /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */
263 /* for clipping computations. It simply uses the FT_MulDiv() function */
264 /* defined in `ftcalc.h'. */
265 #define SMulDiv FT_MulDiv
266 #define SMulDiv_No_Round FT_MulDiv_No_Round
268 /* The rasterizer is a very general purpose component; please leave */
269 /* the following redefinitions there (you never know your target */
281 #define NULL (void*)0
293 #define MaxBezier 32 /* The maximum number of stacked Bezier curves. */
294 /* Setting this constant to more than 32 is a */
295 /* pure waste of space. */
297 #define Pixel_Bits 6 /* fractional bits of *input* coordinates */
300 /*************************************************************************/
301 /*************************************************************************/
303 /** SIMPLE TYPE DECLARATIONS **/
305 /*************************************************************************/
306 /*************************************************************************/
309 typedef unsigned int UInt;
311 typedef unsigned short UShort, *PUShort;
312 typedef long Long, *PLong;
313 typedef unsigned long ULong;
315 typedef unsigned char Byte, *PByte;
319 typedef union Alignment_
325 } Alignment, *PAlignment;
328 typedef struct TPoint_
336 /* values for the `flags' bit field */
338 #define Overshoot_Top 0x10
339 #define Overshoot_Bottom 0x20
342 /* States of each line, arc, and profile */
343 typedef enum TStates_
353 typedef struct TProfile_ TProfile;
354 typedef TProfile* PProfile;
358 FT_F26Dot6 X; /* current coordinate during sweep */
359 PProfile link; /* link to next profile (various purposes) */
360 PLong offset; /* start of profile's data in render pool */
361 unsigned flags; /* Bit 0-2: drop-out mode */
362 /* Bit 3: profile orientation (up/down) */
363 /* Bit 4: is top profile? */
364 /* Bit 5: is bottom profile? */
365 long height; /* profile's height in scanlines */
366 long start; /* profile's starting scanline */
368 unsigned countL; /* number of lines to step before this */
369 /* profile becomes drawable */
371 PProfile next; /* next profile in same contour, used */
372 /* during drop-out control */
375 typedef PProfile TProfileList;
376 typedef PProfile* PProfileList;
379 /* Simple record used to implement a stack of bands, required */
380 /* by the sub-banding mechanism */
381 typedef struct black_TBand_
383 Short y_min; /* band's minimum */
384 Short y_max; /* band's maximum */
389 #define AlignProfileSize \
390 ( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( long ) )
398 #ifdef FT_STATIC_RASTER
401 #define RAS_ARGS /* void */
402 #define RAS_ARG /* void */
404 #define RAS_VARS /* void */
405 #define RAS_VAR /* void */
407 #define FT_UNUSED_RASTER do { } while ( 0 )
410 #else /* !FT_STATIC_RASTER */
413 #define RAS_ARGS black_PWorker worker,
414 #define RAS_ARG black_PWorker worker
416 #define RAS_VARS worker,
417 #define RAS_VAR worker
419 #define FT_UNUSED_RASTER FT_UNUSED( worker )
422 #endif /* !FT_STATIC_RASTER */
425 typedef struct black_TWorker_ black_TWorker, *black_PWorker;
428 /* prototypes used for sweep function dispatch */
430 Function_Sweep_Init( RAS_ARGS Short* min,
434 Function_Sweep_Span( RAS_ARGS Short y,
441 Function_Sweep_Step( RAS_ARG );
444 /* NOTE: These operations are only valid on 2's complement processors */
450 #define FLOOR( x ) ( (x) & -ras.precision )
451 #define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision )
452 #define TRUNC( x ) ( (Long)(x) >> ras.precision_bits )
453 #define FRAC( x ) ( (x) & ( ras.precision - 1 ) )
454 #define SCALED( x ) ( ( (ULong)(x) << ras.scale_shift ) - ras.precision_half )
456 #define IS_BOTTOM_OVERSHOOT( x ) \
457 (Bool)( CEILING( x ) - x >= ras.precision_half )
458 #define IS_TOP_OVERSHOOT( x ) \
459 (Bool)( x - FLOOR( x ) >= ras.precision_half )
461 /* The most used variables are positioned at the top of the structure. */
462 /* Thus, their offset can be coded with less opcodes, resulting in a */
463 /* smaller executable. */
465 struct black_TWorker_
467 Int precision_bits; /* precision related variables */
472 Int precision_jitter;
474 Int scale_shift; /* == precision_shift for bitmaps */
475 /* == precision_shift+1 for pixmaps */
477 PLong buff; /* The profiles buffer */
478 PLong sizeBuff; /* Render pool size */
479 PLong maxBuff; /* Profiles buffer size */
480 PLong top; /* Current cursor in buffer */
484 Int numTurns; /* number of Y-turns in outline */
486 TPoint* arc; /* current Bezier arc pointer */
488 UShort bWidth; /* target bitmap width */
489 PByte bTarget; /* target bitmap buffer */
490 PByte gTarget; /* target pixmap buffer */
495 UShort num_Profs; /* current number of profiles */
497 Bool fresh; /* signals a fresh new profile which */
498 /* `start' field must be completed */
499 Bool joint; /* signals that the last arc ended */
500 /* exactly on a scanline. Allows */
501 /* removal of doublets */
502 PProfile cProfile; /* current profile */
503 PProfile fProfile; /* head of linked list of profiles */
504 PProfile gProfile; /* contour's first profile in case */
507 TStates state; /* rendering state */
509 FT_Bitmap target; /* description of target bit/pixmap */
512 Long traceOfs; /* current offset in target bitmap */
513 Long traceG; /* current offset in target pixmap */
515 Short traceIncr; /* sweep's increment in target bitmap */
517 Short gray_min_x; /* current min x during gray rendering */
518 Short gray_max_x; /* current max x during gray rendering */
520 /* dispatch variables */
522 Function_Sweep_Init* Proc_Sweep_Init;
523 Function_Sweep_Span* Proc_Sweep_Span;
524 Function_Sweep_Span* Proc_Sweep_Drop;
525 Function_Sweep_Step* Proc_Sweep_Step;
527 Byte dropOutControl; /* current drop_out control method */
529 Bool second_pass; /* indicates whether a horizontal pass */
530 /* should be performed to control */
531 /* drop-out accurately when calling */
532 /* Render_Glyph. Note that there is */
533 /* no horizontal pass during gray */
536 TPoint arcs[3 * MaxBezier + 1]; /* The Bezier stack */
538 black_TBand band_stack[16]; /* band stack used for sub-banding */
539 Int band_top; /* band stack top */
541 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
545 Byte gray_lines[RASTER_GRAY_LINES];
546 /* Intermediate table used to render the */
547 /* graylevels pixmaps. */
548 /* gray_lines is a buffer holding two */
549 /* monochrome scanlines */
551 Short gray_width; /* width in bytes of one monochrome */
552 /* intermediate scanline of gray_lines. */
553 /* Each gray pixel takes 2 bits long there */
555 /* The gray_lines must hold 2 lines, thus with size */
556 /* in bytes of at least `gray_width*2'. */
558 #endif /* FT_RASTER_ANTI_ALIASING */
563 typedef struct black_TRaster_
568 black_PWorker worker;
572 } black_TRaster, *black_PRaster;
574 #ifdef FT_STATIC_RASTER
576 static black_TWorker cur_ras;
579 #else /* !FT_STATIC_RASTER */
581 #define ras (*worker)
583 #endif /* !FT_STATIC_RASTER */
586 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
588 /* A lookup table used to quickly count set bits in four gray 2x2 */
589 /* cells. The values of the table have been produced with the */
590 /* following code: */
592 /* for ( i = 0; i < 256; i++ ) */
597 /* for ( c = 0; c < 4; c++ ) */
601 /* if ( j & 0x80 ) l++; */
602 /* if ( j & 0x40 ) l++; */
604 /* j = ( j << 2 ) & 0xFF; */
606 /* printf( "0x%04X", l ); */
610 static const short count_table[256] =
612 0x0000, 0x0001, 0x0001, 0x0002, 0x0010, 0x0011, 0x0011, 0x0012,
613 0x0010, 0x0011, 0x0011, 0x0012, 0x0020, 0x0021, 0x0021, 0x0022,
614 0x0100, 0x0101, 0x0101, 0x0102, 0x0110, 0x0111, 0x0111, 0x0112,
615 0x0110, 0x0111, 0x0111, 0x0112, 0x0120, 0x0121, 0x0121, 0x0122,
616 0x0100, 0x0101, 0x0101, 0x0102, 0x0110, 0x0111, 0x0111, 0x0112,
617 0x0110, 0x0111, 0x0111, 0x0112, 0x0120, 0x0121, 0x0121, 0x0122,
618 0x0200, 0x0201, 0x0201, 0x0202, 0x0210, 0x0211, 0x0211, 0x0212,
619 0x0210, 0x0211, 0x0211, 0x0212, 0x0220, 0x0221, 0x0221, 0x0222,
620 0x1000, 0x1001, 0x1001, 0x1002, 0x1010, 0x1011, 0x1011, 0x1012,
621 0x1010, 0x1011, 0x1011, 0x1012, 0x1020, 0x1021, 0x1021, 0x1022,
622 0x1100, 0x1101, 0x1101, 0x1102, 0x1110, 0x1111, 0x1111, 0x1112,
623 0x1110, 0x1111, 0x1111, 0x1112, 0x1120, 0x1121, 0x1121, 0x1122,
624 0x1100, 0x1101, 0x1101, 0x1102, 0x1110, 0x1111, 0x1111, 0x1112,
625 0x1110, 0x1111, 0x1111, 0x1112, 0x1120, 0x1121, 0x1121, 0x1122,
626 0x1200, 0x1201, 0x1201, 0x1202, 0x1210, 0x1211, 0x1211, 0x1212,
627 0x1210, 0x1211, 0x1211, 0x1212, 0x1220, 0x1221, 0x1221, 0x1222,
628 0x1000, 0x1001, 0x1001, 0x1002, 0x1010, 0x1011, 0x1011, 0x1012,
629 0x1010, 0x1011, 0x1011, 0x1012, 0x1020, 0x1021, 0x1021, 0x1022,
630 0x1100, 0x1101, 0x1101, 0x1102, 0x1110, 0x1111, 0x1111, 0x1112,
631 0x1110, 0x1111, 0x1111, 0x1112, 0x1120, 0x1121, 0x1121, 0x1122,
632 0x1100, 0x1101, 0x1101, 0x1102, 0x1110, 0x1111, 0x1111, 0x1112,
633 0x1110, 0x1111, 0x1111, 0x1112, 0x1120, 0x1121, 0x1121, 0x1122,
634 0x1200, 0x1201, 0x1201, 0x1202, 0x1210, 0x1211, 0x1211, 0x1212,
635 0x1210, 0x1211, 0x1211, 0x1212, 0x1220, 0x1221, 0x1221, 0x1222,
636 0x2000, 0x2001, 0x2001, 0x2002, 0x2010, 0x2011, 0x2011, 0x2012,
637 0x2010, 0x2011, 0x2011, 0x2012, 0x2020, 0x2021, 0x2021, 0x2022,
638 0x2100, 0x2101, 0x2101, 0x2102, 0x2110, 0x2111, 0x2111, 0x2112,
639 0x2110, 0x2111, 0x2111, 0x2112, 0x2120, 0x2121, 0x2121, 0x2122,
640 0x2100, 0x2101, 0x2101, 0x2102, 0x2110, 0x2111, 0x2111, 0x2112,
641 0x2110, 0x2111, 0x2111, 0x2112, 0x2120, 0x2121, 0x2121, 0x2122,
642 0x2200, 0x2201, 0x2201, 0x2202, 0x2210, 0x2211, 0x2211, 0x2212,
643 0x2210, 0x2211, 0x2211, 0x2212, 0x2220, 0x2221, 0x2221, 0x2222
646 #endif /* FT_RASTER_OPTION_ANTI_ALIASING */
650 /*************************************************************************/
651 /*************************************************************************/
653 /** PROFILES COMPUTATION **/
655 /*************************************************************************/
656 /*************************************************************************/
659 /*************************************************************************/
662 /* Set_High_Precision */
665 /* Set precision variables according to param flag. */
668 /* High :: Set to True for high precision (typically for ppem < 24), */
669 /* false otherwise. */
672 Set_High_Precision( RAS_ARGS Int High )
675 * `precision_step' is used in `Bezier_Up' to decide when to split a
676 * given y-monotonous Bezier arc that crosses a scanline before
677 * approximating it as a straight segment. The default value of 32 (for
678 * low accuracy) corresponds to
680 * 32 / 64 == 0.5 pixels ,
682 * while for the high accuracy case we have
684 * 256/ (1 << 12) = 0.0625 pixels .
686 * `precision_jitter' is an epsilon threshold used in
687 * `Vertical_Sweep_Span' to deal with small imperfections in the Bezier
688 * decomposition (after all, we are working with approximations only);
689 * it avoids switching on additional pixels which would cause artifacts
692 * The value of `precision_jitter' has been determined heuristically.
698 ras.precision_bits = 12;
699 ras.precision_step = 256;
700 ras.precision_jitter = 30;
704 ras.precision_bits = 6;
705 ras.precision_step = 32;
706 ras.precision_jitter = 2;
709 FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" ));
711 ras.precision = 1 << ras.precision_bits;
712 ras.precision_half = ras.precision / 2;
713 ras.precision_shift = ras.precision_bits - Pixel_Bits;
717 /*************************************************************************/
723 /* Create a new profile in the render pool. */
726 /* aState :: The state/orientation of the new profile. */
728 /* overshoot :: Whether the profile's unrounded start position */
729 /* differs by at least a half pixel. */
732 /* SUCCESS on success. FAILURE in case of overflow or of incoherent */
736 New_Profile( RAS_ARGS TStates aState,
741 ras.cProfile = (PProfile)ras.top;
742 ras.fProfile = ras.cProfile;
743 ras.top += AlignProfileSize;
746 if ( ras.top >= ras.maxBuff )
748 ras.error = FT_THROW( Overflow );
752 ras.cProfile->flags = 0;
753 ras.cProfile->start = 0;
754 ras.cProfile->height = 0;
755 ras.cProfile->offset = ras.top;
756 ras.cProfile->link = (PProfile)0;
757 ras.cProfile->next = (PProfile)0;
758 ras.cProfile->flags = ras.dropOutControl;
762 case Ascending_State:
763 ras.cProfile->flags |= Flow_Up;
765 ras.cProfile->flags |= Overshoot_Bottom;
767 FT_TRACE6(( "New ascending profile = %p\n", ras.cProfile ));
770 case Descending_State:
772 ras.cProfile->flags |= Overshoot_Top;
773 FT_TRACE6(( "New descending profile = %p\n", ras.cProfile ));
777 FT_ERROR(( "New_Profile: invalid profile direction\n" ));
778 ras.error = FT_THROW( Invalid );
783 ras.gProfile = ras.cProfile;
793 /*************************************************************************/
799 /* Finalize the current profile. */
802 /* overshoot :: Whether the profile's unrounded end position differs */
803 /* by at least a half pixel. */
806 /* SUCCESS on success. FAILURE in case of overflow or incoherency. */
809 End_Profile( RAS_ARGS Bool overshoot )
814 h = (Long)( ras.top - ras.cProfile->offset );
818 FT_ERROR(( "End_Profile: negative height encountered\n" ));
819 ras.error = FT_THROW( Neg_Height );
828 FT_TRACE6(( "Ending profile %p, start = %ld, height = %ld\n",
829 ras.cProfile, ras.cProfile->start, h ));
831 ras.cProfile->height = h;
834 if ( ras.cProfile->flags & Flow_Up )
835 ras.cProfile->flags |= Overshoot_Top;
837 ras.cProfile->flags |= Overshoot_Bottom;
840 oldProfile = ras.cProfile;
841 ras.cProfile = (PProfile)ras.top;
843 ras.top += AlignProfileSize;
845 ras.cProfile->height = 0;
846 ras.cProfile->offset = ras.top;
848 oldProfile->next = ras.cProfile;
852 if ( ras.top >= ras.maxBuff )
854 FT_TRACE1(( "overflow in End_Profile\n" ));
855 ras.error = FT_THROW( Overflow );
865 /*************************************************************************/
871 /* Insert a salient into the sorted list placed on top of the render */
875 /* New y scanline position. */
878 /* SUCCESS on success. FAILURE in case of overflow. */
881 Insert_Y_Turn( RAS_ARGS Int y )
887 n = ras.numTurns - 1;
888 y_turns = ras.sizeBuff - ras.numTurns;
890 /* look for first y value that is <= */
891 while ( n >= 0 && y < y_turns[n] )
894 /* if it is <, simply insert it, ignore if == */
895 if ( n >= 0 && y > y_turns[n] )
898 Int y2 = (Int)y_turns[n];
909 if ( ras.maxBuff <= ras.top )
911 ras.error = FT_THROW( Overflow );
915 ras.sizeBuff[-ras.numTurns] = y;
922 /*************************************************************************/
925 /* Finalize_Profile_Table */
928 /* Adjust all links in the profiles list. */
931 /* SUCCESS on success. FAILURE in case of overflow. */
934 Finalize_Profile_Table( RAS_ARG )
951 p->link = (PProfile)( p->offset + p->height );
955 if ( p->flags & Flow_Up )
957 bottom = (Int)p->start;
958 top = (Int)( p->start + p->height - 1 );
962 bottom = (Int)( p->start - p->height + 1 );
965 p->offset += p->height - 1;
968 if ( Insert_Y_Turn( RAS_VARS bottom ) ||
969 Insert_Y_Turn( RAS_VARS top + 1 ) )
983 /*************************************************************************/
989 /* Subdivide one conic Bezier into two joint sub-arcs in the Bezier */
993 /* None (subdivided Bezier is taken from the top of the stack). */
996 /* This routine is the `beef' of this component. It is _the_ inner */
997 /* loop that should be optimized to hell to get the best performance. */
1000 Split_Conic( TPoint* base )
1005 base[4].x = base[2].x;
1007 a = base[3].x = ( base[2].x + b ) / 2;
1008 b = base[1].x = ( base[0].x + b ) / 2;
1009 base[2].x = ( a + b ) / 2;
1011 base[4].y = base[2].y;
1013 a = base[3].y = ( base[2].y + b ) / 2;
1014 b = base[1].y = ( base[0].y + b ) / 2;
1015 base[2].y = ( a + b ) / 2;
1017 /* hand optimized. gcc doesn't seem to be too good at common */
1018 /* expression substitution and instruction scheduling ;-) */
1022 /*************************************************************************/
1028 /* Subdivide a third-order Bezier arc into two joint sub-arcs in the */
1032 /* This routine is the `beef' of the component. It is one of _the_ */
1033 /* inner loops that should be optimized like hell to get the best */
1037 Split_Cubic( TPoint* base )
1042 base[6].x = base[3].x;
1045 base[1].x = a = ( base[0].x + c + 1 ) >> 1;
1046 base[5].x = b = ( base[3].x + d + 1 ) >> 1;
1047 c = ( c + d + 1 ) >> 1;
1048 base[2].x = a = ( a + c + 1 ) >> 1;
1049 base[4].x = b = ( b + c + 1 ) >> 1;
1050 base[3].x = ( a + b + 1 ) >> 1;
1052 base[6].y = base[3].y;
1055 base[1].y = a = ( base[0].y + c + 1 ) >> 1;
1056 base[5].y = b = ( base[3].y + d + 1 ) >> 1;
1057 c = ( c + d + 1 ) >> 1;
1058 base[2].y = a = ( a + c + 1 ) >> 1;
1059 base[4].y = b = ( b + c + 1 ) >> 1;
1060 base[3].y = ( a + b + 1 ) >> 1;
1064 /*************************************************************************/
1070 /* Compute the x-coordinates of an ascending line segment and store */
1071 /* them in the render pool. */
1074 /* x1 :: The x-coordinate of the segment's start point. */
1076 /* y1 :: The y-coordinate of the segment's start point. */
1078 /* x2 :: The x-coordinate of the segment's end point. */
1080 /* y2 :: The y-coordinate of the segment's end point. */
1082 /* miny :: A lower vertical clipping bound value. */
1084 /* maxy :: An upper vertical clipping bound value. */
1087 /* SUCCESS on success, FAILURE on render pool overflow. */
1090 Line_Up( RAS_ARGS Long x1,
1098 Int e1, e2, f1, f2, size; /* XXX: is `Short' sufficient? */
1107 if ( Dy <= 0 || y2 < miny || y1 > maxy )
1112 /* Take care: miny-y1 can be a very large value; we use */
1113 /* a slow MulDiv function to avoid clipping bugs */
1114 x1 += SMulDiv( Dx, miny - y1, Dy );
1115 e1 = (Int)TRUNC( miny );
1120 e1 = (Int)TRUNC( y1 );
1121 f1 = (Int)FRAC( y1 );
1126 /* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */
1127 e2 = (Int)TRUNC( maxy );
1132 e2 = (Int)TRUNC( y2 );
1133 f2 = (Int)FRAC( y2 );
1142 x1 += SMulDiv( Dx, ras.precision - f1, Dy );
1153 ras.joint = (char)( f2 == 0 );
1157 ras.cProfile->start = e1;
1162 if ( ras.top + size >= ras.maxBuff )
1164 ras.error = FT_THROW( Overflow );
1170 Ix = SMulDiv_No_Round( ras.precision, Dx, Dy );
1171 Rx = ( ras.precision * Dx ) % Dy;
1176 Ix = -SMulDiv_No_Round( ras.precision, -Dx, Dy );
1177 Rx = ( ras.precision * -Dx ) % Dy;
1203 /*************************************************************************/
1209 /* Compute the x-coordinates of an descending line segment and store */
1210 /* them in the render pool. */
1213 /* x1 :: The x-coordinate of the segment's start point. */
1215 /* y1 :: The y-coordinate of the segment's start point. */
1217 /* x2 :: The x-coordinate of the segment's end point. */
1219 /* y2 :: The y-coordinate of the segment's end point. */
1221 /* miny :: A lower vertical clipping bound value. */
1223 /* maxy :: An upper vertical clipping bound value. */
1226 /* SUCCESS on success, FAILURE on render pool overflow. */
1229 Line_Down( RAS_ARGS Long x1,
1241 result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );
1243 if ( fresh && !ras.fresh )
1244 ras.cProfile->start = -ras.cProfile->start;
1250 /* A function type describing the functions used to split Bezier arcs */
1251 typedef void (*TSplitter)( TPoint* base );
1254 /*************************************************************************/
1260 /* Compute the x-coordinates of an ascending Bezier arc and store */
1261 /* them in the render pool. */
1264 /* degree :: The degree of the Bezier arc (either 2 or 3). */
1266 /* splitter :: The function to split Bezier arcs. */
1268 /* miny :: A lower vertical clipping bound value. */
1270 /* maxy :: An upper vertical clipping bound value. */
1273 /* SUCCESS on success, FAILURE on render pool overflow. */
1276 Bezier_Up( RAS_ARGS Int degree,
1281 Long y1, y2, e, e2, e0;
1295 if ( y2 < miny || y1 > maxy )
1310 f1 = (Short)( FRAC( y1 ) );
1321 *top++ = arc[degree].x;
1329 ras.cProfile->start = TRUNC( e0 );
1336 if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff )
1339 ras.error = FT_THROW( Overflow );
1345 while ( arc >= start_arc && e <= e2 )
1354 if ( y2 - y1 >= ras.precision_step )
1361 *top++ = arc[degree].x + FMulDiv( arc[0].x - arc[degree].x,
1387 /*************************************************************************/
1393 /* Compute the x-coordinates of an descending Bezier arc and store */
1394 /* them in the render pool. */
1397 /* degree :: The degree of the Bezier arc (either 2 or 3). */
1399 /* splitter :: The function to split Bezier arcs. */
1401 /* miny :: A lower vertical clipping bound value. */
1403 /* maxy :: An upper vertical clipping bound value. */
1406 /* SUCCESS on success, FAILURE on render pool overflow. */
1409 Bezier_Down( RAS_ARGS Int degree,
1414 TPoint* arc = ras.arc;
1418 arc[0].y = -arc[0].y;
1419 arc[1].y = -arc[1].y;
1420 arc[2].y = -arc[2].y;
1422 arc[3].y = -arc[3].y;
1426 result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny );
1428 if ( fresh && !ras.fresh )
1429 ras.cProfile->start = -ras.cProfile->start;
1431 arc[0].y = -arc[0].y;
1436 /*************************************************************************/
1442 /* Inject a new line segment and adjust the Profiles list. */
1445 /* x :: The x-coordinate of the segment's end point (its start point */
1446 /* is stored in `lastX'). */
1448 /* y :: The y-coordinate of the segment's end point (its start point */
1449 /* is stored in `lastY'). */
1452 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1456 Line_To( RAS_ARGS Long x,
1459 /* First, detect a change of direction */
1461 switch ( ras.state )
1464 if ( y > ras.lastY )
1466 if ( New_Profile( RAS_VARS Ascending_State,
1467 IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
1472 if ( y < ras.lastY )
1473 if ( New_Profile( RAS_VARS Descending_State,
1474 IS_TOP_OVERSHOOT( ras.lastY ) ) )
1479 case Ascending_State:
1480 if ( y < ras.lastY )
1482 if ( End_Profile( RAS_VARS IS_TOP_OVERSHOOT( ras.lastY ) ) ||
1483 New_Profile( RAS_VARS Descending_State,
1484 IS_TOP_OVERSHOOT( ras.lastY ) ) )
1489 case Descending_State:
1490 if ( y > ras.lastY )
1492 if ( End_Profile( RAS_VARS IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ||
1493 New_Profile( RAS_VARS Ascending_State,
1494 IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
1503 /* Then compute the lines */
1505 switch ( ras.state )
1507 case Ascending_State:
1508 if ( Line_Up( RAS_VARS ras.lastX, ras.lastY,
1509 x, y, ras.minY, ras.maxY ) )
1513 case Descending_State:
1514 if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
1515 x, y, ras.minY, ras.maxY ) )
1530 /*************************************************************************/
1536 /* Inject a new conic arc and adjust the profile list. */
1539 /* cx :: The x-coordinate of the arc's new control point. */
1541 /* cy :: The y-coordinate of the arc's new control point. */
1543 /* x :: The x-coordinate of the arc's end point (its start point is */
1544 /* stored in `lastX'). */
1546 /* y :: The y-coordinate of the arc's end point (its start point is */
1547 /* stored in `lastY'). */
1550 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1554 Conic_To( RAS_ARGS Long cx,
1559 Long y1, y2, y3, x3, ymin, ymax;
1564 ras.arc[2].x = ras.lastX;
1565 ras.arc[2].y = ras.lastY;
1578 /* first, categorize the Bezier arc */
1591 if ( y2 < ymin || y2 > ymax )
1593 /* this arc has no given direction, split it! */
1594 Split_Conic( ras.arc );
1597 else if ( y1 == y3 )
1599 /* this arc is flat, ignore it and pop it from the Bezier stack */
1604 /* the arc is y-monotonous, either ascending or descending */
1605 /* detect a change of direction */
1606 state_bez = y1 < y3 ? Ascending_State : Descending_State;
1607 if ( ras.state != state_bez )
1609 Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 )
1610 : IS_TOP_OVERSHOOT( y1 );
1613 /* finalize current profile if any */
1614 if ( ras.state != Unknown_State &&
1615 End_Profile( RAS_VARS o ) )
1618 /* create a new profile */
1619 if ( New_Profile( RAS_VARS state_bez, o ) )
1623 /* now call the appropriate routine */
1624 if ( state_bez == Ascending_State )
1626 if ( Bezier_Up( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
1630 if ( Bezier_Down( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
1634 } while ( ras.arc >= ras.arcs );
1646 /*************************************************************************/
1652 /* Inject a new cubic arc and adjust the profile list. */
1655 /* cx1 :: The x-coordinate of the arc's first new control point. */
1657 /* cy1 :: The y-coordinate of the arc's first new control point. */
1659 /* cx2 :: The x-coordinate of the arc's second new control point. */
1661 /* cy2 :: The y-coordinate of the arc's second new control point. */
1663 /* x :: The x-coordinate of the arc's end point (its start point is */
1664 /* stored in `lastX'). */
1666 /* y :: The y-coordinate of the arc's end point (its start point is */
1667 /* stored in `lastY'). */
1670 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1674 Cubic_To( RAS_ARGS Long cx1,
1681 Long y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
1686 ras.arc[3].x = ras.lastX;
1687 ras.arc[3].y = ras.lastY;
1703 /* first, categorize the Bezier arc */
1727 if ( ymin2 < ymin1 || ymax2 > ymax1 )
1729 /* this arc has no given direction, split it! */
1730 Split_Cubic( ras.arc );
1733 else if ( y1 == y4 )
1735 /* this arc is flat, ignore it and pop it from the Bezier stack */
1740 state_bez = ( y1 <= y4 ) ? Ascending_State : Descending_State;
1742 /* detect a change of direction */
1743 if ( ras.state != state_bez )
1745 Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 )
1746 : IS_TOP_OVERSHOOT( y1 );
1749 /* finalize current profile if any */
1750 if ( ras.state != Unknown_State &&
1751 End_Profile( RAS_VARS o ) )
1754 if ( New_Profile( RAS_VARS state_bez, o ) )
1758 /* compute intersections */
1759 if ( state_bez == Ascending_State )
1761 if ( Bezier_Up( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
1765 if ( Bezier_Down( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
1769 } while ( ras.arc >= ras.arcs );
1782 #define SWAP_( x, y ) do \
1792 /*************************************************************************/
1795 /* Decompose_Curve */
1798 /* Scan the outline arrays in order to emit individual segments and */
1799 /* Beziers by calling Line_To() and Bezier_To(). It handles all */
1800 /* weird cases, like when the first point is off the curve, or when */
1801 /* there are simply no `on' points in the contour! */
1804 /* first :: The index of the first point in the contour. */
1806 /* last :: The index of the last point in the contour. */
1808 /* flipped :: If set, flip the direction of the curve. */
1811 /* SUCCESS on success, FAILURE on error. */
1814 Decompose_Curve( RAS_ARGS UShort first,
1819 FT_Vector v_control;
1827 unsigned tag; /* current point's state */
1830 points = ras.outline.points;
1831 limit = points + last;
1833 v_start.x = SCALED( points[first].x );
1834 v_start.y = SCALED( points[first].y );
1835 v_last.x = SCALED( points[last].x );
1836 v_last.y = SCALED( points[last].y );
1840 SWAP_( v_start.x, v_start.y );
1841 SWAP_( v_last.x, v_last.y );
1844 v_control = v_start;
1846 point = points + first;
1847 tags = ras.outline.tags + first;
1849 /* set scan mode if necessary */
1850 if ( tags[0] & FT_CURVE_TAG_HAS_SCANMODE )
1851 ras.dropOutControl = (Byte)tags[0] >> 5;
1853 tag = FT_CURVE_TAG( tags[0] );
1855 /* A contour cannot start with a cubic control point! */
1856 if ( tag == FT_CURVE_TAG_CUBIC )
1857 goto Invalid_Outline;
1859 /* check first point to determine origin */
1860 if ( tag == FT_CURVE_TAG_CONIC )
1862 /* first point is conic control. Yes, this happens. */
1863 if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_CURVE_TAG_ON )
1865 /* start at last point if it is on the curve */
1871 /* if both first and last points are conic, */
1872 /* start at their middle and record its position */
1874 v_start.x = ( v_start.x + v_last.x ) / 2;
1875 v_start.y = ( v_start.y + v_last.y ) / 2;
1877 /* v_last = v_start; */
1883 ras.lastX = v_start.x;
1884 ras.lastY = v_start.y;
1886 while ( point < limit )
1891 tag = FT_CURVE_TAG( tags[0] );
1895 case FT_CURVE_TAG_ON: /* emit a single line_to */
1900 x = SCALED( point->x );
1901 y = SCALED( point->y );
1905 if ( Line_To( RAS_VARS x, y ) )
1910 case FT_CURVE_TAG_CONIC: /* consume conic arcs */
1911 v_control.x = SCALED( point[0].x );
1912 v_control.y = SCALED( point[0].y );
1915 SWAP_( v_control.x, v_control.y );
1918 if ( point < limit )
1926 tag = FT_CURVE_TAG( tags[0] );
1928 x = SCALED( point[0].x );
1929 y = SCALED( point[0].y );
1934 if ( tag == FT_CURVE_TAG_ON )
1936 if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) )
1941 if ( tag != FT_CURVE_TAG_CONIC )
1942 goto Invalid_Outline;
1944 v_middle.x = ( v_control.x + x ) / 2;
1945 v_middle.y = ( v_control.y + y ) / 2;
1947 if ( Conic_To( RAS_VARS v_control.x, v_control.y,
1948 v_middle.x, v_middle.y ) )
1957 if ( Conic_To( RAS_VARS v_control.x, v_control.y,
1958 v_start.x, v_start.y ) )
1963 default: /* FT_CURVE_TAG_CUBIC */
1965 Long x1, y1, x2, y2, x3, y3;
1968 if ( point + 1 > limit ||
1969 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
1970 goto Invalid_Outline;
1975 x1 = SCALED( point[-2].x );
1976 y1 = SCALED( point[-2].y );
1977 x2 = SCALED( point[-1].x );
1978 y2 = SCALED( point[-1].y );
1986 if ( point <= limit )
1988 x3 = SCALED( point[0].x );
1989 y3 = SCALED( point[0].y );
1994 if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) )
1999 if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) )
2006 /* close the contour with a line segment */
2007 if ( Line_To( RAS_VARS v_start.x, v_start.y ) )
2014 ras.error = FT_THROW( Invalid );
2021 /*************************************************************************/
2027 /* Convert a glyph into a series of segments and arcs and make a */
2028 /* profiles list with them. */
2031 /* flipped :: If set, flip the direction of curve. */
2034 /* SUCCESS on success, FAILURE if any error was encountered during */
2038 Convert_Glyph( RAS_ARGS int flipped )
2044 ras.fProfile = NULL;
2048 ras.maxBuff = ras.sizeBuff - AlignProfileSize;
2052 ras.cProfile = (PProfile)ras.top;
2053 ras.cProfile->offset = ras.top;
2058 for ( i = 0; i < ras.outline.n_contours; i++ )
2060 PProfile lastProfile;
2064 ras.state = Unknown_State;
2065 ras.gProfile = NULL;
2067 if ( Decompose_Curve( RAS_VARS (unsigned short)start,
2068 ras.outline.contours[i],
2072 start = ras.outline.contours[i] + 1;
2074 /* we must now check whether the extreme arcs join or not */
2075 if ( FRAC( ras.lastY ) == 0 &&
2076 ras.lastY >= ras.minY &&
2077 ras.lastY <= ras.maxY )
2078 if ( ras.gProfile &&
2079 ( ras.gProfile->flags & Flow_Up ) ==
2080 ( ras.cProfile->flags & Flow_Up ) )
2082 /* Note that ras.gProfile can be nil if the contour was too small */
2085 lastProfile = ras.cProfile;
2086 if ( ras.cProfile->flags & Flow_Up )
2087 o = IS_TOP_OVERSHOOT( ras.lastY );
2089 o = IS_BOTTOM_OVERSHOOT( ras.lastY );
2090 if ( End_Profile( RAS_VARS o ) )
2093 /* close the `next profile in contour' linked list */
2095 lastProfile->next = ras.gProfile;
2098 if ( Finalize_Profile_Table( RAS_VAR ) )
2101 return (Bool)( ras.top < ras.maxBuff ? SUCCESS : FAILURE );
2105 /*************************************************************************/
2106 /*************************************************************************/
2108 /** SCAN-LINE SWEEPS AND DRAWING **/
2110 /*************************************************************************/
2111 /*************************************************************************/
2114 /*************************************************************************/
2118 /* Initializes an empty linked list. */
2121 Init_Linked( TProfileList* l )
2127 /*************************************************************************/
2131 /* Inserts a new profile in a linked list. */
2134 InsNew( PProfileList list,
2137 PProfile *old, current;
2147 if ( x < current->X )
2149 old = ¤t->link;
2153 profile->link = current;
2158 /*************************************************************************/
2162 /* Removes an old profile from a linked list. */
2165 DelOld( PProfileList list,
2168 PProfile *old, current;
2176 if ( current == profile )
2178 *old = current->link;
2182 old = ¤t->link;
2186 /* we should never get there, unless the profile was not part of */
2191 /*************************************************************************/
2195 /* Sorts a trace list. In 95%, the list is already sorted. We need */
2196 /* an algorithm which is fast in this case. Bubble sort is enough */
2200 Sort( PProfileList list )
2202 PProfile *old, current, next;
2205 /* First, set the new X coordinate of each profile */
2209 current->X = *current->offset;
2210 current->offset += current->flags & Flow_Up ? 1 : -1;
2212 current = current->link;
2215 /* Then sort them */
2222 next = current->link;
2226 if ( current->X <= next->X )
2228 old = ¤t->link;
2237 current->link = next->link;
2238 next->link = current;
2244 next = current->link;
2249 /*************************************************************************/
2251 /* Vertical Sweep Procedure Set */
2253 /* These four routines are used during the vertical black/white sweep */
2254 /* phase by the generic Draw_Sweep() function. */
2256 /*************************************************************************/
2259 Vertical_Sweep_Init( RAS_ARGS Short* min,
2262 Long pitch = ras.target.pitch;
2267 ras.traceIncr = (Short)-pitch;
2268 ras.traceOfs = -*min * pitch;
2270 ras.traceOfs += ( ras.target.rows - 1 ) * pitch;
2278 Vertical_Sweep_Span( RAS_ARGS Short y,
2287 Int dropOutControl = left->flags & 7;
2294 /* Drop-out control */
2296 e1 = TRUNC( CEILING( x1 ) );
2298 if ( dropOutControl != 2 &&
2299 x2 - x1 - ras.precision <= ras.precision_jitter )
2302 e2 = TRUNC( FLOOR( x2 ) );
2304 if ( e2 >= 0 && e1 < ras.bWidth )
2312 if ( e2 >= ras.bWidth )
2313 e2 = ras.bWidth - 1;
2315 c1 = (Short)( e1 >> 3 );
2316 c2 = (Short)( e2 >> 3 );
2318 f1 = (Byte) ( 0xFF >> ( e1 & 7 ) );
2319 f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) );
2321 if ( ras.gray_min_x > c1 )
2322 ras.gray_min_x = (short)c1;
2323 if ( ras.gray_max_x < c2 )
2324 ras.gray_max_x = (short)c2;
2326 target = ras.bTarget + ras.traceOfs + c1;
2333 /* memset() is slower than the following code on many platforms. */
2334 /* This is due to the fact that, in the vast majority of cases, */
2335 /* the span length in bytes is relatively small. */
2345 *target |= ( f1 & f2 );
2351 Vertical_Sweep_Drop( RAS_ARGS Short y,
2361 /* Drop-out control */
2367 /* +-------------+---------------------+------------+ */
2371 /* pixel contour contour pixel */
2374 /* drop-out mode scan conversion rules (as defined in OpenType) */
2375 /* --------------------------------------------------------------- */
2379 /* 3 same as mode 2 */
2382 /* 6, 7 same as mode 2 */
2390 Int dropOutControl = left->flags & 7;
2393 if ( e1 == e2 + ras.precision )
2395 switch ( dropOutControl )
2397 case 0: /* simple drop-outs including stubs */
2401 case 4: /* smart drop-outs including stubs */
2402 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2405 case 1: /* simple drop-outs excluding stubs */
2406 case 5: /* smart drop-outs excluding stubs */
2408 /* Drop-out Control Rules #4 and #6 */
2410 /* The specification neither provides an exact definition */
2411 /* of a `stub' nor gives exact rules to exclude them. */
2413 /* Here the constraints we use to recognize a stub. */
2417 /* - P_Left and P_Right are in the same contour */
2418 /* - P_Right is the successor of P_Left in that contour */
2419 /* - y is the top of P_Left and P_Right */
2423 /* - P_Left and P_Right are in the same contour */
2424 /* - P_Left is the successor of P_Right in that contour */
2425 /* - y is the bottom of P_Left */
2427 /* We draw a stub if the following constraints are met. */
2429 /* - for an upper or lower stub, there is top or bottom */
2430 /* overshoot, respectively */
2431 /* - the covered interval is greater or equal to a half */
2434 /* upper stub test */
2435 if ( left->next == right &&
2436 left->height <= 0 &&
2437 !( left->flags & Overshoot_Top &&
2438 x2 - x1 >= ras.precision_half ) )
2441 /* lower stub test */
2442 if ( right->next == left &&
2444 !( left->flags & Overshoot_Bottom &&
2445 x2 - x1 >= ras.precision_half ) )
2448 if ( dropOutControl == 1 )
2451 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2454 default: /* modes 2, 3, 6, 7 */
2455 return; /* no drop-out control */
2458 /* undocumented but confirmed: If the drop-out would result in a */
2459 /* pixel outside of the bounding box, use the pixel inside of the */
2460 /* bounding box instead */
2463 else if ( TRUNC( pxl ) >= ras.bWidth )
2466 /* check that the other pixel isn't set */
2467 e1 = pxl == e1 ? e2 : e1;
2471 c1 = (Short)( e1 >> 3 );
2472 f1 = (Short)( e1 & 7 );
2474 if ( e1 >= 0 && e1 < ras.bWidth &&
2475 ras.bTarget[ras.traceOfs + c1] & ( 0x80 >> f1 ) )
2484 if ( e1 >= 0 && e1 < ras.bWidth )
2486 c1 = (Short)( e1 >> 3 );
2487 f1 = (Short)( e1 & 7 );
2489 if ( ras.gray_min_x > c1 )
2490 ras.gray_min_x = c1;
2491 if ( ras.gray_max_x < c1 )
2492 ras.gray_max_x = c1;
2494 ras.bTarget[ras.traceOfs + c1] |= (char)( 0x80 >> f1 );
2500 Vertical_Sweep_Step( RAS_ARG )
2502 ras.traceOfs += ras.traceIncr;
2506 /***********************************************************************/
2508 /* Horizontal Sweep Procedure Set */
2510 /* These four routines are used during the horizontal black/white */
2511 /* sweep phase by the generic Draw_Sweep() function. */
2513 /***********************************************************************/
2516 Horizontal_Sweep_Init( RAS_ARGS Short* min,
2519 /* nothing, really */
2527 Horizontal_Sweep_Span( RAS_ARGS Short y,
2537 if ( x2 - x1 < ras.precision )
2551 bits = ras.bTarget + ( y >> 3 );
2552 f1 = (Byte)( 0x80 >> ( y & 7 ) );
2556 if ( e1 >= 0 && (ULong)e1 < ras.target.rows )
2561 p = bits - e1 * ras.target.pitch;
2562 if ( ras.target.pitch > 0 )
2563 p += ( ras.target.rows - 1 ) * ras.target.pitch;
2573 Horizontal_Sweep_Drop( RAS_ARGS Short y,
2584 /* During the horizontal sweep, we only take care of drop-outs */
2586 /* e1 + <-- pixel center */
2588 /* x1 ---+--> <-- contour */
2591 /* x2 <--+--- <-- contour */
2594 /* e2 + <-- pixel center */
2602 Int dropOutControl = left->flags & 7;
2605 if ( e1 == e2 + ras.precision )
2607 switch ( dropOutControl )
2609 case 0: /* simple drop-outs including stubs */
2613 case 4: /* smart drop-outs including stubs */
2614 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2617 case 1: /* simple drop-outs excluding stubs */
2618 case 5: /* smart drop-outs excluding stubs */
2619 /* see Vertical_Sweep_Drop for details */
2621 /* rightmost stub test */
2622 if ( left->next == right &&
2623 left->height <= 0 &&
2624 !( left->flags & Overshoot_Top &&
2625 x2 - x1 >= ras.precision_half ) )
2628 /* leftmost stub test */
2629 if ( right->next == left &&
2631 !( left->flags & Overshoot_Bottom &&
2632 x2 - x1 >= ras.precision_half ) )
2635 if ( dropOutControl == 1 )
2638 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2641 default: /* modes 2, 3, 6, 7 */
2642 return; /* no drop-out control */
2645 /* undocumented but confirmed: If the drop-out would result in a */
2646 /* pixel outside of the bounding box, use the pixel inside of the */
2647 /* bounding box instead */
2650 else if ( (ULong)( TRUNC( pxl ) ) >= ras.target.rows )
2653 /* check that the other pixel isn't set */
2654 e1 = pxl == e1 ? e2 : e1;
2658 bits = ras.bTarget + ( y >> 3 );
2659 f1 = (Byte)( 0x80 >> ( y & 7 ) );
2661 bits -= e1 * ras.target.pitch;
2662 if ( ras.target.pitch > 0 )
2663 bits += ( ras.target.rows - 1 ) * ras.target.pitch;
2666 (ULong)e1 < ras.target.rows &&
2674 bits = ras.bTarget + ( y >> 3 );
2675 f1 = (Byte)( 0x80 >> ( y & 7 ) );
2679 if ( e1 >= 0 && (ULong)e1 < ras.target.rows )
2681 bits -= e1 * ras.target.pitch;
2682 if ( ras.target.pitch > 0 )
2683 bits += ( ras.target.rows - 1 ) * ras.target.pitch;
2691 Horizontal_Sweep_Step( RAS_ARG )
2693 /* Nothing, really */
2698 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
2701 /*************************************************************************/
2703 /* Vertical Gray Sweep Procedure Set */
2705 /* These two routines are used during the vertical gray-levels sweep */
2706 /* phase by the generic Draw_Sweep() function. */
2710 /* - The target pixmap's width *must* be a multiple of 4. */
2712 /* - You have to use the function Vertical_Sweep_Span() for the gray */
2715 /*************************************************************************/
2718 Vertical_Gray_Sweep_Init( RAS_ARGS Short* min,
2721 Long pitch, byte_len;
2725 *max = ( *max + 3 ) & -2;
2728 pitch = ras.target.pitch;
2730 ras.traceIncr = (Short)byte_len;
2731 ras.traceG = ( *min / 2 ) * byte_len;
2735 ras.traceG += ( ras.target.rows - 1 ) * pitch;
2736 byte_len = -byte_len;
2739 ras.gray_min_x = (Short)byte_len;
2740 ras.gray_max_x = -(Short)byte_len;
2745 Vertical_Gray_Sweep_Step( RAS_ARG )
2747 short* count = (short*)count_table;
2751 ras.traceOfs += ras.gray_width;
2753 if ( ras.traceOfs > ras.gray_width )
2758 pix = ras.gTarget + ras.traceG + ras.gray_min_x * 4;
2761 if ( ras.gray_max_x >= 0 )
2763 Long last_pixel = ras.target.width - 1;
2764 Int last_cell = last_pixel >> 2;
2765 Int last_bit = last_pixel & 3;
2772 if ( ras.gray_max_x >= last_cell && last_bit != 3 )
2774 ras.gray_max_x = last_cell - 1;
2778 if ( ras.gray_min_x < 0 )
2781 bit = ras.bTarget + ras.gray_min_x;
2782 bit2 = bit + ras.gray_width;
2784 c1 = ras.gray_max_x - ras.gray_min_x;
2788 c2 = count[*bit] + count[*bit2];
2792 pix[0] = grays[(c2 >> 12) & 0x000F];
2793 pix[1] = grays[(c2 >> 8 ) & 0x000F];
2794 pix[2] = grays[(c2 >> 4 ) & 0x000F];
2795 pix[3] = grays[ c2 & 0x000F];
2809 c2 = count[*bit] + count[*bit2];
2815 pix[2] = grays[(c2 >> 4 ) & 0x000F];
2817 pix[1] = grays[(c2 >> 8 ) & 0x000F];
2819 pix[0] = grays[(c2 >> 12) & 0x000F];
2829 ras.traceG += ras.traceIncr;
2831 ras.gray_min_x = 32000;
2832 ras.gray_max_x = -32000;
2838 Horizontal_Gray_Sweep_Span( RAS_ARGS Short y,
2844 /* nothing, really */
2855 Horizontal_Gray_Sweep_Drop( RAS_ARGS Short y,
2865 /* During the horizontal sweep, we only take care of drop-outs */
2872 Int dropOutControl = left->flags & 7;
2875 if ( e1 == e2 + ras.precision )
2877 switch ( dropOutControl )
2879 case 0: /* simple drop-outs including stubs */
2883 case 4: /* smart drop-outs including stubs */
2884 e1 = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2887 case 1: /* simple drop-outs excluding stubs */
2888 case 5: /* smart drop-outs excluding stubs */
2889 /* see Vertical_Sweep_Drop for details */
2891 /* rightmost stub test */
2892 if ( left->next == right && left->height <= 0 )
2895 /* leftmost stub test */
2896 if ( right->next == left && left->start == y )
2899 if ( dropOutControl == 1 )
2902 e1 = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2906 default: /* modes 2, 3, 6, 7 */
2907 return; /* no drop-out control */
2919 if ( x2 - x1 >= ras.precision_half )
2920 color = ras.grays[2];
2922 color = ras.grays[1];
2924 e1 = TRUNC( e1 ) / 2;
2925 if ( e1 < ras.target.rows )
2927 pixel = ras.gTarget - e1 * ras.target.pitch + y / 2;
2928 if ( ras.target.pitch > 0 )
2929 pixel += ( ras.target.rows - 1 ) * ras.target.pitch;
2931 if ( pixel[0] == ras.grays[0] )
2938 #endif /* FT_RASTER_OPTION_ANTI_ALIASING */
2941 /*************************************************************************/
2943 /* Generic Sweep Drawing routine */
2945 /*************************************************************************/
2948 Draw_Sweep( RAS_ARG )
2950 Short y, y_change, y_height;
2952 PProfile P, Q, P_Left, P_Right;
2954 Short min_Y, max_Y, top, bottom, dropouts;
2956 Long x1, x2, xs, e1, e2;
2958 TProfileList waiting;
2959 TProfileList draw_left, draw_right;
2962 /* initialize empty linked lists */
2964 Init_Linked( &waiting );
2966 Init_Linked( &draw_left );
2967 Init_Linked( &draw_right );
2969 /* first, compute min and max Y */
2972 max_Y = (Short)TRUNC( ras.minY );
2973 min_Y = (Short)TRUNC( ras.maxY );
2979 bottom = (Short)P->start;
2980 top = (Short)( P->start + P->height - 1 );
2982 if ( min_Y > bottom )
2988 InsNew( &waiting, P );
2993 /* check the Y-turns */
2994 if ( ras.numTurns == 0 )
2996 ras.error = FT_THROW( Invalid );
3000 /* now initialize the sweep */
3002 ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y );
3004 /* then compute the distance of each profile from min_Y */
3010 P->countL = (UShort)( P->start - min_Y );
3019 if ( ras.numTurns > 0 &&
3020 ras.sizeBuff[-ras.numTurns] == min_Y )
3023 while ( ras.numTurns > 0 )
3025 /* check waiting list for new activations */
3032 P->countL -= y_height;
3033 if ( P->countL == 0 )
3035 DelOld( &waiting, P );
3037 if ( P->flags & Flow_Up )
3038 InsNew( &draw_left, P );
3040 InsNew( &draw_right, P );
3046 /* sort the drawing lists */
3049 Sort( &draw_right );
3051 y_change = (Short)ras.sizeBuff[-ras.numTurns--];
3052 y_height = (Short)( y_change - y );
3054 while ( y < y_change )
3061 P_Right = draw_right;
3078 if ( x2 - x1 <= ras.precision &&
3079 e1 != x1 && e2 != x2 )
3081 if ( e1 > e2 || e2 == e1 + ras.precision )
3083 Int dropOutControl = P_Left->flags & 7;
3086 if ( dropOutControl != 2 )
3088 /* a drop-out was detected */
3093 /* mark profile for drop-out processing */
3102 ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right );
3106 P_Left = P_Left->link;
3107 P_Right = P_Right->link;
3110 /* handle drop-outs _after_ the span drawing -- */
3111 /* drop-out processing has been moved out of the loop */
3112 /* for performance tuning */
3118 ras.Proc_Sweep_Step( RAS_VAR );
3125 Sort( &draw_right );
3129 /* now finalize the profiles that need it */
3135 if ( P->height == 0 )
3136 DelOld( &draw_left, P );
3144 if ( P->height == 0 )
3145 DelOld( &draw_right, P );
3150 /* for gray-scaling, flush the bitmap scanline cache */
3151 while ( y <= max_Y )
3153 ras.Proc_Sweep_Step( RAS_VAR );
3162 P_Right = draw_right;
3166 if ( P_Left->countL )
3170 dropouts--; /* -- this is useful when debugging only */
3172 ras.Proc_Sweep_Drop( RAS_VARS y,
3179 P_Left = P_Left->link;
3180 P_Right = P_Right->link;
3187 /*************************************************************************/
3190 /* Render_Single_Pass */
3193 /* Perform one sweep with sub-banding. */
3196 /* flipped :: If set, flip the direction of the outline. */
3199 /* Renderer error code. */
3202 Render_Single_Pass( RAS_ARGS Bool flipped )
3207 while ( ras.band_top >= 0 )
3209 ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision;
3210 ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision;
3214 ras.error = Raster_Err_None;
3216 if ( Convert_Glyph( RAS_VARS flipped ) )
3218 if ( ras.error != Raster_Err_Overflow )
3221 ras.error = Raster_Err_None;
3226 ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) );
3229 i = ras.band_stack[ras.band_top].y_min;
3230 j = ras.band_stack[ras.band_top].y_max;
3232 k = (Short)( ( i + j ) / 2 );
3234 if ( ras.band_top >= 7 || k < i )
3237 ras.error = FT_THROW( Invalid );
3242 ras.band_stack[ras.band_top + 1].y_min = k;
3243 ras.band_stack[ras.band_top + 1].y_max = j;
3245 ras.band_stack[ras.band_top].y_max = (Short)( k - 1 );
3252 if ( Draw_Sweep( RAS_VAR ) )
3262 /*************************************************************************/
3268 /* Render a glyph in a bitmap. Sub-banding if needed. */
3271 /* FreeType error code. 0 means success. */
3273 FT_LOCAL_DEF( FT_Error )
3274 Render_Glyph( RAS_ARG )
3279 Set_High_Precision( RAS_VARS ras.outline.flags &
3280 FT_OUTLINE_HIGH_PRECISION );
3281 ras.scale_shift = ras.precision_shift;
3283 if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )
3284 ras.dropOutControl = 2;
3287 if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS )
3288 ras.dropOutControl = 4;
3290 ras.dropOutControl = 0;
3292 if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) )
3293 ras.dropOutControl += 1;
3296 ras.second_pass = (FT_Byte)( !( ras.outline.flags &
3297 FT_OUTLINE_SINGLE_PASS ) );
3299 /* Vertical Sweep */
3300 ras.Proc_Sweep_Init = Vertical_Sweep_Init;
3301 ras.Proc_Sweep_Span = Vertical_Sweep_Span;
3302 ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
3303 ras.Proc_Sweep_Step = Vertical_Sweep_Step;
3306 ras.band_stack[0].y_min = 0;
3307 ras.band_stack[0].y_max = (short)( ras.target.rows - 1 );
3309 ras.bWidth = (unsigned short)ras.target.width;
3310 ras.bTarget = (Byte*)ras.target.buffer;
3312 if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 )
3315 /* Horizontal Sweep */
3316 if ( ras.second_pass && ras.dropOutControl != 2 )
3318 ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
3319 ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
3320 ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;
3321 ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
3324 ras.band_stack[0].y_min = 0;
3325 ras.band_stack[0].y_max = (short)( ras.target.width - 1 );
3327 if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 )
3331 return Raster_Err_None;
3335 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3337 /*************************************************************************/
3340 /* Render_Gray_Glyph */
3343 /* Render a glyph with grayscaling. Sub-banding if needed. */
3346 /* FreeType error code. 0 means success. */
3348 FT_LOCAL_DEF( FT_Error )
3349 Render_Gray_Glyph( RAS_ARG )
3355 Set_High_Precision( RAS_VARS ras.outline.flags &
3356 FT_OUTLINE_HIGH_PRECISION );
3357 ras.scale_shift = ras.precision_shift + 1;
3359 if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )
3360 ras.dropOutControl = 2;
3363 if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS )
3364 ras.dropOutControl = 4;
3366 ras.dropOutControl = 0;
3368 if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) )
3369 ras.dropOutControl += 1;
3372 ras.second_pass = !( ras.outline.flags & FT_OUTLINE_SINGLE_PASS );
3374 /* Vertical Sweep */
3377 ras.band_stack[0].y_min = 0;
3378 ras.band_stack[0].y_max = 2 * ras.target.rows - 1;
3380 ras.bWidth = ras.gray_width;
3381 pixel_width = 2 * ( ( ras.target.width + 3 ) >> 2 );
3383 if ( ras.bWidth > pixel_width )
3384 ras.bWidth = pixel_width;
3386 ras.bWidth = ras.bWidth * 8;
3387 ras.bTarget = (Byte*)ras.gray_lines;
3388 ras.gTarget = (Byte*)ras.target.buffer;
3390 ras.Proc_Sweep_Init = Vertical_Gray_Sweep_Init;
3391 ras.Proc_Sweep_Span = Vertical_Sweep_Span;
3392 ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
3393 ras.Proc_Sweep_Step = Vertical_Gray_Sweep_Step;
3395 error = Render_Single_Pass( RAS_VARS 0 );
3399 /* Horizontal Sweep */
3400 if ( ras.second_pass && ras.dropOutControl != 2 )
3402 ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
3403 ras.Proc_Sweep_Span = Horizontal_Gray_Sweep_Span;
3404 ras.Proc_Sweep_Drop = Horizontal_Gray_Sweep_Drop;
3405 ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
3408 ras.band_stack[0].y_min = 0;
3409 ras.band_stack[0].y_max = ras.target.width * 2 - 1;
3411 error = Render_Single_Pass( RAS_VARS 1 );
3416 return Raster_Err_None;
3419 #else /* !FT_RASTER_OPTION_ANTI_ALIASING */
3421 FT_LOCAL_DEF( FT_Error )
3422 Render_Gray_Glyph( RAS_ARG )
3426 return FT_THROW( Unsupported );
3429 #endif /* !FT_RASTER_OPTION_ANTI_ALIASING */
3433 ft_black_init( black_PRaster raster )
3435 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3439 /* set default 5-levels gray palette */
3440 for ( n = 0; n < 5; n++ )
3441 raster->grays[n] = n * 255 / 4;
3443 raster->gray_width = RASTER_GRAY_LINES / 2;
3445 FT_UNUSED( raster );
3450 /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
3451 /**** a static object. *****/
3458 ft_black_new( void* memory,
3459 FT_Raster *araster )
3461 static black_TRaster the_raster;
3462 FT_UNUSED( memory );
3465 *araster = (FT_Raster)&the_raster;
3466 FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) );
3467 ft_black_init( &the_raster );
3474 ft_black_done( FT_Raster raster )
3477 FT_UNUSED( raster );
3481 #else /* !_STANDALONE_ */
3485 ft_black_new( FT_Memory memory,
3486 black_PRaster *araster )
3489 black_PRaster raster = NULL;
3493 if ( !FT_NEW( raster ) )
3495 raster->memory = memory;
3496 ft_black_init( raster );
3506 ft_black_done( black_PRaster raster )
3508 FT_Memory memory = (FT_Memory)raster->memory;
3515 #endif /* !_STANDALONE_ */
3519 ft_black_reset( black_PRaster raster,
3525 if ( pool_base && pool_size >= (long)sizeof ( black_TWorker ) + 2048 )
3527 black_PWorker worker = (black_PWorker)pool_base;
3530 raster->buffer = pool_base + ( ( sizeof ( *worker ) + 7 ) & ~7 );
3531 raster->buffer_size = (long)( pool_base + pool_size -
3532 (char*)raster->buffer );
3533 raster->worker = worker;
3537 raster->buffer = NULL;
3538 raster->buffer_size = 0;
3539 raster->worker = NULL;
3546 ft_black_set_mode( black_PRaster raster,
3548 const char* palette )
3550 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3552 if ( mode == FT_MAKE_TAG( 'p', 'a', 'l', '5' ) )
3554 /* set 5-levels gray palette */
3555 raster->grays[0] = palette[0];
3556 raster->grays[1] = palette[1];
3557 raster->grays[2] = palette[2];
3558 raster->grays[3] = palette[3];
3559 raster->grays[4] = palette[4];
3564 FT_UNUSED( raster );
3566 FT_UNUSED( palette );
3575 ft_black_render( black_PRaster raster,
3576 const FT_Raster_Params* params )
3578 const FT_Outline* outline = (const FT_Outline*)params->source;
3579 const FT_Bitmap* target_map = params->target;
3580 black_PWorker worker;
3583 if ( !raster || !raster->buffer || !raster->buffer_size )
3584 return FT_THROW( Not_Ini );
3587 return FT_THROW( Invalid );
3589 /* return immediately if the outline is empty */
3590 if ( outline->n_points == 0 || outline->n_contours <= 0 )
3591 return Raster_Err_None;
3593 if ( !outline->contours || !outline->points )
3594 return FT_THROW( Invalid );
3596 if ( outline->n_points !=
3597 outline->contours[outline->n_contours - 1] + 1 )
3598 return FT_THROW( Invalid );
3600 worker = raster->worker;
3602 /* this version of the raster does not support direct rendering, sorry */
3603 if ( params->flags & FT_RASTER_FLAG_DIRECT )
3604 return FT_THROW( Unsupported );
3607 return FT_THROW( Invalid );
3610 if ( !target_map->width || !target_map->rows )
3611 return Raster_Err_None;
3613 if ( !target_map->buffer )
3614 return FT_THROW( Invalid );
3616 ras.outline = *outline;
3617 ras.target = *target_map;
3619 worker->buff = (PLong) raster->buffer;
3620 worker->sizeBuff = worker->buff +
3621 raster->buffer_size / sizeof ( Long );
3622 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3623 worker->grays = raster->grays;
3624 worker->gray_width = raster->gray_width;
3626 FT_MEM_ZERO( worker->gray_lines, worker->gray_width * 2 );
3629 return ( params->flags & FT_RASTER_FLAG_AA )
3630 ? Render_Gray_Glyph( RAS_VAR )
3631 : Render_Glyph( RAS_VAR );
3635 FT_DEFINE_RASTER_FUNCS( ft_standard_raster,
3636 FT_GLYPH_FORMAT_OUTLINE,
3637 (FT_Raster_New_Func) ft_black_new,
3638 (FT_Raster_Reset_Func) ft_black_reset,
3639 (FT_Raster_Set_Mode_Func)ft_black_set_mode,
3640 (FT_Raster_Render_Func) ft_black_render,
3641 (FT_Raster_Done_Func) ft_black_done