ref: 488cd71e4ba08cd5b1d6076401323e9bb6ee25fd
dir: /src/base/ftraster.c/
/***************************************************************************/ /* */ /* ftraster.c */ /* */ /* The FreeType glyph rasterizer (body). */ /* */ /* Copyright 1996-2000 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 <ftraster.h> #ifndef _STANDALONE_ #include <ftconfig.h> #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. */ /* */ #undef 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 */ /*************************************************************************/ /* */ /* FT_RASTER_CONSTANT_PRECISION */ /* */ /* Define this configuration macro if you want to use a constant */ /* precision for the internal sub-pixel coordinates. Otherwise, the */ /* precision is either 64 or 1024 units per pixel, depending on the */ /* outline's "high_precision" flag.. */ /* */ /* This results in a speed boost, but the macro can be undefined if */ /* it results in rendering errors (mainly changed drop-outs).. */ /* */ #define FT_RASTER_CONSTANT_PRECISION /*************************************************************************/ /* */ /* FT_PRECISION_BITS */ /* */ /* When the macro FT_RASTER_CONSTANT_PRECISION is defined, this */ /* constant holds the number of bits used for the internal sub-pixels */ /* */ /* This number should be at least 6, but use at least 8 if you */ /* intend to generate small glyph images (use 6 for a printer, for */ /* example..) */ /* */ #define FT_PRECISION_BITS 8 /*************************************************************************/ /* */ /* FT_DYNAMIC_BEZIER_STEPS */ /* */ /* Set this macro to enable the bezier decomposition to be */ /* dynamically computed. This is interesting when the precision is */ /* constant, as it speeds things a bit while keeping a very good */ /* accuracy on the bezier intersections.. */ /* */ #define FT_DYNAMIC_BEZIER_STEPS #else /* _STANDALONE_ */ #include <freetype.h> #include <ftconfig.h> /*************************************************************************/ /* */ /* 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 #define FT_RASTER_CONSTANT_PRECISION #define FT_DYNAMIC_BEZIER_STEPS #define FT_PRECISION_BITS 8 #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) /*************************************************************************/ /* */ /* 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 INPUT_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 Byte, *PByte; typedef int TResult; /*************************************************************************/ /* */ /* 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; #ifdef FT_RASTER_CONSTANT_PRECISION #define PRECISION_BITS FT_PRECISION_BITS #define PRECISION (1 << PRECISION_BITS) #define PRECISION_MASK (-1L << PRECISION_BITS) #define PRECISION_HALF (PRECISION >> 1) #define PRECISION_JITTER (PRECISION >> 5) #define PRECISION_STEP PRECISION_HALF #else #define PRECISION_BITS ras.precision_bits #define PRECISION ras.precision #define PRECISION_MASK ras.precision_mask #define PRECISION_HALF ras.precision_half #define PRECISION_JITTER ras.precision_jitter #define PRECISION_STEP ras.precision_step #endif /*************************************************************************/ /* */ /* Compute lowest integer coordinate below a given value. */ /* */ #define FLOOR( x ) ( (x) & PRECISION_MASK ) /*************************************************************************/ /* */ /* Compute highest integer coordinate above a given value. */ /* */ #define CEILING( x ) ( ((x) + PRECISION - 1) & PRECISION_MASK ) /*************************************************************************/ /* */ /* Get integer coordinate of a given 26.6 or 22.10 `x' coordinate -- no */ /* rounding. */ /* */ #define TRUNC( x ) ( (signed long)(x) >> PRECISION_BITS ) /*************************************************************************/ /* */ /* Get the fractional part of a given coordinate. */ /* */ #define FRAC( x ) ( (x) & (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; #ifndef FT_RASTER_CONSTANT_PRECISION int precision_bits; /* precision related variables */ int precision; int precision_half; long precision_mask; int precision_shift; int precision_step; int precision_jitter; #endif 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 */ void* memory; 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 */ /* 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 */ }; #ifdef DEBUG_RASTER /*************************************************************************/ /* */ /* <Function> */ /* Pset */ /* */ /* <Description> */ /* Used for debugging only. Plots a point in VRAM during rendering */ /* (not afterwards). */ /* */ /* <Note> */ /* 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 / (PRECISION * 8) ); break; case FT_Flow_Down: o = Vio_ScanLineWidth * ( ras.cur_prof->start - ras.cursor + ras.cur_prof->offset ) + ( x / (PRECISION * 8) ); break; } if ( o > 0 ) Vio[o] |= (unsigned)0x80 >> ( (x/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 */ /*************************************************************************/ /* */ /* <Function> */ /* Set_High_Precision */ /* */ /* <Description> */ /* Sets precision variables according to the parameter flag. */ /* */ /* <Input> */ /* High :: Set to True for high precision (typically for ppem < 18), */ /* false otherwise. */ /* */ static void Set_High_Precision( RAS_ARG_ char High ) { #ifdef FT_RASTER_CONSTANT_PRECISION (void)High; (void)&ras; #else 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 = 1L << ras.precision_bits; ras.precision_half = ras.precision / 2; ras.precision_shift = ras.precision_bits - INPUT_BITS; ras.precision_mask = -ras.precision; #endif } /*************************************************************************/ /* */ /* 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. */ /* */ /*************************************************************************/ /*************************************************************************/ /* */ /* <Function> */ /* New_Profile */ /* */ /* <Description> */ /* Creates a new Profile in the render pool. */ /* */ /* <Input> */ /* aState :: The state/orientation of the new profile. */ /* */ /* <Return> */ /* 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; } /*************************************************************************/ /* */ /* <Function> */ /* End_Profile */ /* */ /* <Description> */ /* 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. */ /* */ /* <Return> */ /* 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; } /*************************************************************************/ /* */ /* <Function> */ /* Insert_Extrema */ /* */ /* <Description> */ /* 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. */ /* */ /* <Input> */ /* 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; } /*************************************************************************/ /* */ /* <Function> */ /* Finalize_Profile_Table */ /* */ /* <Description> */ /* Adjusts all links in the profiles list. Called when the outline */ /* parsing is done. */ /* */ /* <Return> */ /* 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; } /*************************************************************************/ /* */ /* <Function> */ /* Line_Up */ /* */ /* <Description> */ /* Computes the scan-line intersections of an ascending line segment */ /* and stores them in the render pool. */ /* */ /* <Input> */ /* 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. */ /* */ /* <Return> */ /* 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, 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 <<= 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; } /*************************************************************************/ /* */ /* <Function> */ /* Line_Down */ /* */ /* <Description> */ /* Computes the scan-line intersections of a descending line segment */ /* and stores them in the render pool. */ /* */ /* <Input> */ /* 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. */ /* */ /* <Return> */ /* 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 ); #ifdef FT_DYNAMIC_BEZIER_STEPS static TPos Dynamic_Bezier_Threshold( RAS_ARG_ int degree, TPoint* arc ) { TPos min_x, max_x, min_y, max_y, A, B; TPos wide_x, wide_y, threshold; TPoint* cur = arc; TPoint* limit = cur + degree; /* first of all, set the threshold to the maximum x or y extent */ min_x = max_x = arc[0].x; min_y = max_y = arc[0].y; cur++; for ( ; cur < limit; cur++ ) { TPos x = cur->x; TPos y = cur->y; if ( x < min_x ) min_x = x; if ( x > max_x ) max_x = x; if ( y < min_y ) min_y = y; if ( y > max_y ) max_y = y; } wide_x = (max_x - min_x) << 4; wide_y = (max_y - min_y) << 4; threshold = wide_x; if (threshold < wide_y) threshold = wide_y; /* now compute the second and third order error values */ wide_x = arc[0].x + arc[1].x - arc[2].x*2; wide_y = arc[0].y + arc[1].y - arc[2].y*2; if (wide_x < 0) wide_x = -wide_x; if (wide_y < 0) wide_y = -wide_y; A = wide_x; if ( A < wide_y ) A = wide_y; if (degree >= 3) { wide_x = arc[3].x - arc[0].x + 3*(arc[2].x - arc[3].x); wide_y = arc[3].y - arc[0].y + 3*(arc[2].y - arc[3].y); if (wide_x < 0) wide_x = -wide_x; if (wide_y < 0) wide_y = -wide_y; B = wide_x; if ( B < wide_y ) B = wide_y; } else B = 0; while ( A > 0 || B > 0 ) { threshold >>= 1; A >>= 2; B >>= 3; } if (threshold < PRECISION_STEP) threshold = PRECISION_STEP; return threshold; } #endif /*************************************************************************/ /* */ /* <Function> */ /* Bezier_Up */ /* */ /* <Description> */ /* 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. */ /* */ /* <Input> */ /* miny :: The minimum vertical grid coordinate. */ /* maxy :: The maximum vertical grid coordinate. */ /* */ /* <Return> */ /* SUCCESS or FAILURE. */ /* */ static TResult Bezier_Up( RAS_ARG_ int degree, TSplitter splitter, TPos miny, TPos maxy ) { TPos y1, y2, e, e2, e0, threshold; 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 += 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; } #ifdef FT_DYNAMIC_BEZIER_STEPS /* compute dynamic bezier step threshold */ threshold = Dynamic_Bezier_Threshold( RAS_VAR_ degree, arc ); #else threshold = PRECISION_STEP; #endif 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 >= e + PRECISION || y2 - y1 >= threshold ) { /* 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 += 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 += PRECISION; /* go to next scanline */ } arc -= degree; /* pop the arc */ } } Fin: ras.cursor = top; ras.arc -= degree; return SUCCESS; } /*************************************************************************/ /* */ /* <Function> */ /* Bezier_Down */ /* */ /* <Description> */ /* 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. */ /* */ /* <Input> */ /* miny :: The minimum vertical grid coordinate. */ /* maxy :: The maximum vertical grid coordinate. */ /* */ /* <Return> */ /* 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 /*************************************************************************/ /* */ /* <Function> */ /* Split_Conic */ /* */ /* <Description> */ /* Subdivides one second-order Bezier arc into two joint sub-arcs in */ /* the Bezier stack. */ /* */ /* <Note> */ /* 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 + 1 ) >> 1; b = base[1].x = ( base[0].x + b + 1 ) >> 1; base[2].x = ( a + b + 1 ) >> 1; base[4].y = base[2].y; b = base[1].y; a = base[3].y = ( base[2].y + b + 1 ) >> 1; b = base[1].y = ( base[0].y + b + 1 ) >> 1; base[2].y = ( a + b ) / 2; } /*************************************************************************/ /* */ /* <Function> */ /* Push_Conic */ /* */ /* <Description> */ /* Clears the Bezier stack and pushes a new arc on top of it. */ /* */ /* <Input> */ /* p2 :: A pointer to the second (control) point. */ /* p3 :: A pointer to the third (end) point. */ /* */ /* <Note> */ /* 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 /*************************************************************************/ /* */ /* <Function> Split_Cubic */ /* */ /* <Description> */ /* Subdivides a third-order Bezier arc into two joint sub-arcs in */ /* the Bezier stack. */ /* */ /* <Note> */ /* 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 + 1 ) >> 1; base[5].x = b = ( base[3].x + d + 1 ) >> 1; c = ( c + d + 1 ) >> 1; base[2].x = a = ( a + c + 1 ) >> 1; base[4].x = b = ( b + c + 1 ) >> 1; base[3].x = ( a + b + 1 ) >> 1; base[6].y = base[3].y; c = base[1].y; d = base[2].y; base[1].y = a = ( base[0].y + c + 1 ) >> 1; base[5].y = b = ( base[3].y + d + 1 ) >> 1; c = ( c + d + 1 ) >> 1; base[2].y = a = ( a + c + 1 ) >> 1; base[4].y = b = ( b + c + 1 ) >> 1; base[3].y = ( a + b + 1 ) >> 1; } /*************************************************************************/ /* */ /* <Function> */ /* Push_Cubic */ /* */ /* <Description> */ /* Clears the Bezier stack and pushes a new third-order Bezier arc on */ /* top of it. */ /* */ /* <Input> */ /* p2 :: A pointer to the second (control) point. */ /* p3 :: A pointer to the third (control) point. */ /* p4 :: A pointer to the fourth (end) point. */ /* */ /* <Note> */ /* 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 */ /*************************************************************************/ /* */ /* <Function> */ /* Check_Contour */ /* */ /* <Description> */ /* Performs some checks at contour closure. */ /* */ /* <Return> */ /* 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; } /*************************************************************************/ /* */ /* <Function> */ /* Move_To */ /* */ /* <Description> */ /* This function injects a new contour in the render pool. */ /* */ /* <Input> */ /* to :: A pointer to the contour's first point. */ /* raster :: A pointer to the current raster object. */ /* */ /* <Return> */ /* Error code. 0 means success. */ /* */ /* <Note> */ /* 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; } /*************************************************************************/ /* */ /* <Function> */ /* Line_To */ /* */ /* <Description> */ /* This function injects a new line segment in the render pool and */ /* adjusts the profiles list accordingly. */ /* */ /* <Input> */ /* to :: A pointer to the target position. */ /* raster :: A pointer to the current raster object. */ /* */ /* <Return> */ /* Error code. 0 means success. */ /* */ /* <Note> */ /* 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 /*************************************************************************/ /* */ /* <Function> */ /* Conic_To */ /* */ /* <Description> */ /* Injects a new conic Bezier arc and adjusts the profile list */ /* accordingly. */ /* */ /* <Input> */ /* control :: A pointer to an intermediate control point. */ /* to :: A pointer to the end point. */ /* raster :: A handle to the current raster object. */ /* */ /* <Return> */ /* Error code. 0 means success. */ /* */ /* <Note> */ /* 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 /*************************************************************************/ /* */ /* <Function> */ /* Cubic_To */ /* */ /* <Description> */ /* Injects a new cubic Bezier arc and adjusts the profile list */ /* accordingly. */ /* */ /* <Input> */ /* 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. */ /* */ /* <Return> */ /* Error code. 0 means success. */ /* */ /* <Note> */ /* 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 */ /*************************************************************************/ /* */ /* <Function> */ /* Convert_Glyph */ /* */ /* <Description> */ /* Converts a glyph into a series of segments and arcs and makes a */ /* profiles list with them. */ /* */ /* <InOut> */ /* outline :: The glyph outline. */ /* */ /* <Return> */ /* 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_Outline_Decompose( 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 ********/ /******** ********/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /* */ /* <Function> */ /* Vertical_Sweep_Init */ /* */ /* <Description> */ /* Initializes the vertical bitmap sweep. Called by the generic */ /* sweep/draw routine before its loop. */ /* */ /* <Input> */ /* 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); } /*************************************************************************/ /* */ /* <Function> */ /* Vertical_Sweep_Span */ /* */ /* <Description> */ /* Draws a single horizontal bitmap span during the vertical bitmap */ /* sweep. */ /* */ /* <Input> */ /* 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 - PRECISION <= 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)); 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 ); } } /*************************************************************************/ /* */ /* <Function> */ /* Vertical_Test_Pixel */ /* */ /* <Description> */ /* Tests a pixel `light' during the vertical bitmap sweep. Used */ /* during drop-out control only. */ /* */ /* <Input> */ /* 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)) ); } /*************************************************************************/ /* */ /* <Function> */ /* Vertical_Set_Pixel */ /* */ /* <Description> */ /* Sets a single pixel in a bitmap during the vertical sweep. Used */ /* during drop-out control. */ /* */ /* <Input> */ /* 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 ) ras.bit_buffer[ras.trace_bit+(x >> 3)] |= (char)(0x80 >> (x & 7)); } /*************************************************************************/ /* */ /* <Function> */ /* Vertical_Sweep_Step */ /* */ /* <Description> */ /* 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 ********/ /******** ********/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /* */ /* <Function> */ /* Horizontal_Sweep_Init */ /* */ /* <Description> */ /* Initializes the horizontal bitmap sweep. Called by the generic */ /* sweep/draw routine before its loop. */ /* */ /* <Input> */ /* 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 */ } /*************************************************************************/ /* */ /* <Function> */ /* Horizontal_Sweep_Span */ /* */ /* <Description> */ /* Draws a single vertical bitmap span during the horizontal bitmap */ /* sweep. Actually, this function is only used to check for weird */ /* drop-out cases. */ /* */ /* <Input> */ /* 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 < 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; } } } } /*************************************************************************/ /* */ /* <Function> */ /* Horizontal_Test_Pixel */ /* */ /* <Description> */ /* Tests a pixel `light' during the horizontal bitmap sweep. Used */ /* during drop-out control only. */ /* */ /* <Input> */ /* 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) ); } /*************************************************************************/ /* */ /* <Function> */ /* Horizontal_Set_Pixel */ /* */ /* <Description> */ /* Sets a single pixel in a bitmap during the horizontal sweep. Used */ /* during drop-out control. */ /* */ /* <Input> */ /* 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; } } /*************************************************************************/ /* */ /* <Function> */ /* Horizontal_Sweep_Step */ /* */ /* <Description> */ /* 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 /*************************************************************************/ /* */ /* <Function> */ /* Vertical_Gray_Sweep_Init */ /* */ /* <Description> */ /* Initializes the vertical bitmap sweep. Called by the generic */ /* sweep/draw routine before its loop. */ /* */ /* <Input> */ /* 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; } /*************************************************************************/ /* */ /* <Function> */ /* Vertical_Gray_Sweep_Span */ /* */ /* <Description> */ /* Draws a single horizontal bitmap span during the vertical bitmap */ /* sweep. */ /* */ /* <Input> */ /* 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 = PRECISION_BITS - 6; PByte target; UNUSED( y ); x1 += PRECISION_HALF; x2 += PRECISION_HALF; 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; } } } /*************************************************************************/ /* */ /* <Function> */ /* Vertical_Gray_Test_Pixel */ /* */ /* <Description> */ /* Tests a pixel `light' during the vertical bitmap sweep. Used */ /* during drop-out control only. */ /* */ /* <Input> */ /* 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 } /*************************************************************************/ /* */ /* <Function> */ /* Vertical_Gray_Set_Pixel */ /* */ /* <Description> */ /* Sets a single pixel in a bitmap during the vertical sweep. Used */ /* during drop-out control. */ /* */ /* <Input> */ /* 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 ); } } /*************************************************************************/ /* */ /* <Function> */ /* Vertical_Sweep_Step */ /* */ /* <Description> */ /* 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 ********/ /******** ********/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /* */ /* <Function> */ /* Horizontal_Sweep_Init */ /* */ /* <Description> */ /* Initializes the horizontal bitmap sweep. Called by the generic */ /* sweep/draw routine before its loop. */ /* */ /* <Input> */ /* 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 */ } /*************************************************************************/ /* */ /* <Function> */ /* Horizontal_Gray_Sweep_Span */ /* */ /* <Description> */ /* Draws a single vertical bitmap span during the horizontal bitmap */ /* sweep. */ /* */ /* <Input> */ /* 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 = PRECISION_BITS - 6; int incr; PByte bits; Byte b; UNUSED( y ); x1 += PRECISION_HALF; x2 += PRECISION_HALF; 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++; b = (Byte)((64-x1) + (b >> 1)); bits[0] = b; if ( e2 < 24 ) { 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++; b = (Byte)(x2 + (b >> 1)); bits[0] = b; } } else { b = bits[0]; if (b < 127) b++; b = (Byte)((b >> 1)+(x2-x1)); bits[0] = b; } } } /*************************************************************************/ /* */ /* <Function> */ /* Horizontal_Gray_Test_Pixel */ /* */ /* <Description> */ /* Tests a pixel `light' during the horizontal bitmap sweep. Used */ /* during drop-out control only. */ /* */ /* <Input> */ /* 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 } /*************************************************************************/ /* */ /* <Function> */ /* Horizontal_Set_Pixel */ /* */ /* <Description> */ /* Sets a single pixel in a bitmap during the horizontal sweep. Used */ /* during drop-out control. */ /* */ /* <Input> */ /* 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 <= PRECISION && ras.dropout_mode ) { e1 = CEILING( x1 ); e2 = FLOOR( x2 ); if ( e1 > e2 || e2 == e1 + 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 + 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; } /*************************************************************************/ /* */ /* <Function> */ /* Render_Single_Pass */ /* */ /* <Description> */ /* Performs one sweep with sub-banding. */ /* */ /* <Input> */ /* flipped :: whether or not we have to flip. */ /* */ /* <Returns> */ /* 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 << PRECISION_BITS) - 1; ras.minY = (long)band[0].y_min << PRECISION_BITS; 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 1 /* for debugging */ if ( ras.start_prof ) if ( Draw_Sweep( RAS_VAR ) ) return ras.error; #endif 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 = PRECISION_BITS - INPUT_BITS; ras.scale_delta = 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 = PRECISION_BITS - INPUT_BITS; ras.scale_delta = 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 */ /**** RASTER OBJECT CREATION : in standalone mode, we simply use *****/ /**** a static object .. *****/ #ifdef _STANDALONE_ static int ft_raster_new( void* memory, FT_Raster *araster ) { static FT_RasterRec_ the_raster; *araster = &the_raster; memset( &the_raster, sizeof(the_raster), 0 ); return 0; } static void ft_raster_done( FT_Raster raster ) { /* nothing */ raster->init = 0; } #else #include "ftobjs.h" static int ft_raster_new( FT_Memory memory, FT_Raster* araster ) { FT_Error error; FT_Raster raster; *araster = 0; if ( !ALLOC( raster, sizeof(*raster) )) { raster->memory = memory; *araster = raster; } return error; } static void ft_raster_done( FT_Raster raster ) { FT_Memory memory = (FT_Memory)raster->memory; FREE( raster ); } #endif static void ft_raster_reset( FT_Raster raster, const char* pool_base, long pool_size ) { if ( raster && pool_base && pool_size >= 4096 ) { /* save the pool */ raster->pool = (PPos)pool_base; raster->pool_size = raster->pool + pool_size / sizeof ( TPos ); } } static int ft_raster_render( FT_Raster raster, FT_Raster_Params* params ) { FT_Outline* outline = (FT_Outline*)params->source; FT_Bitmap* target_map = params->target; if ( !raster || !raster->pool || !raster->pool_size ) return ErrRaster_Uninitialized_Object; if ( !outline || !outline->contours || !outline->points ) return ErrRaster_Invalid_Outline; /* return immediately if the outline is empty */ if ( outline->n_points == 0 || outline->n_contours <= 0 ) return ErrRaster_Ok; 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; /* Note that we always use drop-out mode 2, because it seems that */ /* it's the only way to do to get results consistent with Windows */ /* rendering.. */ ras.dropout_mode = 2; ras.second_pass = (outline->flags & ft_outline_single_pass) == 0; SET_High_Precision( (char)((outline->flags & ft_outline_high_precision)!= 0) ); /* this version of the raster does not support direct rendering, sorry */ if ( params->flags & ft_raster_flag_direct ) return ErrRaster_Unimplemented; return ( params->flags & ft_raster_flag_aa ? Raster_Render8( raster ) : Raster_Render1( raster ) ); } FT_Raster_Funcs ft_default_raster = { ft_glyph_format_outline, (FT_Raster_New_Func) ft_raster_new, (FT_Raster_Reset_Func) ft_raster_reset, (FT_Raster_Set_Mode_Func) 0, (FT_Raster_Render_Func) ft_raster_render, (FT_Raster_Done_Func) ft_raster_done }; /* END */