/***************************************************************************/ /* */ /* ftraster2.c */ /* */ /* The FreeType glyph rasterizer (body). */ /* */ /* Copyright 1996-1999 by */ /* David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ /* This file is part of the FreeType project, and may only be used */ /* modified and distributed under the terms of the FreeType project */ /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ /* this file you indicate that you have read the license and */ /* understand and accept it fully. */ /* */ /***************************************************************************/ /*************************************************************************/ /* */ /* The `raster' component implements FreeType's scan-line converter, the */ /* one used to generate bitmaps and pixmaps from vectorial outline */ /* descriptions. */ /* */ /* It has been rewritten entirely for FreeType 2.0, in order to become */ /* completely independent of the rest of the library. It should now be */ /* possible to include it more easily in all kinds of libraries and */ /* applications, which do not necessarily need the font engines and API. */ /* */ /* This version contains the following features: */ /* */ /* - Support for third-order Bezier arcs. */ /* */ /* - Improved performance of the 5-levels anti-aliasing algorithm. */ /* */ /* - 17-levels anti-aliasing for smoother curves, though the difference */ /* isn't always noticeable, depending on your palette. */ /* */ /* - An API to decompose a raster outline into a path (i.e., into a */ /* a series of segments and arcs). */ /* */ /* Planned additions: */ /* */ /* - Getting rid of the second pass for horizontal drop-out detection. */ /* I've got a few ideas, but I'll have to experiment in Pascal with */ /* them. to avoid damaging of the rendering of glyphs at small sizes. */ /* */ /* - Adding a `composition' callback, which should be invoked during */ /* anti-aliased rendering. In short, it will allow line-by-line */ /* composition (i.e., transparencies, etc.) of the output in a fairly */ /* portable way. Of course, a single sweep is required there. */ /* */ /*************************************************************************/ #include #ifndef _STANDALONE_ #include #endif #ifndef EXPORT_FUNC #define EXPORT_FUNC /* nothing */ #endif #undef FT_COMPONENT #define FT_COMPONENT trace_raster #ifdef _STANDALONE_ /*************************************************************************/ /* */ /* The following defines are used when the raster is compiled as a */ /* stand-alone object. Each of them is commented, and you're free to */ /* toggle them to suit your needs. */ /* */ /*************************************************************************/ /*************************************************************************/ /* */ /* FT_RASTER_INT_IS_32 */ /* */ /* Set this configuration macro to the unsigned type which has 32 */ /* bits. */ /* */ #define FT_RASTER_INT_IS_32 /*************************************************************************/ /* */ /* FT_RASTER_OPTION_ANTI_ALIAS */ /* */ /* Define this configuration macro if you want to support */ /* anti-aliasing. */ /* */ #define FT_RASTER_OPTION_ANTI_ALIAS /*************************************************************************/ /* */ /* FT_RASTER_OPTION_CONIC_BEZIERS */ /* */ /* Define this configuration macro if your source outlines contain */ /* second-order Bezier arcs. Typically, these are TrueType outlines. */ /* */ #define FT_RASTER_CONIC_BEZIERS /*************************************************************************/ /* */ /* FT_RASTER_OPTION_CUBIC_BEZIERS */ /* */ /* Define this configuration macro if your source outlines contain */ /* third-order Bezier arcs. Typically, these are Type1 outlines. */ /* */ #define FT_RASTER_CUBIC_BEZIERS /*************************************************************************/ /* */ /* FT_RASTER_ANTI_ALIAS_5 */ /* */ /* Define this configuration macro if you want to enable the 5-grays */ /* anti-aliasing mode. Ignored if FT_RASTER_OPTION_ANTI_ALIAS isn't */ /* defined. */ /* */ #define FT_RASTER_ANTI_ALIAS_5 /*************************************************************************/ /* */ /* FT_RASTER_ANTI_ALIAS_17 */ /* */ /* Define this configuration macro if you want to enable the 17-grays */ /* anti-aliasing mode. Ignored if FT_RASTER_OPTION_ANTI_ALIAS isn't */ /* defined. */ /* */ /* #define FT_RASTER_ANTI_ALIAS_17 */ /*************************************************************************/ /* */ /* FT_RASTER_LITTLE_ENDIAN */ /* FT_RASTER_BIG_ENDIAN */ /* */ /* The default anti-alias routines are processor-independent, but */ /* slow. Define one of these macros to suit your own system, and */ /* enjoy greatly improved rendering speed. */ /* */ /* #define FT_RASTER_LITTLE_ENDIAN */ /* #define FT_RASTER_BIG_ENDIAN */ #else /* _STANDALONE_ */ #include #include /*************************************************************************/ /* */ /* The following defines are used when the raster is compiled within the */ /* FreeType base layer. Don't change these unless you really know what */ /* you're doing. */ /* */ /*************************************************************************/ #ifdef FT_CONFIG_OPTION_ANTI_ALIAS #define FT_RASTER_OPTION_ANTI_ALIAS #endif #define FT_RASTER_CONIC_BEZIERS #define FT_RASTER_CUBIC_BEZIERS #define FT_RASTER_ANTI_ALIAS_5 /* #define FT_RASTER_ANTI_ALIAS_17 */ #ifdef FT_CONFIG_OPTION_LITTLE_ENDIAN #define FT_RASTER_LITTLE_ENDIAN #endif #ifdef FT_CONFIG_OPTION_BIG_ENDIAN #define FT_RASTER_BIG_ENDIAN #endif #endif /* _STANDALONE_ */ /* to keep the compiler happy */ #ifndef PTRACE2 #define PTRACE2(x) /*void*/ #endif /*************************************************************************/ /* */ /* FT_RASTER_ANY_ENDIAN indicates that no endianess was defined by one */ /* of the configuration macros. */ /* */ #if !defined( FT_RASTER_LITTLE_ENDIAN ) && !defined( FT_RASTER_BIG_ENDIAN ) #define FT_RASTER_ANY_ENDIAN #endif /*************************************************************************/ /* */ /* The rasterizer is a very general purpose component. Please leave the */ /* following redefinitions here (you never know your target */ /* environment). */ /* */ /*************************************************************************/ #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #ifndef NULL #define NULL (void*)0 #endif #ifndef UNUSED #define UNUSED( arg ) ( (void)(arg) ) #endif #undef FAILURE #define FAILURE TRUE #undef SUCCESS #define SUCCESS FALSE #ifndef ABS #define ABS(x) ( (x) < 0 ? -(x) : (x) ) #endif /*************************************************************************/ /* */ /* Please don't touch the following macros. Their importance is */ /* historical to FreeType, but they have some nice effects, like getting */ /* rid of all `->' symbols when accessing the raster object (replacing */ /* them with a simple `.'). */ /* */ /*************************************************************************/ /* used in function signatures to define the _first_ argument */ #define RAS_ARG_ FT_Raster raster, #define RAS_ARG FT_Raster raster /* used to call a function within this component, first parameter */ #define RAS_VAR_ raster, #define RAS_VAR raster /* used to access the current raster object, with a `.' instead of a */ /* `->' */ #define ras (*raster) /*************************************************************************/ /* */ /* For anti-aliasing modes, we use a 2 or 4 lines intermediate bitmap */ /* which is filtered repeatedly to render each pixmap row. The */ /* following macro defines this buffer's size in bytes (which is part of */ /* raster objects). */ /* */ #define ANTI_ALIAS_BUFFER_SIZE 2048 /*************************************************************************/ /* */ /* Error codes returned by the scan-line converter/raster. */ /* */ #define ErrRaster_Ok 0 #define ErrRaster_Uninitialized_Object 1 #define ErrRaster_Overflow 2 #define ErrRaster_Negative_Height 3 #define ErrRaster_Invalid_Outline 4 #define ErrRaster_Invalid_Map 5 #define ErrRaster_AntiAlias_Unsupported 6 #define ErrRaster_Invalid_Pool 7 #define ErrRaster_Unimplemented 8 #define ErrRaster_Bad_Palette_Count 9 #define Flow_Up 1 #define Flow_Down -1 #define SET_High_Precision( p ) Set_High_Precision( RAS_VAR_ p ) /*************************************************************************/ /* */ /* Fast MulDiv, as `b' is always < 64. Don't use intermediate */ /* precision. */ /* */ #define FMulDiv( a, b, c ) ( (a) * (b) / (c) ) /*************************************************************************/ /* */ /* Define DEBUG_RASTER if you want to generate a debug version of the */ /* rasterizer. This will progressively draw the glyphs while all the */ /* computation are done directly on the graphics screen (the glyphs will */ /* will be shown inverted). */ /* */ /* Note that DEBUG_RASTER should only be used for debugging with b/w */ /* rendering, not with gray levels. */ /* */ /* The definition of DEBUG_RASTER should appear in the file */ /* `ftconfig.h'. */ /* */ #ifdef DEBUG_RASTER extern char* vio; /* A pointer to VRAM or display buffer */ #endif /*************************************************************************/ /* */ /* The maximum number of stacked Bezier curves. Setting this constant */ /* to more than 32 is a pure waste of space. */ /* */ #define MaxBezier 32 /*************************************************************************/ /* */ /* The number fractional bits of *input* coordinates. We always use the */ /* 26.6 format (i.e, 6 bits for the fractional part), but hackers are */ /* free to experiment with different values. */ /* */ #define Pixel_Bits 6 /*************************************************************************/ /* */ /* An unsigned type that is exactly 32 bits on your platform. This */ /* means `unsigned long' on 16-bit machines, and `unsigned int' on */ /* others. */ /* */ #ifdef _STANDALONE_ #if defined( FT_RASTER_INT_IS_32 ) typedef unsigned int FT_Word32; #elif defined( FT_RASTER_LONG_IS_32 ) typedef unsigned long FT_Word32; #else #error "no 32bit type found - please check your configuration" #endif #endif /*************************************************************************/ /* */ /* A pointer to an unsigned char. */ /* */ typedef unsigned char* PByte; typedef char TResult; typedef unsigned char Byte; /*************************************************************************/ /* */ /* The type of the pixel coordinates used within the render pool during */ /* scan-line conversion. We use longs to store either 26.6 or 22.10 */ /* fixed float values, depending on the `precision' we want to use */ /* (i.e., low resp. high precision). These are ideals in order to */ /* subdivise Bezier arcs in halves by simple additions and shifts. */ /* */ /* Note that this is an 8-bytes integer on 64 bits systems. */ /* */ typedef long TPos, *PPos; /*************************************************************************/ /* */ /* The type of a scanline position/coordinate within a map. */ /* */ typedef int TScan, *PScan; /*************************************************************************/ /* */ /* States and directions of each line, arc, and profile. */ /* */ typedef enum _TDirection { Unknown, Ascending, Descending, Flat } TDirection; struct _TProfile; typedef struct _TProfile TProfile; typedef TProfile* PProfile; /*************************************************************************/ /* */ /* The `master' structure used for decomposing outlines. */ /* */ struct _TProfile { TPos X; /* current coordinate during sweep */ PProfile link; /* link to next profile - various purpose */ PPos offset; /* start of profile's data in render pool */ int flow; /* Profile orientation: Asc/Descending */ TScan height; /* profile's height in scanlines */ TScan start; /* profile's starting scanline */ TScan countL; /* number of lines to step before this */ /* profile becomes drawable */ PProfile next; /* next profile in same contour, used */ /* during drop-out control */ }; typedef PProfile TProfileList; typedef PProfile* PProfileList; /*************************************************************************/ /* */ /* A simple record used to implement a stack of bands, required by the */ /* sub-banding mechanism. */ /* */ typedef struct _TBand { TScan y_min; /* band's minimum */ TScan y_max; /* band's maximum */ } TBand; /*************************************************************************/ /* */ /* The size in _TPos_ of a profile record in the render pool. */ /* */ #define AlignProfileSize \ ( (sizeof ( TProfile ) + sizeof ( TPos ) - 1) / sizeof ( TPos ) ) /*************************************************************************/ /* */ /* Prototypes used for sweep function dispatch. */ /* */ typedef void (*Function_Sweep_Init)( RAS_ARG_ int* min, int* max ); typedef void (*Function_Sweep_Span)( RAS_ARG_ TScan y, TPos x1, TPos x2 ); typedef int (*Function_Test_Pixel)( RAS_ARG_ TScan y, int x ); typedef void (*Function_Set_Pixel)( RAS_ARG_ TScan y, int x, int color ); typedef void (*Function_Sweep_Step)( RAS_ARG ); typedef struct Raster_Render_ { Function_Sweep_Init init; Function_Sweep_Span span; Function_Sweep_Step step; Function_Test_Pixel test_pixel; Function_Set_Pixel set_pixel; } Raster_Render; /*************************************************************************/ /* */ /* Compute lowest integer coordinate below a given value. */ /* */ #define FLOOR( x ) ( (x) & ras.precision_mask ) /*************************************************************************/ /* */ /* Compute highest integer coordinate above a given value. */ /* */ #define CEILING( x ) ( ((x) + ras.precision - 1) & ras.precision_mask ) /*************************************************************************/ /* */ /* Get integer coordinate of a given 26.6 or 22.10 `x' coordinate -- no */ /* rounding. */ /* */ #define TRUNC( x ) ( (signed long)(x) >> ras.precision_bits ) /*************************************************************************/ /* */ /* Get the fractional part of a given coordinate. */ /* */ #define FRAC( x ) ( (x) & (ras.precision - 1) ) /*************************************************************************/ /* */ /* Scale an `input coordinate' (as found in FT_Outline structures) into */ /* a `work coordinate' which depends on current resolution and render */ /* mode. */ /* */ #define SCALED( x ) ( ((x) << ras.scale_shift) - ras.scale_delta ) /*************************************************************************/ /* */ /* DEBUG_PSET is used to plot a single pixel in VRam during debug mode. */ /* */ #ifdef DEBUG_RASTER #define DEBUG_PSET Pset() #else #define DEBUG_PSET #endif /*************************************************************************/ /* */ /* This structure defines a point in a plane. */ /* */ typedef struct _TPoint { TPos x, y; } TPoint; /*************************************************************************/ /* */ /* The most used variables are at the beginning of the structure. Thus, */ /* their offset can be coded with less opcodes which results in a */ /* smaller executable. */ /* */ struct FT_RasterRec_ { PPos cursor; /* Current cursor in render pool */ PPos pool; /* The render pool base address */ PPos pool_size; /* The render pool's size */ PPos pool_limit; /* Limit of profiles zone in pool */ int bit_width; /* target bitmap width */ PByte bit_buffer; /* target bitmap buffer */ PByte pix_buffer; /* target pixmap buffer */ TPoint last; long minY, maxY; int error; int precision_bits; /* precision related variables */ int precision; int precision_half; long precision_mask; int precision_shift; int precision_step; int precision_jitter; FT_Outline* outline; int n_points; /* number of points in current glyph */ int n_contours; /* number of contours in current glyph */ int n_extrema; /* number of `extrema' scanlines */ TPoint* arc; /* current Bezier arc pointer */ int num_profs; /* current number of profiles */ char fresh; /* signals a fresh new profile which */ /* `start' field must be completed */ char joint; /* signals that the last arc ended */ /* exactly on a scanline. Allows */ /* removal of doublets */ PProfile cur_prof; /* current profile */ PProfile start_prof; /* head of linked list of profiles */ PProfile first_prof; /* contour's first profile in case */ /* of impact */ TDirection state; /* rendering state */ FT_Bitmap target; /* description of target bit/pixmap */ int trace_bit; /* current offset in target bitmap */ int trace_pix; /* current offset in target pixmap */ int trace_incr; /* sweep's increment in target bitmap */ int gray_min_x; /* current min x during gray rendering */ int gray_max_x; /* current max x during gray rendering */ /* dispatch variables */ Raster_Render render; int scale_shift; /* == 0 for bitmaps */ /* == 1 for 5-levels pixmaps */ /* == 2 for 17-levels pixmaps */ int scale_delta; /* ras.precision_half for bitmaps */ /* 0 for pixmaps */ char dropout_mode; /* current drop_out control method */ char second_pass; /* indicates whether a horizontal pass */ /* should be performed to control drop-out */ /* accurately when calling Render_Glyph. */ /* Note that there is no horizontal pass */ /* during gray rendering. */ char flipped; /* this flag is set during the rendering to */ /* indicate the second pass. */ TBand band_stack[16]; /* band stack used for sub-banding */ int band_top; /* band stack top */ TPoint arcs[2 * MaxBezier + 1]; /* The Bezier stack */ #if defined( FT_RASTER_OPTION_ANTI_ALIAS ) long grays[20]; /* Palette of gray levels used for render */ int gray_width; /* length in bytes of the monochrome */ /* intermediate scanline of gray_lines. */ /* Each gray pixel takes 2 or 4 bits long */ /* The gray_lines must hold 2 lines, thus with size */ /* in bytes of at least `gray_width*2' */ int grays_count; /* number of entries in the palette */ char gray_lines[ANTI_ALIAS_BUFFER_SIZE]; /* Intermediate table used to render the */ /* graylevels pixmaps. */ /* gray_lines is a buffer holding 2 or 4 */ /* monochrome scanlines */ int count_table[256]; /* Look-up table used to quickly count */ /* set bits in several gray 2x2 cells */ /* at once. */ #endif /* FT_RASTER_OPTION_ANTI_ALIAS */ }; #ifdef DEBUG_RASTER /*************************************************************************/ /* */ /* */ /* Pset */ /* */ /* */ /* Used for debugging only. Plots a point in VRAM during rendering */ /* (not afterwards). */ /* */ /* */ /* This procedure relies on the value of cProfile->start, which may */ /* not be set when Pset() is called sometimes. This will usually */ /* result in a dot plotted on the first screen scanline (far away */ /* from its original position). */ /* */ /* This `feature' reflects nothing wrong in the current */ /* implementation, and the bitmap is rendered correctly, so don't */ /* panic if you see `flying' dots in debugging mode. */ /* */ static void Pset( RAS_ARG ) { long o; long x; x = ras.cursor[-1]; switch ( ras.cur_prof->flow ) { case FT_Flow_Up: o = Vio_ScanLineWidth * ( ras.cursor - ras.cur_prof->offset + ras.cur_prof->start ) + ( x / (ras.precision * 8) ); break; case FT_Flow_Down: o = Vio_ScanLineWidth * ( ras.cur_prof->start - ras.cursor + ras.cur_prof->offset ) + ( x / (ras.precision * 8) ); break; } if ( o > 0 ) Vio[o] |= (unsigned)0x80 >> ( (x/ras.precision) & 7 ); } static void Clear_Band( RAS_ARG_ TScan y1, TScan y2 ) { MEM_Set( Vio + y1*Vio_ScanLineWidth, (y2-y1+1)*Vio_ScanLineWidth, 0 ); } #endif /* DEBUG_RASTER */ /*************************************************************************/ /* */ /* */ /* Set_High_Precision */ /* */ /* */ /* Sets precision variables according to the parameter flag. */ /* */ /* */ /* High :: Set to True for high precision (typically for ppem < 18), */ /* false otherwise. */ /* */ static void Set_High_Precision( RAS_ARG_ char High ) { if ( High ) { ras.precision_bits = 10; ras.precision_step = 128; ras.precision_jitter = 24; } else { ras.precision_bits = 6; ras.precision_step = 32; ras.precision_jitter = 2; } ras.precision = 1 << ras.precision_bits; ras.precision_half = ras.precision / 2; ras.precision_shift = ras.precision_bits - Pixel_Bits; ras.precision_mask = -ras.precision; } /*************************************************************************/ /* */ /* A simple technical note on how the raster works: */ /* */ /* Converting an outline into a bitmap is achieved in several steps */ /* which are: */ /* */ /* 1 - Decomposing the outline into successive `profiles'. Each */ /* profile is simply an array of scanline intersections on a given */ /* dimension. A profile's main attributes are */ /* */ /* o its scanline position boundaries, i.e. `Ymin' and `Ymax'. */ /* */ /* o an array of intersection coordinates for each scanline */ /* between `Ymin' and `Ymax'. */ /* */ /* o a direction, indicating wether is was built going `up' or */ /* `down', as this is very important for filling rules. */ /* */ /* 2 - Sweeping the target map's scanlines in order to compute segment */ /* `spans' which are then filled. Additionaly, this pass performs */ /* drop-out control. */ /* */ /* The outline data is parsed during step 1 only. The profiles are */ /* built from the bottom of the render pool, used as a stack. The */ /* following graphics shows the profile list under construction: */ /* */ /* ____________________________________________________________ _ _ */ /* | | | | | */ /* | profile | coordinates for | profile | coordinates for |--> */ /* | 1 | profile 1 | 2 | profile 2 |--> */ /* |_________|___________________|_________|_________________|__ _ _ */ /* */ /* ^ ^ */ /* | | */ /* start of render pool cursor */ /* */ /* The top of the profile stack is kept in the `cursor' variable. */ /* */ /* As you can see, a profile record is pushed on top of the render */ /* pool, which is then followed by its coordinates/intersections. If */ /* a change of direction is detected in the outline, a new profile is */ /* generated until the end of the outline. */ /* */ /* Note that when all profiles have been generated, the function */ /* Finalize_Profile_Table() is used to record, for each profile, its */ /* bottom-most scanline as well as the scanline above its upmost */ /* boundary. These positions are called `extrema' because they (sort */ /* of) correspond to local extrema. They are stored in a sorted list */ /* built from the top of the render pool as a downwards stack: */ /* */ /* _ _ _______________________________________ */ /* | | */ /* <--| sorted list of | */ /* <--| extrema scanlines | */ /* _ _ __________________|____________________| */ /* */ /* ^ ^ */ /* | | */ /* pool_limit end of render pool */ /* */ /* This list is later used during the sweep phase in order to */ /* optimize performance (see technical note on the sweep below). */ /* */ /* Of course, the raster detects whether the two stacks collide and */ /* handles the situation propertly. */ /* */ /*************************************************************************/ /*************************************************************************/ /* */ /* */ /* New_Profile */ /* */ /* */ /* Creates a new Profile in the render pool. */ /* */ /* */ /* aState :: The state/orientation of the new profile. */ /* */ /* */ /* SUCCESS or FAILURE. */ /* */ static TResult New_Profile( RAS_ARG_ TDirection direction ) { if ( ras.start_prof == NULL ) { ras.cur_prof = (PProfile)ras.cursor; /* current profile */ ras.start_prof = ras.cur_prof; /* first profile in pool */ ras.cursor += AlignProfileSize; /* record profile in buffer */ } /* check for overflow */ if ( ras.cursor >= ras.pool_limit ) { ras.error = ErrRaster_Overflow; return FAILURE; } /* record profile direction */ switch ( direction ) { case Ascending: ras.cur_prof->flow = Flow_Up; break; case Descending: ras.cur_prof->flow = Flow_Down; break; default: ras.error = ErrRaster_Invalid_Map; return FAILURE; } /* initialize a few fields */ { PProfile cur = ras.cur_prof; cur->start = 0; /* current start scanline */ cur->height = 0; /* current height */ cur->offset = ras.cursor; /* address of first coordinate */ cur->link = (PProfile)0; /* link to next profile in pool */ cur->next = (PProfile)0; /* link to next profile in contour */ } /* record the first profile in a contour */ if ( ras.first_prof == NULL ) ras.first_prof = ras.cur_prof; ras.state = direction; ras.fresh = TRUE; /* this profile has no coordinates yet */ ras.joint = FALSE; return SUCCESS; } /*************************************************************************/ /* */ /* */ /* End_Profile */ /* */ /* */ /* Finalizes the current Profile and computes its height. If it is */ /* not 0, the profile's fields are updated and a new profile is */ /* pushed on top of its coordinates. Otherwise the current profile */ /* is kept and the recording of intersections is restarted. */ /* */ /* */ /* SUCCESS or FAILURE. */ /* */ static TResult End_Profile( RAS_ARG ) { int h; h = ras.cursor - ras.cur_prof->offset; if ( h < 0 ) { /* This error should _never_ occur unless the raster is buggy */ ras.error = ErrRaster_Negative_Height; return FAILURE; } if ( h > 0 ) { PProfile old, new; /* record scanline height in current profile, create a new one */ /* and set a link from the old one to it */ old = ras.cur_prof; old->height = h; ras.cur_prof = new = (PProfile)ras.cursor; ras.cursor += AlignProfileSize; new->height = 0; new->offset = ras.cursor; old->next = new; ras.num_profs++; } /* check for overflow */ if ( ras.cursor >= ras.pool_limit ) { ras.error = ErrRaster_Overflow; return FAILURE; } ras.joint = FALSE; return SUCCESS; } /*************************************************************************/ /* */ /* */ /* Insert_Extrema */ /* */ /* */ /* Records that a given scanline contains at least one local */ /* extremum. The table of extrema is placed at the end of the render */ /* pool and grows downwards. It is used during the sweep phase. */ /* */ /* */ /* y :: The coordinate of the scanline containing an extremum. */ /* */ static TResult Insert_Extrema( RAS_ARG_ TScan y ) { PPos extrema; TScan y2; int n; PTRACE2(( "EXTREMA += %d", y )); n = ras.n_extrema - 1; extrema = ras.pool_size - ras.n_extrema; /* look for first y extremum that is <= */ while ( n >= 0 && y < extrema[n] ) n--; /* if it is <, simply insert it, ignore if == */ if ( n >= 0 && y > extrema[n] ) while ( n >= 0 ) { y2 = extrema[n]; extrema[n] = y; y = y2; n--; } if ( n < 0 ) { ras.pool_limit--; ras.n_extrema++; ras.pool_size[-ras.n_extrema] = y; if ( ras.pool_limit <= ras.cursor ) { ras.error = ErrRaster_Overflow; return FAILURE; } } return SUCCESS; } /*************************************************************************/ /* */ /* */ /* Finalize_Profile_Table */ /* */ /* */ /* Adjusts all links in the profiles list. Called when the outline */ /* parsing is done. */ /* */ /* */ /* SUCCESS or FAILURE. */ /* */ static TResult Finalize_Profile_Table( RAS_ARG ) { int n, bottom, top; PProfile p; n = ras.num_profs; if ( n > 1 ) { p = ras.start_prof; while ( n > 0 ) { if ( n > 1 ) p->link = (PProfile)( p->offset + p->height ); else p->link = NULL; switch ( p->flow ) { case Flow_Down: PTRACE2(( "FLOW DOWN (start = %d, height = %d)", p->start, p->height )); bottom = p->start - p->height+1; top = p->start; p->start = bottom; p->offset += p->height-1; break; case Flow_Up: default: PTRACE2(( "FLOW UP (start = %d, height = %d)", p->start, p->height )); bottom = p->start; top = p->start + p->height-1; } if ( Insert_Extrema( RAS_VAR_ bottom ) || Insert_Extrema( RAS_VAR_ top+1 ) ) return FAILURE; p = p->link; n--; } } else ras.start_prof = NULL; return SUCCESS; } /*************************************************************************/ /* */ /* */ /* Line_Up */ /* */ /* */ /* Computes the scan-line intersections of an ascending line segment */ /* and stores them in the render pool. */ /* */ /* */ /* x1 :: The start x coordinate. */ /* y1 :: The start y coordinate. */ /* x2 :: The end x coordinate. */ /* y2 :: The end y coordinate. */ /* miny :: The minimum vertical grid coordinate. */ /* maxy :: The maximum vertical grid coordinate. */ /* */ /* */ /* SUCCESS or FAILURE. */ /* */ static TResult Line_Up( RAS_ARG_ TPos x1, TPos y1, TPos x2, TPos y2, TPos miny, TPos maxy ) { TPos Dx, Dy; int e1, e2, f1, f2, size; TPos Ix, Rx, Ax; PPos top; Dx = x2 - x1; Dy = y2 - y1; if ( Dy <= 0 || y2 < miny || y1 > maxy ) return SUCCESS; /* clip to higher scanline when necessary */ if ( y2 > maxy ) { /* x2 += FMulDiv( Dx, maxy-y2, Dy ); UNNECESSARY */ e2 = TRUNC( maxy ); f2 = 0; } else { e2 = TRUNC( y2 ); f2 = FRAC( y2 ); } /* clip to lower scanline when necessary */ if ( y1 < miny ) { TPos x, y; /* we use a binary search to compute the lower // clipping intersection. That's because we don't // want to use an external function like FT_MulDiv // to compute it directly. */ if ( y2 == miny ) goto Exit; do { x = (x1 + x2) >> 1; y = (y1 + y2) >> 1; if (y <= miny) { x1 = x; y1 = y; } else { x2 = x; y2 = y; } } while ( y1 < miny ); e1 = TRUNC( miny ); f1 = 0; } else { e1 = TRUNC( y1 ); f1 = FRAC( y1 ); } /* adjust start point so that we begin on an integer scanline position */ if ( f1 > 0 ) { if ( e1 == e2 ) goto Exit; else { x1 += FMulDiv( Dx, ras.precision - f1, Dy ); e1 += 1; } } else if ( ras.joint ) { ras.cursor--; ras.joint = FALSE; } ras.joint = ( f2 == 0 ); /* if this is a `fresh' profile, record its starting scanline */ if ( ras.fresh ) { ras.cur_prof->start = e1; ras.fresh = FALSE; } /* check for overflow */ size = e2 - e1 + 1; if ( ras.cursor + size >= ras.pool_limit ) { ras.error = ErrRaster_Overflow; return FAILURE; } /* compute decision variables and push the intersections on top */ /* of the render pool */ Dx <<= ras.precision_bits; Ix = Dx / Dy; Rx = Dx % Dy; if (Rx < 0) { Ix --; Rx += Dy; } Ax = -Dy; Rx <<= 1; Dy <<= 1; top = ras.cursor; while ( size > 0 ) { *top++ = x1; DEBUG_PSET; x1 += Ix; Ax += Rx; if ( Ax >= 0 ) { Ax -= Dy; x1 ++; } size--; } ras.cursor = top; Exit: return SUCCESS; } /*************************************************************************/ /* */ /* */ /* Line_Down */ /* */ /* */ /* Computes the scan-line intersections of a descending line segment */ /* and stores them in the render pool. */ /* */ /* */ /* x1 :: The start x coordinate. */ /* y1 :: The start y coordinate. */ /* x2 :: The end x coordinate. */ /* y2 :: The end y coordinate. */ /* miny :: The minimum vertical grid coordinate. */ /* maxy :: The maximum vertical grid coordinate. */ /* */ /* */ /* SUCCESS or FAILURE. */ /* */ static TResult Line_Down( RAS_ARG_ TPos x1, TPos y1, TPos x2, TPos y2, TPos miny, TPos maxy ) { TResult result, fresh; /* simply invert the coordinates and call Line_Up */ fresh = ras.fresh; result = Line_Up( RAS_VAR_ x1, -y1, x2, -y2, -maxy, -miny ); /* if this was a fresh profile, invert the recorded start position */ if ( fresh && !ras.fresh ) ras.cur_prof->start = -ras.cur_prof->start; return result; } /* A function type describing the functions used to split bezier arcs */ typedef void (*TSplitter)( TPoint* base ); /*************************************************************************/ /* */ /* */ /* Bezier_Up */ /* */ /* */ /* Computes the scan-line intersections of an ascending second-order */ /* Bezier arc and stores them in the render pool. The arc is taken */ /* from the top of the stack. */ /* */ /* */ /* miny :: The minimum vertical grid coordinate. */ /* maxy :: The maximum vertical grid coordinate. */ /* */ /* */ /* SUCCESS or FAILURE. */ /* */ static TResult Bezier_Up( RAS_ARG_ int degree, TSplitter splitter, TPos miny, TPos maxy ) { TPos y1, y2, e, e2, e0; int f1; TPoint* arc; TPoint* start_arc; PPos top; arc = ras.arc; y1 = arc[degree].y; y2 = arc[0].y; top = ras.cursor; if ( y2 < miny || y1 > maxy ) goto Fin; e2 = FLOOR( y2 ); /* integer end y */ if ( e2 > maxy ) e2 = FLOOR(maxy); e0 = CEILING(miny); if ( y1 < miny ) { e = e0; /* integer start y == current scanline */ } else { e = CEILING( y1 ); /* integer start y == current scanline */ f1 = FRAC( y1 ); /* fractional shift of start y */ e0 = e; /* first integer scanline to be pushed */ if ( f1 == 0 ) /* do we start on an integer scanline? */ { if ( ras.joint ) { top--; ras.joint = FALSE; } *top++ = arc[degree].x; /* write directly start position */ DEBUG_PSET; e += ras.precision; /* go to next scanline */ } } /* record start position if necessary */ if ( ras.fresh ) { ras.cur_prof->start = TRUNC( e0 ); ras.fresh = FALSE; } /* exit if the current scanline is already above the max scanline */ if ( e2 < e ) goto Fin; /* check for overflow */ if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.pool_limit ) { ras.cursor = top; ras.error = ErrRaster_Overflow; return FAILURE; } start_arc = arc; /* loop while there is still an arc on the bezier stack */ /* and the current scan line is below y max == e2 */ while ( arc >= start_arc && e <= e2 ) { ras.joint = FALSE; y2 = arc[0].y; /* final y of the top-most arc */ if ( y2 > e ) /* the arc intercepts the current scanline */ { y1 = arc[degree].y; /* start y of top-most arc */ if ( y2 - y1 >= ras.precision_step ) { /* if the arc's height is too great, split it */ splitter( arc ); arc += degree; } else { /* otherwise, approximate it as a segment and compute */ /* its intersection with the current scanline */ *top++ = arc[degree].x + FMulDiv( arc[0].x-arc[degree].x, e - y1, y2 - y1 ); DEBUG_PSET; arc -= degree; /* pop the arc */ e += ras.precision; /* go to next scanline */ } } else { if ( y2 == e ) /* if the arc falls on the scanline */ { /* record its _joint_ intersection */ ras.joint = TRUE; *top++ = arc[0].x; DEBUG_PSET; e += ras.precision; /* go to next scanline */ } arc -= degree; /* pop the arc */ } } Fin: ras.cursor = top; ras.arc -= degree; return SUCCESS; } /*************************************************************************/ /* */ /* */ /* Bezier_Down */ /* */ /* */ /* Computes the scan-line intersections of a descending second-order */ /* Bezier arc and stores them in the render pool. The arc is taken */ /* from the top of the stack. */ /* */ /* */ /* miny :: The minimum vertical grid coordinate. */ /* maxy :: The maximum vertical grid coordinate. */ /* */ /* */ /* SUCCESS or FAILURE. */ /* */ static TResult Bezier_Down( RAS_ARG_ int degree, TSplitter splitter, TPos miny, TPos maxy ) { TPoint* arc = ras.arc; TResult result, fresh; arc[0].y = -arc[0].y; arc[1].y = -arc[1].y; arc[2].y = -arc[2].y; if (degree > 2) arc[3].y = -arc[3].y; fresh = ras.fresh; result = Bezier_Up( RAS_VAR_ degree, splitter, -maxy, -miny ); if ( fresh && !ras.fresh ) ras.cur_prof->start = -ras.cur_prof->start; arc[0].y = -arc[0].y; return result; } #ifdef FT_RASTER_CONIC_BEZIERS /*************************************************************************/ /* */ /* */ /* Split_Conic */ /* */ /* */ /* Subdivides one second-order Bezier arc into two joint sub-arcs in */ /* the Bezier stack. */ /* */ /* */ /* This routine is the `beef' of the component. It is one of _the_ */ /* inner loops that should be optimized like hell to get the best */ /* performance. */ /* */ static void Split_Conic( TPoint* base ) { TPos a, b; base[4].x = base[2].x; b = base[1].x; a = base[3].x = ( base[2].x + b )/2; b = base[1].x = ( base[0].x + b )/2; base[2].x = ( a + b ) / 2; base[4].y = base[2].y; b = base[1].y; a = base[3].y = ( base[2].y + b )/2; b = base[1].y = ( base[0].y + b )/2; base[2].y = ( a + b ) / 2; } /*************************************************************************/ /* */ /* */ /* Push_Conic */ /* */ /* */ /* Clears the Bezier stack and pushes a new arc on top of it. */ /* */ /* */ /* p2 :: A pointer to the second (control) point. */ /* p3 :: A pointer to the third (end) point. */ /* */ /* */ /* The first point is taken as `raster->last', so it doesn't appear */ /* in the signature. */ /* */ static void Push_Conic( RAS_ARG_ FT_Vector* p2, FT_Vector* p3 ) { #undef STORE #define STORE( _arc, point ) \ { \ TPos x = SCALED( point->x ); \ TPos y = SCALED( point->y ); \ \ \ if ( ras.flipped ) \ { \ _arc.x = y; \ _arc.y = x; \ } \ else \ { \ _arc.x = x; \ _arc.y = y; \ } \ } TPoint* arc; ras.arc = arc = ras.arcs; arc[2] = ras.last; STORE( arc[1], p2 ); STORE( arc[0], p3 ); #undef STORE } #endif /* FT_RASTER_CONIC_BEZIERS */ #ifdef FT_RASTER_CUBIC_BEZIERS /*************************************************************************/ /* */ /* Split_Cubic */ /* */ /* */ /* Subdivides a third-order Bezier arc into two joint sub-arcs in */ /* the Bezier stack. */ /* */ /* */ /* This routine is the `beef' of the component. It is one of _the_ */ /* inner loops that should be optimized like hell to get the best */ /* performance. */ /* */ static void Split_Cubic( TPoint* base ) { TPos a, b, c, d; base[6].x = base[3].x; c = base[1].x; d = base[2].x; base[1].x = a = ( base[0].x + c ) / 2; base[5].x = b = ( base[3].x + d ) / 2; c = ( c + d ) / 2; base[2].x = a = ( a + c ) / 2; base[4].x = b = ( b + c ) / 2; base[3].x = ( a + b ) / 2; base[6].y = base[3].y; c = base[1].y; d = base[2].y; base[1].y = a = ( base[0].y + c ) / 2; base[5].y = b = ( base[3].y + d ) / 2; c = ( c + d ) / 2; base[2].y = a = ( a + c ) / 2; base[4].y = b = ( b + c ) / 2; base[3].y = ( a + b ) / 2; } /*************************************************************************/ /* */ /* */ /* Push_Cubic */ /* */ /* */ /* Clears the Bezier stack and pushes a new third-order Bezier arc on */ /* top of it. */ /* */ /* */ /* p2 :: A pointer to the second (control) point. */ /* p3 :: A pointer to the third (control) point. */ /* p4 :: A pointer to the fourth (end) point. */ /* */ /* */ /* The first point is taken as `raster->last', so it doesn't appear */ /* in the signature. */ /* */ /* This is the same as Push_Conic(), except that it deals with */ /* third-order Beziers. */ /* */ static void Push_Cubic( RAS_ARG_ FT_Vector* p2, FT_Vector* p3, FT_Vector* p4 ) { #undef STORE #define STORE( _arc, point ) \ { \ TPos x = SCALED( point->x ); \ TPos y = SCALED( point->y ); \ \ \ if ( ras.flipped ) \ { \ _arc.x = y; \ _arc.y = x; \ } \ else \ { \ _arc.x = x; \ _arc.y = y; \ } \ } TPoint* arc; ras.arc = arc = ras.arcs; arc[3] = ras.last; STORE( arc[2], p2 ); STORE( arc[1], p3 ); STORE( arc[0], p4 ); #undef STORE } #endif /* FT_RASTER_CUBIC_BEZIERS */ /*************************************************************************/ /* */ /* */ /* Check_Contour */ /* */ /* */ /* Performs some checks at contour closure. */ /* */ /* */ /* SUCCESS or FAILURE. */ /* */ static TResult Check_Contour( RAS_ARG ) { PProfile lastProfile; /* Sometimes, the first and last profile in a contour join on */ /* an integer scan-line; we must then remove the last intersection */ /* from the last profile to get rid of doublets */ if ( ( FRAC( ras.last.y ) == 0 && ras.last.y >= ras.minY && ras.last.y <= ras.maxY ) ) { if ( ras.first_prof && ras.first_prof->flow == ras.cur_prof->flow ) ras.cursor--; } lastProfile = ras.cur_prof; if ( End_Profile( RAS_VAR ) ) return FAILURE; /* close the `next profile in contour' linked list */ lastProfile->next = ras.first_prof; return SUCCESS; } /*************************************************************************/ /* */ /* */ /* Move_To */ /* */ /* */ /* This function injects a new contour in the render pool. */ /* */ /* */ /* to :: A pointer to the contour's first point. */ /* raster :: A pointer to the current raster object. */ /* */ /* */ /* Error code. 0 means success. */ /* */ /* */ /* This function is used as a `FTRasterMoveTo_Func' by the outline */ /* decomposer. */ /* */ static int Move_To( FT_Vector* to, FT_Raster raster ) { TPos scaled_x, scaled_y; /* if there was already a contour being built, perform some checks */ if ( ras.start_prof ) if ( Check_Contour( RAS_VAR ) ) return FAILURE; /* set the `current last point' */ scaled_x = SCALED( to->x ); scaled_y = SCALED( to->y ); if ( ras.flipped ) { ras.last.x = scaled_y; ras.last.y = scaled_x; } else { ras.last.x = scaled_x; ras.last.y = scaled_y; } ras.state = Unknown; ras.first_prof = NULL; return SUCCESS; } /*************************************************************************/ /* */ /* */ /* Line_To */ /* */ /* */ /* This function injects a new line segment in the render pool and */ /* adjusts the profiles list accordingly. */ /* */ /* */ /* to :: A pointer to the target position. */ /* raster :: A pointer to the current raster object. */ /* */ /* */ /* Error code. 0 means success. */ /* */ /* */ /* This function is used as a `FTRasterLineTo_Func' by the outline */ /* decomposer. */ /* */ static int Line_To( FT_Vector* to, FT_Raster raster ) { TPos x, scaled_x; TPos y, scaled_y; scaled_x = SCALED( to->x ); scaled_y = SCALED( to->y ); if ( ras.flipped ) { x = scaled_y; y = scaled_x; } else { x = scaled_x; y = scaled_y; } /* First, detect a change of direction */ if ( y != ras.last.y ) { TDirection new_state = ( (y > ras.last.y) ? Ascending : Descending ); if ( ras.state != new_state ) { if ( ras.state != Unknown && End_Profile( RAS_VAR ) ) goto Fail; if ( New_Profile( RAS_VAR_ new_state ) ) goto Fail; } } /* Then compute the lines */ switch ( ras.state ) { case Ascending: if ( Line_Up ( RAS_VAR_ ras.last.x, ras.last.y, x, y, ras.minY, ras.maxY ) ) goto Fail; break; case Descending: if ( Line_Down( RAS_VAR_ ras.last.x, ras.last.y, x, y, ras.minY, ras.maxY ) ) goto Fail; break; default: ; } ras.last.x = x; ras.last.y = y; return SUCCESS; Fail: return FAILURE; } #ifdef FT_RASTER_CONIC_BEZIERS /*************************************************************************/ /* */ /* */ /* Conic_To */ /* */ /* */ /* Injects a new conic Bezier arc and adjusts the profile list */ /* accordingly. */ /* */ /* */ /* control :: A pointer to an intermediate control point. */ /* to :: A pointer to the end point. */ /* raster :: A handle to the current raster object. */ /* */ /* */ /* Error code. 0 means success. */ /* */ /* */ /* This function is used as a `FTRasterConicTo_Func' by the outline */ /* decomposer. */ /* */ static int Conic_To( FT_Vector* control, FT_Vector* to, FT_Raster raster ) { TPos y1, y2, y3, x3, ymin, ymax; TDirection state_bez; Push_Conic( RAS_VAR_ control, to ); do { y1 = ras.arc[2].y; y2 = ras.arc[1].y; y3 = ras.arc[0].y; x3 = ras.arc[0].x; /* first, categorize the Bezier arc */ if ( y1 <= y3 ) { ymin = y1; ymax = y3; } else { ymin = y3; ymax = y1; } if ( y2 < ymin || y2 > ymax ) { /* this arc has no given direction, split it !! */ Split_Conic( ras.arc ); ras.arc += 2; } else if ( y1 == y3 ) { /* this arc is flat, ignore it and pop it from the bezier stack */ ras.arc -= 2; } else { /* the arc is y-monotonous, either ascending or descending */ /* detect a change of direction */ state_bez = y1 < y3 ? Ascending : Descending; if ( ras.state != state_bez ) { /* finalize current profile if any */ if ( ras.state != Unknown && End_Profile( RAS_VAR ) ) goto Fail; /* create a new profile */ if ( New_Profile( RAS_VAR_ state_bez ) ) goto Fail; } /* now call the appropriate routine */ if ( state_bez == Ascending ) { if ( Bezier_Up( RAS_VAR_ 2, Split_Conic, ras.minY, ras.maxY ) ) goto Fail; } else if ( Bezier_Down( RAS_VAR_ 2, Split_Conic, ras.minY, ras.maxY ) ) goto Fail; } } while ( ras.arc >= ras.arcs ); ras.last.x = x3; ras.last.y = y3; return SUCCESS; Fail: return FAILURE; } #else /* FT_RASTER_CONIC_BEZIERS */ static int Conic_To( FT_Vector* control, FT_Vector* to, FT_Raster raster ) { UNUSED( control ); UNUSED( to ); UNUSED( raster ); return ErrRaster_Invalid_Outline; } #endif /* FT_RASTER_CONIC_BEZIERS */ #ifdef FT_RASTER_CUBIC_BEZIERS /*************************************************************************/ /* */ /* */ /* Cubic_To */ /* */ /* */ /* Injects a new cubic Bezier arc and adjusts the profile list */ /* accordingly. */ /* */ /* */ /* control1 :: A pointer to the first control point. */ /* control2 :: A pointer to the second control point. */ /* to :: A pointer to the end point. */ /* raster :: A handle to the current raster object. */ /* */ /* */ /* Error code. 0 means success. */ /* */ /* */ /* This function is used as a `FTRasterCubicTo_Func' by the outline */ /* decomposer. */ /* */ static int Cubic_To( FT_Vector* control1, FT_Vector* control2, FT_Vector* to, FT_Raster raster ) { TPos y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2; TDirection state_bez; Push_Cubic( RAS_VAR_ control1, control2, to ); do { y1 = ras.arc[3].y; y2 = ras.arc[2].y; y3 = ras.arc[1].y; y4 = ras.arc[0].y; x4 = ras.arc[0].x; /* first, categorize the Bezier arc */ if ( y1 <= y4 ) { ymin1 = y1; ymax1 = y4; } else { ymin1 = y4; ymax1 = y1; } if ( y2 <= y3 ) { ymin2 = y2; ymax2 = y3; } else { ymin2 = y3; ymax2 = y2; } if ( ymin2 < ymin1 || ymax2 > ymax1 ) { /* this arc has no given direction, split it! */ Split_Cubic( ras.arc ); ras.arc += 3; } else if ( y1 == y4 ) { /* this arc is flat, ignore it and pop it from the bezier stack */ ras.arc -= 3; } else { state_bez = ( y1 <= y4 ) ? Ascending : Descending; /* detect a change of direction */ if ( ras.state != state_bez ) { if ( ras.state != Unknown && End_Profile( RAS_VAR ) ) goto Fail; if ( New_Profile( RAS_VAR_ state_bez ) ) goto Fail; } /* compute intersections */ if ( state_bez == Ascending ) { if ( Bezier_Up ( RAS_VAR_ 3, Split_Cubic, ras.minY, ras.maxY ) ) goto Fail; } else if ( Bezier_Down ( RAS_VAR_ 3, Split_Cubic, ras.minY, ras.maxY ) ) goto Fail; } } while ( ras.arc >= ras.arcs ); ras.last.x = x4; ras.last.y = y4; return SUCCESS; Fail: return FAILURE; } #else /* FT_RASTER_CUBIC_BEZIERS */ int Cubic_To( FT_Vector* control1, FT_Vector* control2, FT_Vector* to, FT_Raster raster ) { UNUSED( control1 ); UNUSED( control2 ); UNUSED( to ); UNUSED( raster ); return ErrRaster_Invalid_Outline; } #endif /* FT_RASTER_CUBIC_BEZIERS */ /********************************************************************/ /* */ /* The following function is compiled in the raster only when it is */ /* compile as a stand-alone module.. */ /* It can, otherwise, be found in the FreeType base layer */ #ifdef _STANDALONE_ /*************************************************************************/ /* */ /* */ /* FT_Decompose_Outline */ /* */ /* */ /* Walks over an outline's structure to decompose it into individual */ /* segments and Bezier arcs. This function is also able to emit */ /* `move to' and `close to' operations to indicate the start and end */ /* of new contours in the outline. */ /* */ /* */ /* outline :: A pointer to the source target. */ /* */ /* interface :: A table of `emitters', i.e,. function pointers called */ /* during decomposition to indicate path operations. */ /* */ /* user :: A typeless pointer which is passed to each emitter */ /* during the decomposition. It can be used to store */ /* the state during the decomposition. */ /* */ /* */ /* Error code. 0 means sucess. */ /* */ #if 0 typedef int (*FTRasterMoveTo_Func)( FT_Vector* to, void* user ); typedef int (*FTRasterLineTo_Func)( FT_Vector* to, void* user ); typedef int (*FTRasterConicTo_Func)( FT_Vector* control, FT_Vector* to, void* user ); typedef int (*FTRasterCubicTo_Func)( FT_Vector* control1, FT_Vector* control2, FT_Vector* to, void* user ); typedef struct FT_Raster_Funcs_ { FTRasterMoveTo_Func move_to; FTRasterLineTo_Func line_to; FTRasterConicTo_Func conic_to; FTRasterCubicTo_Func cubic_to; } FT_Raster_Funcs; #endif int FT_Decompose_Outline( FT_Outline* outline, FT_Raster_Funcs* interface, void* user ) { typedef enum _phases { phase_point, phase_conic, phase_cubic, phase_cubic2 } TPhase; FT_Vector v_first; FT_Vector v_last; FT_Vector v_control; FT_Vector v_control2; FT_Vector v_start; FT_Vector* point; PByte flags; int n; /* index of contour in outline */ int first; /* index of first point in contour */ int index; /* current point's index */ int error; char tag; /* current point's state */ TPhase phase; first = 0; for ( n = 0; n < outline->n_contours; n++ ) { int last; /* index of last point in contour */ last = outline->contours[n]; v_first = outline->points[first]; v_last = outline->points[last]; v_start = v_control = v_first; tag = FT_CURVE_TAG( outline->flags[first] ); index = first; /* A contour cannot start with a cubic control point! */ if ( tag == FT_Curve_Tag_Cubic ) return ErrRaster_Invalid_Outline; /* check first point to determine origin */ if ( tag == FT_Curve_Tag_Conic ) { /* first point is conic control. Yes, this happens. */ if ( FT_CURVE_TAG( outline->flags[last] ) == FT_Curve_Tag_On ) { /* start at last point if it is on the curve */ v_start = v_last; } else { /* if both first and last points are conic, */ /* start at their middle and record its position */ /* for closure */ v_start.x = ( v_start.x + v_last.x ) / 2; v_start.y = ( v_start.y + v_last.y ) / 2; v_last = v_start; } phase = phase_conic; } else phase = phase_point; /* Begin a new contour with MOVE_TO */ error = interface->move_to( &v_start, user ); if ( error ) return error; point = outline->points + first; flags = outline->flags + first; /* now process each contour point individually */ while ( index < last ) { index++; point++; flags++; tag = FT_CURVE_TAG( flags[0] ); switch ( phase ) { case phase_point: /* the previous point was on the curve */ switch ( tag ) { /* two succesive on points -> emit segment */ case FT_Curve_Tag_On: error = interface->line_to( point, user ); break; /* on point + conic control -> remember control point */ case FT_Curve_Tag_Conic: v_control = point[0]; phase = phase_conic; break; /* on point + cubic control -> remember first control */ default: v_control = point[0]; phase = phase_cubic; break; } break; case phase_conic: /* the previous point was a conic control */ switch ( tag ) { /* conic control + on point -> emit conic arc */ case FT_Curve_Tag_On: error = interface->conic_to( &v_control, point, user ); phase = phase_point; break; /* two successive conics -> emit conic arc `in between' */ case FT_Curve_Tag_Conic: { FT_Vector v_middle; v_middle.x = (v_control.x + point->x)/2; v_middle.y = (v_control.y + point->y)/2; error = interface->conic_to( &v_control, &v_middle, user ); v_control = point[0]; } break; default: error = ErrRaster_Invalid_Outline; } break; case phase_cubic: /* the previous point was a cubic control */ /* this point _must_ be a cubic control too */ if ( tag != FT_Curve_Tag_Cubic ) return ErrRaster_Invalid_Outline; v_control2 = point[0]; phase = phase_cubic2; break; case phase_cubic2: /* the two previous points were cubics */ /* this point _must_ be an on point */ if ( tag != FT_Curve_Tag_On ) error = ErrRaster_Invalid_Outline; else error = interface->cubic_to( &v_control, &v_control2, point, user ); phase = phase_point; break; } /* lazy error testing */ if ( error ) return error; } /* end of contour, close curve cleanly */ error = 0; tag = FT_CURVE_TAG( outline->flags[first] ); switch ( phase ) { case phase_point: if ( tag == FT_Curve_Tag_On ) error = interface->line_to( &v_first, user ); break; case phase_conic: error = interface->conic_to( &v_control, &v_start, user ); break; case phase_cubic2: if ( tag == FT_Curve_Tag_On ) error = interface->cubic_to( &v_control, &v_control2, &v_first, user ); else error = ErrRaster_Invalid_Outline; break; default: error = ErrRaster_Invalid_Outline; break; } if ( error ) return error; first = last + 1; } return SUCCESS; } #endif /*************************************************************************/ /* */ /* */ /* Convert_Glyph */ /* */ /* */ /* Converts a glyph into a series of segments and arcs and makes a */ /* profiles list with them. */ /* */ /* */ /* outline :: The glyph outline. */ /* */ /* */ /* SUCCESS or FAILURE. */ /* */ static TResult Convert_Glyph( RAS_ARG_ FT_Outline* outline ) { static FT_Outline_Funcs interface = { (FT_Outline_MoveTo_Func)Move_To, (FT_Outline_LineTo_Func)Line_To, (FT_Outline_ConicTo_Func)Conic_To, (FT_Outline_CubicTo_Func)Cubic_To }; /* Set up state in the raster object */ ras.start_prof = NULL; ras.joint = FALSE; ras.fresh = FALSE; ras.pool_limit = ras.pool_size - AlignProfileSize; ras.n_extrema = 0; ras.cur_prof = (PProfile)ras.cursor; ras.cur_prof->offset = ras.cursor; ras.num_profs = 0; /* Now decompose curve */ if ( FT_Decompose_Outline( outline, &interface, &ras ) ) return FAILURE; /* XXX: the error condition is in ras.error */ /* Check the last contour if needed */ if ( Check_Contour( RAS_VAR ) ) return FAILURE; /* Finalize profiles list */ return Finalize_Profile_Table( RAS_VAR ); } /*************************************************************************/ /* */ /* Init_Linked */ /* */ /* Inits an empty linked list. */ /* */ static void Init_Linked( TProfileList* l ) { *l = NULL; } /*************************************************************************/ /* */ /* InsNew */ /* */ /* Inserts a new Profile in a linked list. */ /* */ static void InsNew( PProfileList list, PProfile profile ) { PProfile *old, current; TPos x; old = list; current = *old; x = profile->X; while ( current ) { if ( x < current->X ) break; old = ¤t->link; current = *old; } profile->link = current; *old = profile; } /*************************************************************************/ /* */ /* DelOld */ /* */ /* Removes an old Profile from a linked list. */ /* */ static void DelOld( PProfileList list, PProfile profile ) { PProfile *old, current; old = list; current = *old; while ( current ) { if ( current == profile ) { *old = current->link; return; } old = ¤t->link; current = *old; } /* We should never reach this place, unless the Profile was not */ /* part of the list. */ } /*************************************************************************/ /* */ /* Update */ /* */ /* Updates all X offsets of a drawing list. */ /* */ static void Update( PProfile first ) { PProfile current = first; while ( current ) { current->X = *current->offset; current->offset += current->flow; current->height--; current = current->link; } } /*************************************************************************/ /* */ /* Sort */ /* */ /* Sorts a trace list. In 95%, the list is already sorted. We need */ /* an algorithm which is fast in this case. Bubble sort is enough */ /* and simple to implement. */ /* */ static void Sort( PProfileList list ) { PProfile *old, current, next; /* First, set the new X coordinate of each profile */ Update( *list ); /* Then sort them */ old = list; current = *old; if ( !current ) return; next = current->link; while ( next ) { if ( current->X <= next->X ) { old = ¤t->link; current = *old; if ( !current ) return; } else { *old = next; current->link = next->link; next->link = current; old = list; current = *old; } next = current->link; } } /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /******** ********/ /******** Vertical Bitmap Sweep Routines ********/ /******** ********/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /* */ /* */ /* Vertical_Sweep_Init */ /* */ /* */ /* Initializes the vertical bitmap sweep. Called by the generic */ /* sweep/draw routine before its loop. */ /* */ /* */ /* min :: The address of the current minimum scanline. */ /* max :: The address of the current maximum scanline. */ /* */ static void Vertical_Sweep_Init( RAS_ARG_ int* min, int* max ) { long pitch; UNUSED( max ); pitch = ras.target.pitch; /* start from the bottom line, going up !! */ ras.trace_bit = - *min * pitch; ras.trace_incr = -pitch; if (pitch > 0) ras.trace_bit += pitch*(ras.target.rows-1); ras.gray_min_x = 0; ras.gray_max_x = 0; } /*************************************************************************/ /* */ /* */ /* Vertical_Sweep_Span */ /* */ /* */ /* Draws a single horizontal bitmap span during the vertical bitmap */ /* sweep. */ /* */ /* */ /* y :: The current scanline. */ /* x1 :: The left span edge. */ /* x2 :: The right span edge. */ /* */ static void Vertical_Sweep_Span( RAS_ARG_ TScan y, TPos x1, TPos x2 ) { TPos e1, e2; int c1, c2; Byte f1, f2; PByte target; UNUSED( y ); /* Drop-out control */ e1 = TRUNC( CEILING( x1 ) ); if ( x2 - x1 - ras.precision <= ras.precision_jitter ) e2 = e1; else e2 = TRUNC( FLOOR( x2 ) ); if ( e1 <= e2 && e2 >= 0 && e1 < ras.bit_width ) { if ( e1 < 0 ) e1 = 0; if ( e2 >= ras.bit_width ) e2 = ras.bit_width - 1; c1 = e1 >> 3; c2 = e2 >> 3; f1 = ((unsigned char)0xFF >> (e1 & 7)); f2 = ~((unsigned char)0x7F >> (e2 & 7)); #ifdef FT_RASTER_ANY_ENDIAN if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1; if ( ras.gray_max_x < c2 ) ras.gray_max_x = c2; #endif target = ras.bit_buffer + ras.trace_bit + c1; c2 -= c1; if ( c2 > 0 ) { target[0] |= f1; /* memset() is slower than the following code on many platforms. */ /* This is due to the fact that, in the vast majority of cases, */ /* the span length in bytes is relatively small. */ c2--; while ( c2 > 0 ) { *(++target) = 0xFF; c2--; } target[1] |= f2; } else *target |= ( f1 & f2 ); } } /*************************************************************************/ /* */ /* */ /* Vertical_Test_Pixel */ /* */ /* */ /* Tests a pixel `light' during the vertical bitmap sweep. Used */ /* during drop-out control only. */ /* */ /* */ /* y :: The current scanline. */ /* x :: The current x coordinate. */ /* */ static int Vertical_Test_Pixel( RAS_ARG_ TScan y, int x ) { int c1 = x >> 3; UNUSED( y ); return ( x >= 0 && x < ras.bit_width && ras.bit_buffer[ras.trace_bit + c1] & (0x80 >> (x & 7)) ); } /*************************************************************************/ /* */ /* */ /* Vertical_Set_Pixel */ /* */ /* */ /* Sets a single pixel in a bitmap during the vertical sweep. Used */ /* during drop-out control. */ /* */ /* */ /* y :: The current scanline. */ /* x :: The current x coordinate. */ /* color :: Ignored by this function. */ /* */ static void Vertical_Set_Pixel( RAS_ARG_ int y, int x, int color ) { UNUSED( color ); UNUSED( y ); if ( x >= 0 && x < ras.bit_width ) { int c1 = x >> 3; if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1; if ( ras.gray_max_x < c1 ) ras.gray_max_x = c1; ras.bit_buffer[ras.trace_bit+c1] |= (char)(0x80 >> (x & 7)); } } /*************************************************************************/ /* */ /* */ /* Vertical_Sweep_Step */ /* */ /* */ /* Called whenever the sweep jumps to another scanline. Only updates */ /* the pointers in the vertical bitmap sweep. */ /* */ static void Vertical_Sweep_Step( RAS_ARG ) { ras.trace_bit += ras.trace_incr; } static const Raster_Render vertical_render_mono = { &Vertical_Sweep_Init, &Vertical_Sweep_Span, &Vertical_Sweep_Step, &Vertical_Test_Pixel, &Vertical_Set_Pixel }; /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /******** ********/ /******** Horizontal Bitmap Sweep Routines ********/ /******** ********/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /* */ /* */ /* Horizontal_Sweep_Init */ /* */ /* */ /* Initializes the horizontal bitmap sweep. Called by the generic */ /* sweep/draw routine before its loop. */ /* */ /* */ /* min :: The address of the current minimum pixel column. */ /* max :: The address of the current maximum pixel column. */ /* */ static void Horizontal_Sweep_Init( RAS_ARG_ int* min, int* max ) { UNUSED( ras ); UNUSED( min ); UNUSED( max ); /* nothing, really */ } /*************************************************************************/ /* */ /* */ /* Horizontal_Sweep_Span */ /* */ /* */ /* Draws a single vertical bitmap span during the horizontal bitmap */ /* sweep. Actually, this function is only used to check for weird */ /* drop-out cases. */ /* */ /* */ /* y :: The current pixel column. */ /* x1 :: The top span edge. */ /* x2 :: The bottom span edge. */ /* */ static void Horizontal_Sweep_Span( RAS_ARG_ TScan y, TPos x1, TPos x2 ) { TPos e1, e2; PByte bits; Byte f1; UNUSED( y ); /* During the horizontal sweep, we only take care of drop-outs */ if ( x2 - x1 < ras.precision ) { e1 = CEILING( x1 ); e2 = FLOOR( x2 ); if ( e1 == e2 ) { bits = ras.bit_buffer + (y >> 3); f1 = (Byte)(0x80 >> (y & 7)); e1 = TRUNC( e1 ); if ( e1 >= 0 && e1 < ras.target.rows ) { long pitch = ras.target.pitch; long offset = - pitch * e1; if (pitch > 0) offset += (ras.target.rows-1)*pitch; bits[offset] |= f1; } } } } /*************************************************************************/ /* */ /* */ /* Horizontal_Test_Pixel */ /* */ /* */ /* Tests a pixel `light' during the horizontal bitmap sweep. Used */ /* during drop-out control only. */ /* */ /* */ /* y :: The current pixel column. */ /* x :: The current row/scanline. */ /* */ static int Horizontal_Test_Pixel( RAS_ARG_ int y, int x ) { char* bits = (char*)ras.bit_buffer + (y >> 3); int f1 = (Byte)(0x80 >> (y & 7)); long pitch = ras.target.pitch; long offset = - pitch * x; if (pitch > 0) offset += (ras.target.rows-1)*pitch; return ( x >= 0 && x < ras.target.rows && (bits[0] & f1) ); } /*************************************************************************/ /* */ /* */ /* Horizontal_Set_Pixel */ /* */ /* */ /* Sets a single pixel in a bitmap during the horizontal sweep. Used */ /* during drop-out control. */ /* */ /* */ /* y :: The current pixel column. */ /* x :: The current row/scanline. */ /* color :: Ignored by this function. */ /* */ static void Horizontal_Set_Pixel( RAS_ARG_ int y, int x, int color ) { char* bits = (char*)ras.bit_buffer + (y >> 3); int f1 = (Byte)(0x80 >> (y & 7)); UNUSED( color ); if ( x >= 0 && x < ras.target.rows ) { long pitch = ras.target.pitch; long offset = - x*pitch; if (pitch > 0) offset += (ras.target.rows-1)*pitch; bits[offset] |= f1; } } /*************************************************************************/ /* */ /* */ /* Horizontal_Sweep_Step */ /* */ /* */ /* Called whenever the sweep jumps to another pixel column. */ /* */ static void Horizontal_Sweep_Step( RAS_ARG ) { UNUSED( ras.target ); /* Nothing, really */ } static const Raster_Render horizontal_render_mono = { &Horizontal_Sweep_Init, &Horizontal_Sweep_Span, &Horizontal_Sweep_Step, &Horizontal_Test_Pixel, &Horizontal_Set_Pixel }; /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /******** ********/ /******** Anti-Aliased Vertical Bitmap Sweep Routines ********/ /******** ********/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ #ifdef FT_RASTER_OPTION_ANTI_ALIAS /*************************************************************************/ /* */ /* */ /* Vertical_Gray_Sweep_Init */ /* */ /* */ /* Initializes the vertical bitmap sweep. Called by the generic */ /* sweep/draw routine before its loop. */ /* */ /* */ /* min :: The address of the current minimum scanline. */ /* max :: The address of the current maximum scanline. */ /* */ static void Vertical_Gray_Sweep_Init( RAS_ARG_ int* min, int* max ) { long pitch; UNUSED( max ); pitch = ras.target.pitch; /* start from the bottom line, going up */ ras.trace_incr = -pitch; ras.trace_bit = - *min * pitch; if (pitch > 0) ras.trace_bit += (ras.target.rows-1)*pitch; } /*************************************************************************/ /* */ /* */ /* Vertical_Gray_Sweep_Span */ /* */ /* */ /* Draws a single horizontal bitmap span during the vertical bitmap */ /* sweep. */ /* */ /* */ /* y :: The current scanline. */ /* x1 :: The left span edge. */ /* x2 :: The right span edge. */ /* */ static void Vertical_Gray_Sweep_Span( RAS_ARG_ TScan y, TPos x1, TPos x2 ) { TPos e1, e2; int shift = ras.precision_bits - 6; PByte target; UNUSED( y ); x1 += ras.precision_half; x2 += ras.precision_half; #ifdef FT_RASTER_OPTION_CONTRAST if ( x2-x1 < ras.precision ) { x1 = ((x1+x2) >> 1) - ras.precision_half; x2 = x1 + ras.precision; } #endif e1 = TRUNC( x1 ); e2 = TRUNC( x2 ); if ( e1 <= e2 && e2 >= 0 && e1 < ras.bit_width ) { x1 = FRAC(x1) >> shift; x2 = FRAC(x2) >> shift; if ( e1 < 0 ) { e1 = 0; x1 = 0; } if ( e2 > ras.bit_width ) { e2 = ras.bit_width-1; x2 = 0; } target = ras.bit_buffer + ras.trace_bit + e1; e2 -= e1; if ( e2 > 0 ) { if (x1 > 0) target[0] += (Byte)(64-x1) << 1; else target[0] = 127; e2--; while (e2 > 0) { *(++target) = 127; e2--; } if (x2) target[1] += (Byte)x2 << 1; } else { target[0] += (Byte)(x2-x1) << 1; } } } /*************************************************************************/ /* */ /* */ /* Vertical_Gray_Test_Pixel */ /* */ /* */ /* Tests a pixel `light' during the vertical bitmap sweep. Used */ /* during drop-out control only. */ /* */ /* */ /* y :: The current scanline. */ /* x :: The current x coordinate. */ /* */ static int Vertical_Gray_Test_Pixel( RAS_ARG_ TScan y, int x ) { UNUSED( y ); #if 0 /* as a rule of thumb, do not add a drop-out if the current */ /* gray level is over 0.5 */ return ( x >= 0 && x < ras.bit_width && ras.bit_buffer[ras.trace_bit + x] >= 64 ); #else UNUSED(x); return 0; #endif } /*************************************************************************/ /* */ /* */ /* Vertical_Gray_Set_Pixel */ /* */ /* */ /* Sets a single pixel in a bitmap during the vertical sweep. Used */ /* during drop-out control. */ /* */ /* */ /* y :: The current scanline. */ /* x :: The current x coordinate. */ /* color :: Ignored by this function. */ /* */ static void Vertical_Gray_Set_Pixel( RAS_ARG_ int y, int x, int color ) { UNUSED( y ); if ( x >= 0 && x < ras.bit_width ) { unsigned char* pixel; pixel = ras.bit_buffer + ras.trace_bit + x; /* do not add too much to the pixel gray level */ color += *pixel; if (color < 64) color = 64; *pixel = ( color >= 127 ? 127 : (unsigned char)color ); } } /*************************************************************************/ /* */ /* */ /* Vertical_Sweep_Step */ /* */ /* */ /* Called whenever the sweep jumps to another scanline. Only updates */ /* the pointers in the vertical bitmap sweep. */ /* */ static void Vertical_Gray_Sweep_Step( RAS_ARG ) { ras.trace_bit += ras.trace_incr; } static const Raster_Render vertical_render_gray = { &Vertical_Gray_Sweep_Init, &Vertical_Gray_Sweep_Span, &Vertical_Gray_Sweep_Step, &Vertical_Gray_Test_Pixel, &Vertical_Gray_Set_Pixel }; /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /******** ********/ /******** Horizontal Bitmap Sweep Routines ********/ /******** ********/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /* */ /* */ /* Horizontal_Sweep_Init */ /* */ /* */ /* Initializes the horizontal bitmap sweep. Called by the generic */ /* sweep/draw routine before its loop. */ /* */ /* */ /* min :: The address of the current minimum pixel column. */ /* max :: The address of the current maximum pixel column. */ /* */ static void Horizontal_Gray_Sweep_Init( RAS_ARG_ int* min, int* max ) { UNUSED( ras ); UNUSED( min ); UNUSED( max ); /* nothing, really */ } /*************************************************************************/ /* */ /* */ /* Horizontal_Gray_Sweep_Span */ /* */ /* */ /* Draws a single vertical bitmap span during the horizontal bitmap */ /* sweep. */ /* */ /* */ /* y :: The current scanline. */ /* x1 :: The left span edge. */ /* x2 :: The right span edge. */ /* */ static void Horizontal_Gray_Sweep_Span( RAS_ARG_ TScan y, TPos x1, TPos x2 ) { TPos e1, e2; int shift = ras.precision_bits - 6; int incr; PByte bits; Byte b; UNUSED( y ); x1 += ras.precision_half; x2 += ras.precision_half; #ifdef FT_RASTER_OPTION_CONTRAST if (x2-x1 < ras.precision) { x1 = ((x1+x2) >> 1) - ras.precision_half; x2 = x1 + ras.precision; } #endif e1 = TRUNC( x1 ); e2 = TRUNC( x2 ); if ( e1 <= e2 && e2 >= 0 && e1 < ras.bit_width ) { x1 = FRAC(x1) >> shift; x2 = FRAC(x2) >> shift; if ( e1 < 0 ) { e1 = 0; x1 = 0; } if ( e2 >= ras.bit_width ) { e2 = ras.bit_width; x2 = 0; } incr = -ras.target.pitch; bits = ras.bit_buffer + y; bits += incr * e1; if (incr < 0) bits -= incr*(ras.target.rows-1); e2 -= e1; if ( e2 > 0 ) { b = bits[0]; if (b < 127) b++; bits[0] = (64-x1) + (b >> 1); if ( ras.precision > 64 ) { e2--; while (e2 > 0) { bits += incr; b = bits[0]; if (b < 127) bits[0] = (Byte)(63+((b+1) >> 1)); e2--; } } else bits += incr*(e2-1); if (x2) { bits += incr; b = bits[0]; if (b < 127) b++; bits[0] = (Byte)(x2 + (b >> 1)); } } else { b = bits[0]; if (b < 127) b++; bits[0] = (Byte)((b >> 1)+(x2-x1)); } } } /*************************************************************************/ /* */ /* */ /* Horizontal_Gray_Test_Pixel */ /* */ /* */ /* Tests a pixel `light' during the horizontal bitmap sweep. Used */ /* during drop-out control only. */ /* */ /* */ /* y :: The current pixel column. */ /* x :: The current row/scanline. */ /* */ static int Horizontal_Gray_Test_Pixel( RAS_ARG_ int y, int x ) { #if 0 unsigned char* pixel = (unsigned char*)ras.bit_buffer + y; if ( ras.target.flow == Flow_Down ) pixel += (ras.target.rows-1 - x) * ras.target.cols; else pixel += x * ras.target.cols; return ( x >= 0 && x < ras.target.rows && *pixel >= 64 ); #else UNUSED(y); UNUSED(x); return 0; #endif } /*************************************************************************/ /* */ /* */ /* Horizontal_Set_Pixel */ /* */ /* */ /* Sets a single pixel in a bitmap during the horizontal sweep. Used */ /* during drop-out control. */ /* */ /* */ /* y :: The current pixel column. */ /* x :: The current row/scanline. */ /* color :: Ignored by this function. */ /* */ static void Horizontal_Gray_Set_Pixel( RAS_ARG_ int y, int x, int color ) { unsigned char* pixel = (unsigned char*)ras.bit_buffer + y; if ( x >= 0 && x < ras.target.rows ) { long pitch = ras.target.pitch; pixel -= pitch*x; if (pitch > 0) pixel += pitch*(ras.target.rows-1); color += *pixel; if (color < 64) color = 64; /* *pixel = (color >= 127 ? 127 : (unsigned char)color ); */ } } static void Gray_Ignore( void ) { ; } static const Raster_Render horizontal_render_gray = { &Horizontal_Gray_Sweep_Init, &Horizontal_Gray_Sweep_Span, (Function_Sweep_Step) &Gray_Ignore, &Horizontal_Gray_Test_Pixel, &Horizontal_Gray_Set_Pixel, }; #endif /* FT_RASTER_OPTION_ANTI_ALIAS */ /*************************************************************************/ /* */ /* A technical note to explain how the scanline sweep is performed: */ /* */ /* The function Draw_Sweep() is used to sweep the scanlines of the */ /* target bitmap or pixmap. For each scanline, it must do the */ /* following: */ /* */ /* - Get the set of all outline intersections for the current */ /* scanline. */ /* */ /* - Sort these intersections (in increasing order). */ /* */ /* - Pair intersections to create spans (horizontal pixel segments) */ /* that are then `drawn' by calling a `sweep_span' function. */ /* */ /* - Check for dropouts: If a span is too small to be drawn, it must */ /* be re-adjusted in order to make it visible again. */ /* */ /* The sweep starts from the bottom of the outline (ymin) and goes */ /* upwards (to ymax). Thus, the function manages the following: */ /* */ /* - A linked list of the profiles which are above the current */ /* scanline. It is called the `wait' list as it contains all the */ /* profiles waiting to be `activated' during the sweep. It contains */ /* all profiles initially. */ /* */ /* - A linked list of the profiles covering the current scanline, */ /* i.e., all the profiles that contain an intersection for the */ /* current scanline. It is called the `draw' list. */ /* */ /* A profile travels from the wait list to the draw list if the */ /* current scanline reaches its bottom border (its ymin). It is also */ /* removed from the draw list (and becomes unlisted) when the current */ /* scanline reaches the scanline above its upper border (its ymax). */ /* */ /* These positions correspond to the `extrema' table built by */ /* Finalize_Profile_Table(). */ /* */ /* The draw list is always sorted in increasing order of the X */ /* coordinates. We use a bubble sort because it is easy to implement */ /* on a linked list, and because in 95% cases, the list is already */ /* correctly sorted when going from one scanline to the other. */ /* */ /* The extrema table gives the scanline coordinates at which at least */ /* one profile must be removed from the `draw' list, or another one */ /* must be moved from the `wait' to `draw' lists. */ /* */ /* Note that when a dropout is detected, the corresponding span is not */ /* drawn immediately but kept on a temporary list. All dropout spans */ /* are drawn after the regular spans on a given scanline. This is a */ /* requirement of the TrueType specification to properly implement */ /* some drop-out control modes -- yes, it's weird! */ /* */ /* Finally, the parser contains four function pointers that are called */ /* by Draw_Sweep(). Each rendering mode (monochrome, anti-aliased-5, */ /* and anti-aliased-17) provide its own set of such functions. These */ /* are: */ /* */ /* sweep_init: Called only when the sweep starts. Used to set */ /* up some variables. */ /* */ /* sweep_span: Used to draw a horizontal span on the current */ /* scanline. */ /* */ /* sweep_test_pixel: Used to test a pixel's intensity, as it is */ /* required for drop-out control. */ /* */ /* sweep_put_pixel: Used to write a single pixel when a drop-out */ /* needs to be lighted/drawn. */ /* */ /*************************************************************************/ /*************************************************************************/ /* */ /* Generic Sweep Drawing routine */ /* */ static TResult Draw_Sweep( RAS_ARG ) { TScan y, y_change, y_height; PProfile P, Q, P_Left, P_Right; TScan min_Y, max_Y, top, bottom, dropouts; TPos x1, x2, e1, e2; TProfileList wait; TProfileList draw; /* Init empty linked lists */ Init_Linked( &wait ); Init_Linked( &draw ); /* first, compute min and max Y -- and add profiles to the wait list */ P = ras.start_prof; max_Y = TRUNC( ras.minY ); min_Y = TRUNC( ras.maxY ); while ( P ) { Q = P->link; bottom = P->start; top = P->start + P->height-1; if ( min_Y > bottom ) min_Y = bottom; if ( max_Y < top ) max_Y = top; P->X = 0; InsNew( &wait, P ); P = Q; } /* Check the extrema table */ if ( ras.n_extrema == 0 ) { ras.error = ErrRaster_Invalid_Outline; return FAILURE; } /* Now inits the sweep */ PTRACE2(( "draw_sweep: initialize sweep\n" )); ras.render.init( RAS_VAR_ &min_Y, &max_Y ); PTRACE2(( " init min_y = %d, max_y = %d\n", min_Y, max_Y )); /* Then compute the distance of each profile from min_Y */ P = wait; while ( P ) { P->countL = P->start - min_Y; P = P->link; } /* Let's go */ y = min_Y; y_height = 0; if ( ras.n_extrema > 0 && ras.pool_size[-ras.n_extrema] == min_Y ) ras.n_extrema--; PTRACE2(( "starting loop with n_extrema = %d", ras.n_extrema )); while ( ras.n_extrema > 0 ) { PProfile prof = wait; /* look in the wait list for new activations */ while ( prof ) { PProfile next = prof->link; prof->countL -= y_height; if ( prof->countL == 0 ) { /* move the profile from the wait list to the draw list */ DelOld( &wait, prof ); InsNew( &draw, prof ); } prof = next; } /* Sort the draw list */ Sort( &draw ); /* compute next y extremum scanline; we won't change the */ /* elements of the wait and draw lists until there */ y_change = ras.pool_size[-ras.n_extrema--]; y_height = y_change - y; PTRACE2(( ">>> y = %d, y_change = %d, y_height = %d", y, y_change, y_height )); while ( y < y_change ) { int window; PProfile left; /* Let's trace */ dropouts = 0; /* skip to next line if there is no active profile there */ if ( !draw ) goto Next_Line; left = draw; window = left->flow; prof = left->link; PTRACE2(( ">>> line y = %d", y )); while ( prof ) { PProfile next = prof->link; window += prof->flow; if ( window == 0 ) { x1 = left->X; x2 = prof->X; if ( x1 > x2 ) { TPos xs = x1; x1 = x2; x2 = xs; } if ( x2 - x1 <= ras.precision && ras.dropout_mode ) { e1 = CEILING( x1 ); e2 = FLOOR( x2 ); if ( e1 > e2 || e2 == e1 + ras.precision ) { /* a drop out was detected */ left->X = x1; prof->X = x2; /* mark profiles for drop-out processing */ left->countL = 1; prof->countL = 2; dropouts++; goto Skip_To_Next; } } PTRACE2(( "drawing span ( y=%d, x1=%d, x2=%d )", y, x1, x2 )); ras.render.span( RAS_VAR_ y, x1, x2 ); Skip_To_Next: left = next; } prof = next; } /* now perform the dropouts _after_ the span drawing */ /* drop-outs processing has been moved out of the loop */ /* for performance tuning */ if ( dropouts > 0 ) goto Scan_DropOuts; Next_Line: ras.render.step( RAS_VAR ); y++; if ( y < y_change ) Sort( &draw ); PTRACE2(( "line sorted for next operation" )); } /* Now finalize the profiles that needs it */ PTRACE2(( "finalizing profiles..." )); { PProfile prof, next; prof = draw; while ( prof ) { next = prof->link; if (prof->height == 0) DelOld( &draw, prof ); prof = next; } } PTRACE2(( "profiles finalized for this run" )); } /* for gray-scaling, flushes the bitmap scanline cache */ while ( y <= max_Y ) { ras.render.step( RAS_VAR ); y++; } return SUCCESS; Scan_DropOuts : P_Left = draw; while ( dropouts > 0 ) { TPos e1, e2; PProfile left, right; while ( P_Left->countL != 1 ) P_Left = P_Left->link; P_Right = P_Left->link; while ( P_Right->countL != 2 ) P_Right = P_Right->link; P_Left->countL = 0; P_Right->countL = 0; /* Now perform the dropout control */ x1 = P_Left->X; x2 = P_Right->X; left = ( ras.flipped ? P_Right : P_Left ); right = ( ras.flipped ? P_Left : P_Right ); PTRACE2(( "performing drop-out control ( x1= %d, x2 = %d )", x1, x2 )); e1 = CEILING( x1 ); e2 = FLOOR ( x2 ); if ( e1 > e2 ) { if ( e1 == e2 + ras.precision ) { switch ( ras.dropout_mode ) { case 1: e1 = e2; break; case 4: e1 = CEILING( (x1 + x2 + 1) / 2 ); break; case 2: case 5: /* Drop-out Control Rule #4 */ /* The spec is not very clear regarding rule #4. It */ /* presents a method that is way too costly to implement */ /* while the general idea seems to get rid of `stubs'. */ /* */ /* Here, we only get rid of stubs recognized when: */ /* */ /* upper stub: */ /* */ /* - P_Left and P_Right are in the same contour */ /* - P_Right is the successor of P_Left in that contour */ /* - y is the top of P_Left and P_Right */ /* */ /* lower stub: */ /* */ /* - P_Left and P_Right are in the same contour */ /* - P_Left is the successor of P_Right in that contour */ /* - y is the bottom of P_Left */ /* */ /* upper stub test */ if ( ( left->next == right && left->height <= 0 ) || /* lower stub test */ ( right->next == left && left->start == y ) || /* check that the rightmost pixel isn't set */ ras.render.test_pixel( RAS_VAR_ y, TRUNC(e1)) ) goto Next_Dropout; if ( ras.dropout_mode == 2 ) e1 = e2; else e1 = CEILING( (x1 + x2 + 1)/2 ); break; default: goto Next_Dropout; /* Unsupported mode */ } } else goto Next_Dropout; } PTRACE2(( " -> setting pixel" )); ras.render.set_pixel( RAS_VAR_ y, TRUNC( e1 ), (x2 - x1) >> ras.scale_shift ); Next_Dropout: dropouts--; } goto Next_Line; } /*************************************************************************/ /* */ /* */ /* Render_Single_Pass */ /* */ /* */ /* Performs one sweep with sub-banding. */ /* */ /* */ /* flipped :: whether or not we have to flip. */ /* */ /* */ /* Error code. 0 means success. */ /* */ static int Render_Single_Pass( RAS_ARG_ int flipped ) { TBand* band; ras.flipped = flipped; band = ras.band_stack; PTRACE2(( "raster: entering render_single_pass (flipped = %d)\n", flipped )); while ( band >= ras.band_stack ) { ras.maxY = ((long)band[0].y_max << (ras.scale_shift+6)) - 1; ras.minY = (long)band[0].y_min << (ras.scale_shift+6); ras.cursor = ras.pool; ras.error = 0; PTRACE2(( "raster: band = [ %d, %d ]\n", band[0].y_min, band[0].y_max )); if ( Convert_Glyph( RAS_VAR_ ras.outline ) ) { int bottom, top, half; if ( ras.error != ErrRaster_Overflow ) return FAILURE; ras.error = ErrRaster_Ok; PTRACE2(( "conversion failure, performing sub-banding\n" )); /* sub-banding */ #ifdef DEBUG_RASTER ClearBand( RAS_VAR_ TRUNC( ras.minY ), TRUNC( ras.maxY ) ); #endif bottom = band[0].y_min; top = band[0].y_max; half = ( top - bottom ) >> 1; if ( band >= ras.band_stack + 7 || half == 0 ) { ras.band_top = 0; ras.error = ErrRaster_Invalid_Outline; return ras.error; } band[1].y_min = bottom + half; band[1].y_max = top; band[0].y_max = bottom + half; band ++; } else { PTRACE2(( "conversion succeeded, span drawing sweep\n" )); if ( ras.start_prof ) if ( Draw_Sweep( RAS_VAR ) ) return ras.error; band --; } } PTRACE2(( "raster: exiting render_single_pass\n" )); return SUCCESS; /* success */ } static int Raster_Render1( FT_Raster raster ) { int error; if ( ras.target.width > ABS(ras.target.pitch)*8 ) return ErrRaster_Invalid_Map; ras.scale_shift = ras.precision_bits - 6; ras.scale_delta = ras.precision_half; /* Vertical Sweep */ ras.band_top = 0; ras.band_stack[0].y_min = 0; ras.band_stack[0].y_max = ras.target.rows; ras.render = vertical_render_mono; ras.bit_width = ras.target.width; ras.bit_buffer = (unsigned char*)ras.target.buffer; if ( (error = Render_Single_Pass( RAS_VAR_ 0 )) != 0 ) return error; /* Horizontal Sweep */ if ( ras.second_pass && ras.dropout_mode != 0 ) { ras.render = horizontal_render_mono; ras.band_top = 0; ras.band_stack[0].y_min = 0; ras.band_stack[0].y_max = ras.target.width; if ( (error = Render_Single_Pass( RAS_VAR_ 1 )) != 0 ) return error; } return ErrRaster_Ok; } #ifdef FT_RASTER_OPTION_ANTI_ALIAS static int Raster_Render8( FT_Raster raster ) { int error; if ( ras.target.width > ABS(ras.target.pitch) ) return ErrRaster_Invalid_Map; /* Vertical Sweep */ ras.band_top = 0; ras.band_stack[0].y_min = 0; ras.band_stack[0].y_max = ras.target.rows; ras.scale_shift = (ras.precision_bits-6); ras.scale_delta = ras.precision_half; ras.dropout_mode = 2; ras.render = vertical_render_gray; ras.bit_width = ras.target.width; ras.bit_buffer = (unsigned char*)ras.target.buffer; ras.pix_buffer = (unsigned char*)ras.target.buffer; error = Render_Single_Pass( RAS_VAR_ 0 ); if ( error ) return error; #if 1 /* Horizontal Sweep */ ras.render = horizontal_render_gray; ras.band_top = 0; ras.bit_width = ras.target.rows; ras.band_stack[0].y_min = 0; ras.band_stack[0].y_max = ras.target.width; return Render_Single_Pass( RAS_VAR_ 1 ); #else return 0; #endif } #else /* FT_RASTER_OPTION_ANTI_ALIAS */ static int Raster_Render8( FT_Raster raster ) { return ErrRaster_Unimplemented; } #endif /* FT_RASTER_OPTION_ANTI_ALIAS */ /*************************************************************************/ /* */ /* */ /* FT_Raster_Render */ /* */ /* */ /* Renders an outline into a target bitmap. */ /* */ /* */ /* raster :: A handle to the raster object used during rendering. */ /* outline :: A pointer to the source outline record/object. */ /* bitmap :: A pointer to the target bitmap descriptor. */ /* */ /* */ /* Error code, interpreted as a FT_Error by FreeType. 0 means */ /* success. */ /* */ EXPORT_FUNC int FT_Raster_Render( FT_Raster raster, FT_Outline* outline, FT_Bitmap* target_map ) { if ( !raster || !raster->pool || !raster->pool_size ) return ErrRaster_Uninitialized_Object; /* return immediately if the outline is empty */ if ( outline->n_points == 0 || outline->n_contours <= 0 ) return ErrRaster_Ok; if ( !outline || !outline->contours || !outline->points ) return ErrRaster_Invalid_Outline; if ( outline->n_points != outline->contours[outline->n_contours - 1] + 1 ) return ErrRaster_Invalid_Outline; if ( !target_map || !target_map->buffer ) return ErrRaster_Invalid_Map; ras.outline = outline; ras.target = *target_map; ras.dropout_mode = outline->dropout_mode; ras.second_pass = outline->second_pass; SET_High_Precision( outline->high_precision ); switch ( target_map->pixel_mode ) { case ft_pixel_mode_mono: return Raster_Render1( raster ); case ft_pixel_mode_grays: return Raster_Render8( raster ); default: return ErrRaster_Unimplemented; } } #ifdef FT_RASTER_OPTION_ANTI_ALIAS /*************************************************************************/ /* */ /* */ /* Reset_Palette_5 */ /* */ /* */ /* Resets lookup table when the 5-gray-levels palette changes. */ /* */ static void Reset_Palette_5( RAS_ARG ) { #ifdef FT_RASTER_ANY_ENDIAN int i, j, l, c; for ( i = 0; i < 256; i++ ) { l = 0; j = i; for ( c = 0; c < 4; c++ ) { l <<= 4; if ( j & 0x80 ) l++; if ( j & 0x40 ) l++; j = ( j << 2 ) & 0xFF; } ras.count_table[i] = l; } #else /* FT_RASTER_ANY_ENDIAN */ int i; for ( i = 0; i < 256; i++ ) { int cnt1, cnt2; cnt1 = ((i & 128) >> 7) + ((i & 64) >> 6) + ((i & 8) >> 3) + ((i & 4) >> 2); cnt2 = ((i & 32) >> 5) + ((i & 16) >> 4) + ((i & 2) >> 1) + (i & 1); /* */ /* Note that when the endianess isn't specified through one of the */ /* configuration, we use the big-endian storage in `count_table' */ /* */ #if defined( FT_RASTER_LITTLE_ENDIAN ) ras.count_table[i] = (ras.grays[cnt2] << 8) | ras.grays[cnt1]; #else ras.count_table[i] = (ras.grays[cnt1] << 8) | ras.grays[cnt2]; #endif } #endif /* FT_RASTER_ANY_ENDIAN */ } /*************************************************************************/ /* */ /* */ /* Reset_Palette_17 */ /* */ /* */ /* Resets lookup table when 17-gray-levels palette changes. */ /* */ #ifdef FT_RASTER_ANTI_ALIAS_17 static void Reset_Palette_17( RAS_ARG ) { int i; for ( i = 0; i < 256; i++ ) ras.count_table[i] = ((i & 128) >> 7) + ((i & 64) >> 6) + ((i & 8) >> 3) + ((i & 4) >> 2) + ((i & 32) >> 5) + ((i & 16) >> 4) + ((i & 2) >> 1) + (i & 1); } #endif /* FT_RASTER_ANTI_ALIAS_17 */ #endif /* TT_RASTER_OPTION_ANTI_ALIAS */ /*************************************************************************/ /* */ /* */ /* FT_Raster_ObjSize */ /* */ /* */ /* This function returns the size of a raster object in bytes. */ /* Client applications are thus able to allocate objects in their own */ /* heap/memory space, without revealing the internal structures of */ /* the scan-line converter. */ /* */ /* */ /* The size in bytes of a single raster object. */ /* */ EXPORT_FUNC long FT_Raster_ObjSize( void ) { return (long)sizeof( struct FT_RasterRec_ ); } /*************************************************************************/ /* */ /* */ /* FT_Raster_Init */ /* */ /* */ /* Initializes a fresh raster object which should have been allocated */ /* by client applications. This function is also used to set the */ /* object's render pool. It can be used repeatedly on a single */ /* object if one wants to change the pool's address or size. */ /* */ /* Note that the render pool has no state and is only used during a */ /* call to FT_Raster_Render(). It is thus theorically possible to */ /* share it between several non-concurrent components of your */ /* applications when memory is a scarce resource. */ /* */ /* */ /* pool_size :: The render pool's size in bytes. This must be at */ /* least 4 kByte. */ /* */ /* */ /* raster :: A handle to the target raster object. */ /* */ /* pool_base :: The render pool's base address in memory. */ /* */ /* */ /* An error condition, used as a FT_Error in the FreeType library. */ /* 0 means success. */ /* */ EXPORT_FUNC int FT_Raster_Init( FT_Raster raster, const char* pool_base, long pool_size ) { /* static const char default_palette[5] = { 0, 1, 2, 3, 4 }; */ /* check the object address */ if ( !raster ) return ErrRaster_Uninitialized_Object; /* check the render pool - we won't go under 4 Kb */ if ( !pool_base || pool_size < 4096 ) return ErrRaster_Invalid_Pool; /* save the pool */ raster->pool = (PPos)pool_base; raster->pool_size = raster->pool + pool_size / sizeof ( TPos ); #ifdef FT_RASTER_OPTION_ANTI_ALIAS raster->gray_width = ANTI_ALIAS_BUFFER_SIZE/2; /* clear anti-alias intermediate lines */ { char* p = raster->gray_lines; char* limit = p + ANTI_ALIAS_BUFFER_SIZE; do *p++ = 0; while ( p < limit ); } #endif #if 0 /* set the default palette: 5 levels = 0, 1, 2, 3, and 4 */ FT_Raster_SetPalette( raster, 5, default_palette ); #endif return ErrRaster_Ok; } /*************************************************************************/ /* */ /* */ /* FT_Raster_SetPalette */ /* */ /* */ /* Sets the pixmap rendering palette. Anti-aliasing modes are */ /* implemented/possible, they differ from the number of entries in */ /* the palette. */ /* */ /* */ /* count :: The number of palette entries. Valid values are 2, 5, */ /* and 17, which are the number of intermediate gray */ /* levels supported. */ /* */ /* palette :: An array of `count' chars giving the 8-bit palette of */ /* intermediate `gray' levels for anti-aliased rendering. */ /* */ /* In all modes, palette[0] corresponds to the background, */ /* while palette[count-1] to the foreground. Hence, a */ /* count of 2 corresponds to no anti-aliasing; a count of */ /* 5 uses 3 intermediate levels between the background and */ /* foreground, while a count of 17 uses 15 of them. */ /* */ /* */ /* An error code, used as a FT_Error by the FreeType library. */ /* */ /* */ /* By default, a new object uses mode 5, with a palette of 0, 1, 2, */ /* 3, and 4. You don't need to set the palette if you don't need to */ /* render pixmaps. */ /* */ EXPORT_FUNC int FT_Raster_SetPalette( FT_Raster raster, int count, const char* palette ) { switch ( count ) { #ifdef FT_RASTER_OPTION_ANTI_ALIAS /******************************/ /* The case of 17 gray levels */ /******************************/ case 17: #ifdef FT_RASTER_ANTI_ALIAS_17 { int n; raster->grays_count = count; for ( n = 0; n < count; n++ ) raster->grays[n] = (unsigned char)palette[n]; Reset_Palette_17( RAS_VAR ); break; } #else return ErrRaster_Unimplemented; #endif /*****************************/ /* The case of 5 gray levels */ /*****************************/ case 5: #ifdef FT_RASTER_ANTI_ALIAS_5 { int n; raster->grays_count = count; for ( n = 0; n < count; n++ ) raster->grays[n] = (unsigned char)palette[n]; Reset_Palette_5( RAS_VAR ); break; } #else return ErrRaster_Unimplemented; #endif #endif /* FT_RASTER_OPTION_ANTI_ALIAS */ default: return ErrRaster_Bad_Palette_Count; } return ErrRaster_Ok; } FT_Raster_Interface ft_default_raster = { sizeof( struct FT_RasterRec_ ), ft_glyph_format_outline, (FT_Raster_Init_Proc) FT_Raster_Init, (FT_Raster_Set_Mode_Proc) 0, (FT_Raster_Render_Proc) FT_Raster_Render }; /* END */