-/***************************************************************************/\r
-/* */\r
-/* ftraster.c */\r
-/* */\r
-/* The FreeType glyph rasterizer (body). */\r
-/* */\r
-/* Copyright 1996-2001, 2002, 2003, 2005, 2007 by */\r
-/* David Turner, Robert Wilhelm, and Werner Lemberg. */\r
-/* */\r
-/* This file is part of the FreeType project, and may only be used, */\r
-/* modified, and distributed under the terms of the FreeType project */\r
-/* license, LICENSE.TXT. By continuing to use, modify, or distribute */\r
-/* this file you indicate that you have read the license and */\r
-/* understand and accept it fully. */\r
-/* */\r
-/***************************************************************************/\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* This file can be compiled without the rest of the FreeType engine, by */\r
- /* defining the _STANDALONE_ macro when compiling it. You also need to */\r
- /* put the files `ftimage.h' and `ftmisc.h' into the $(incdir) */\r
- /* directory. Typically, you should do something like */\r
- /* */\r
- /* - copy `src/raster/ftraster.c' (this file) to your current directory */\r
- /* */\r
- /* - copy `include/freetype/ftimage.h' and `src/raster/ftmisc.h' */\r
- /* to your current directory */\r
- /* */\r
- /* - compile `ftraster' with the _STANDALONE_ macro defined, as in */\r
- /* */\r
- /* cc -c -D_STANDALONE_ ftraster.c */\r
- /* */\r
- /* The renderer can be initialized with a call to */\r
- /* `ft_standard_raster.raster_new'; a bitmap can be generated */\r
- /* with a call to `ft_standard_raster.raster_render'. */\r
- /* */\r
- /* See the comments and documentation in the file `ftimage.h' for more */\r
- /* details on how the raster works. */\r
- /* */\r
- /*************************************************************************/\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* This is a rewrite of the FreeType 1.x scan-line converter */\r
- /* */\r
- /*************************************************************************/\r
-\r
-#ifdef _STANDALONE_\r
-\r
-#include "ftmisc.h"\r
-#include "ftimage.h"\r
-\r
-#else /* !_STANDALONE_ */\r
-\r
-#include <ft2build.h>\r
-#include "ftraster.h"\r
-#include FT_INTERNAL_CALC_H /* for FT_MulDiv only */\r
-\r
-#endif /* !_STANDALONE_ */\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* A simple technical note on how the raster works */\r
- /* ----------------------------------------------- */\r
- /* */\r
- /* Converting an outline into a bitmap is achieved in several steps: */\r
- /* */\r
- /* 1 - Decomposing the outline into successive `profiles'. Each */\r
- /* profile is simply an array of scanline intersections on a given */\r
- /* dimension. A profile's main attributes are */\r
- /* */\r
- /* o its scanline position boundaries, i.e. `Ymin' and `Ymax'. */\r
- /* */\r
- /* o an array of intersection coordinates for each scanline */\r
- /* between `Ymin' and `Ymax'. */\r
- /* */\r
- /* o a direction, indicating whether it was built going `up' or */\r
- /* `down', as this is very important for filling rules. */\r
- /* */\r
- /* 2 - Sweeping the target map's scanlines in order to compute segment */\r
- /* `spans' which are then filled. Additionally, this pass */\r
- /* performs drop-out control. */\r
- /* */\r
- /* The outline data is parsed during step 1 only. The profiles are */\r
- /* built from the bottom of the render pool, used as a stack. The */\r
- /* following graphics shows the profile list under construction: */\r
- /* */\r
- /* ____________________________________________________________ _ _ */\r
- /* | | | | | */\r
- /* | profile | coordinates for | profile | coordinates for |--> */\r
- /* | 1 | profile 1 | 2 | profile 2 |--> */\r
- /* |_________|___________________|_________|_________________|__ _ _ */\r
- /* */\r
- /* ^ ^ */\r
- /* | | */\r
- /* start of render pool top */\r
- /* */\r
- /* The top of the profile stack is kept in the `top' variable. */\r
- /* */\r
- /* As you can see, a profile record is pushed on top of the render */\r
- /* pool, which is then followed by its coordinates/intersections. If */\r
- /* a change of direction is detected in the outline, a new profile is */\r
- /* generated until the end of the outline. */\r
- /* */\r
- /* Note that when all profiles have been generated, the function */\r
- /* Finalize_Profile_Table() is used to record, for each profile, its */\r
- /* bottom-most scanline as well as the scanline above its upmost */\r
- /* boundary. These positions are called `y-turns' because they (sort */\r
- /* of) correspond to local extrema. They are stored in a sorted list */\r
- /* built from the top of the render pool as a downwards stack: */\r
- /* */\r
- /* _ _ _______________________________________ */\r
- /* | | */\r
- /* <--| sorted list of | */\r
- /* <--| extrema scanlines | */\r
- /* _ _ __________________|____________________| */\r
- /* */\r
- /* ^ ^ */\r
- /* | | */\r
- /* maxBuff sizeBuff = end of pool */\r
- /* */\r
- /* This list is later used during the sweep phase in order to */\r
- /* optimize performance (see technical note on the sweep below). */\r
- /* */\r
- /* Of course, the raster detects whether the two stacks collide and */\r
- /* handles the situation properly. */\r
- /* */\r
- /*************************************************************************/\r
-\r
-\r
- /*************************************************************************/\r
- /*************************************************************************/\r
- /** **/\r
- /** CONFIGURATION MACROS **/\r
- /** **/\r
- /*************************************************************************/\r
- /*************************************************************************/\r
-\r
- /* define DEBUG_RASTER if you want to compile a debugging version */\r
-#define xxxDEBUG_RASTER\r
-\r
- /* undefine FT_RASTER_OPTION_ANTI_ALIASING if you do not want to support */\r
- /* 5-levels anti-aliasing */\r
-#ifdef FT_CONFIG_OPTION_5_GRAY_LEVELS\r
-#define FT_RASTER_OPTION_ANTI_ALIASING\r
-#endif\r
-\r
- /* The size of the two-lines intermediate bitmap used */\r
- /* for anti-aliasing, in bytes. */\r
-#define RASTER_GRAY_LINES 2048\r
-\r
-\r
- /*************************************************************************/\r
- /*************************************************************************/\r
- /** **/\r
- /** OTHER MACROS (do not change) **/\r
- /** **/\r
- /*************************************************************************/\r
- /*************************************************************************/\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* The macro FT_COMPONENT is used in trace mode. It is an implicit */\r
- /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */\r
- /* messages during execution. */\r
- /* */\r
-#undef FT_COMPONENT\r
-#define FT_COMPONENT trace_raster\r
-\r
-\r
-#ifdef _STANDALONE_\r
-\r
-\r
- /* This macro is used to indicate that a function parameter is unused. */\r
- /* Its purpose is simply to reduce compiler warnings. Note also that */\r
- /* simply defining it as `(void)x' doesn't avoid warnings with certain */\r
- /* ANSI compilers (e.g. LCC). */\r
-#define FT_UNUSED( x ) (x) = (x)\r
-\r
- /* Disable the tracing mechanism for simplicity -- developers can */\r
- /* activate it easily by redefining these two macros. */\r
-#ifndef FT_ERROR\r
-#define FT_ERROR( x ) do ; while ( 0 ) /* nothing */\r
-#endif\r
-\r
-#ifndef FT_TRACE\r
-#define FT_TRACE( x ) do ; while ( 0 ) /* nothing */\r
-#define FT_TRACE1( x ) do ; while ( 0 ) /* nothing */\r
-#define FT_TRACE6( x ) do ; while ( 0 ) /* nothing */\r
-#endif\r
-\r
-#define Raster_Err_None 0\r
-#define Raster_Err_Not_Ini -1\r
-#define Raster_Err_Overflow -2\r
-#define Raster_Err_Neg_Height -3\r
-#define Raster_Err_Invalid -4\r
-#define Raster_Err_Unsupported -5\r
-\r
-#define ft_memset memset\r
-\r
-#else /* _STANDALONE_ */\r
-\r
-\r
-#include FT_INTERNAL_OBJECTS_H\r
-#include FT_INTERNAL_DEBUG_H /* for FT_TRACE() and FT_ERROR() */\r
-\r
-#include "rasterrs.h"\r
-\r
-#define Raster_Err_None Raster_Err_Ok\r
-#define Raster_Err_Not_Ini Raster_Err_Raster_Uninitialized\r
-#define Raster_Err_Overflow Raster_Err_Raster_Overflow\r
-#define Raster_Err_Neg_Height Raster_Err_Raster_Negative_Height\r
-#define Raster_Err_Invalid Raster_Err_Invalid_Outline\r
-#define Raster_Err_Unsupported Raster_Err_Cannot_Render_Glyph\r
-\r
-\r
-#endif /* _STANDALONE_ */\r
-\r
-\r
-#ifndef FT_MEM_SET\r
-#define FT_MEM_SET( d, s, c ) ft_memset( d, s, c )\r
-#endif\r
-\r
-#ifndef FT_MEM_ZERO\r
-#define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count )\r
-#endif\r
-\r
- /* FMulDiv means `Fast MulDiv'; it is used in case where `b' is */\r
- /* typically a small value and the result of a*b is known to fit into */\r
- /* 32 bits. */\r
-#define FMulDiv( a, b, c ) ( (a) * (b) / (c) )\r
-\r
- /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */\r
- /* for clipping computations. It simply uses the FT_MulDiv() function */\r
- /* defined in `ftcalc.h'. */\r
-#define SMulDiv FT_MulDiv\r
-\r
- /* The rasterizer is a very general purpose component; please leave */\r
- /* the following redefinitions there (you never know your target */\r
- /* environment). */\r
-\r
-#ifndef TRUE\r
-#define TRUE 1\r
-#endif\r
-\r
-#ifndef FALSE\r
-#define FALSE 0\r
-#endif\r
-\r
-#ifndef NULL\r
-#define NULL (void*)0\r
-#endif\r
-\r
-#ifndef SUCCESS\r
-#define SUCCESS 0\r
-#endif\r
-\r
-#ifndef FAILURE\r
-#define FAILURE 1\r
-#endif\r
-\r
-\r
-#define MaxBezier 32 /* The maximum number of stacked Bezier curves. */\r
- /* Setting this constant to more than 32 is a */\r
- /* pure waste of space. */\r
-\r
-#define Pixel_Bits 6 /* fractional bits of *input* coordinates */\r
-\r
-\r
- /*************************************************************************/\r
- /*************************************************************************/\r
- /** **/\r
- /** SIMPLE TYPE DECLARATIONS **/\r
- /** **/\r
- /*************************************************************************/\r
- /*************************************************************************/\r
-\r
- typedef int Int;\r
- typedef unsigned int UInt;\r
- typedef short Short;\r
- typedef unsigned short UShort, *PUShort;\r
- typedef long Long, *PLong;\r
- typedef unsigned long ULong;\r
-\r
- typedef unsigned char Byte, *PByte;\r
- typedef char Bool;\r
-\r
-\r
- typedef union Alignment_\r
- {\r
- long l;\r
- void* p;\r
- void (*f)(void);\r
-\r
- } Alignment, *PAlignment;\r
-\r
-\r
- typedef struct TPoint_\r
- {\r
- Long x;\r
- Long y;\r
-\r
- } TPoint;\r
-\r
-\r
- typedef enum TFlow_\r
- {\r
- Flow_None = 0,\r
- Flow_Up = 1,\r
- Flow_Down = -1\r
-\r
- } TFlow;\r
-\r
-\r
- /* States of each line, arc, and profile */\r
- typedef enum TStates_\r
- {\r
- Unknown_State,\r
- Ascending_State,\r
- Descending_State,\r
- Flat_State\r
-\r
- } TStates;\r
-\r
-\r
- typedef struct TProfile_ TProfile;\r
- typedef TProfile* PProfile;\r
-\r
- struct TProfile_\r
- {\r
- FT_F26Dot6 X; /* current coordinate during sweep */\r
- PProfile link; /* link to next profile - various purpose */\r
- PLong offset; /* start of profile's data in render pool */\r
- int flow; /* Profile orientation: Asc/Descending */\r
- long height; /* profile's height in scanlines */\r
- long start; /* profile's starting scanline */\r
-\r
- unsigned countL; /* number of lines to step before this */\r
- /* profile becomes drawable */\r
-\r
- PProfile next; /* next profile in same contour, used */\r
- /* during drop-out control */\r
- };\r
-\r
- typedef PProfile TProfileList;\r
- typedef PProfile* PProfileList;\r
-\r
-\r
- /* Simple record used to implement a stack of bands, required */\r
- /* by the sub-banding mechanism */\r
- typedef struct TBand_\r
- {\r
- Short y_min; /* band's minimum */\r
- Short y_max; /* band's maximum */\r
-\r
- } TBand;\r
-\r
-\r
-#define AlignProfileSize \\r
- ( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( long ) )\r
-\r
-\r
-#ifdef FT_STATIC_RASTER\r
-\r
-\r
-#define RAS_ARGS /* void */\r
-#define RAS_ARG /* void */\r
-\r
-#define RAS_VARS /* void */\r
-#define RAS_VAR /* void */\r
-\r
-#define FT_UNUSED_RASTER do ; while ( 0 )\r
-\r
-\r
-#else /* FT_STATIC_RASTER */\r
-\r
-\r
-#define RAS_ARGS PWorker worker,\r
-#define RAS_ARG PWorker worker\r
-\r
-#define RAS_VARS worker,\r
-#define RAS_VAR worker\r
-\r
-#define FT_UNUSED_RASTER FT_UNUSED( worker )\r
-\r
-\r
-#endif /* FT_STATIC_RASTER */\r
-\r
-\r
- typedef struct TWorker_ TWorker, *PWorker;\r
-\r
-\r
- /* prototypes used for sweep function dispatch */\r
- typedef void\r
- Function_Sweep_Init( RAS_ARGS Short* min,\r
- Short* max );\r
-\r
- typedef void\r
- Function_Sweep_Span( RAS_ARGS Short y,\r
- FT_F26Dot6 x1,\r
- FT_F26Dot6 x2,\r
- PProfile left,\r
- PProfile right );\r
-\r
- typedef void\r
- Function_Sweep_Step( RAS_ARG );\r
-\r
-\r
- /* NOTE: These operations are only valid on 2's complement processors */\r
-\r
-#define FLOOR( x ) ( (x) & -ras.precision )\r
-#define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision )\r
-#define TRUNC( x ) ( (signed long)(x) >> ras.precision_bits )\r
-#define FRAC( x ) ( (x) & ( ras.precision - 1 ) )\r
-#define SCALED( x ) ( ( (x) << ras.scale_shift ) - ras.precision_half )\r
-\r
- /* Note that I have moved the location of some fields in the */\r
- /* structure to ensure that the most used variables are used */\r
- /* at the top. Thus, their offset can be coded with less */\r
- /* opcodes, and it results in a smaller executable. */\r
-\r
- struct TWorker_\r
- {\r
- Int precision_bits; /* precision related variables */\r
- Int precision;\r
- Int precision_half;\r
- Long precision_mask;\r
- Int precision_shift;\r
- Int precision_step;\r
- Int precision_jitter;\r
-\r
- Int scale_shift; /* == precision_shift for bitmaps */\r
- /* == precision_shift+1 for pixmaps */\r
-\r
- PLong buff; /* The profiles buffer */\r
- PLong sizeBuff; /* Render pool size */\r
- PLong maxBuff; /* Profiles buffer size */\r
- PLong top; /* Current cursor in buffer */\r
-\r
- FT_Error error;\r
-\r
- Int numTurns; /* number of Y-turns in outline */\r
-\r
- TPoint* arc; /* current Bezier arc pointer */\r
-\r
- UShort bWidth; /* target bitmap width */\r
- PByte bTarget; /* target bitmap buffer */\r
- PByte gTarget; /* target pixmap buffer */\r
-\r
- Long lastX, lastY, minY, maxY;\r
-\r
- UShort num_Profs; /* current number of profiles */\r
-\r
- Bool fresh; /* signals a fresh new profile which */\r
- /* 'start' field must be completed */\r
- Bool joint; /* signals that the last arc ended */\r
- /* exactly on a scanline. Allows */\r
- /* removal of doublets */\r
- PProfile cProfile; /* current profile */\r
- PProfile fProfile; /* head of linked list of profiles */\r
- PProfile gProfile; /* contour's first profile in case */\r
- /* of impact */\r
-\r
- TStates state; /* rendering state */\r
-\r
- FT_Bitmap target; /* description of target bit/pixmap */\r
- FT_Outline outline;\r
-\r
- Long traceOfs; /* current offset in target bitmap */\r
- Long traceG; /* current offset in target pixmap */\r
-\r
- Short traceIncr; /* sweep's increment in target bitmap */\r
-\r
- Short gray_min_x; /* current min x during gray rendering */\r
- Short gray_max_x; /* current max x during gray rendering */\r
-\r
- /* dispatch variables */\r
-\r
- Function_Sweep_Init* Proc_Sweep_Init;\r
- Function_Sweep_Span* Proc_Sweep_Span;\r
- Function_Sweep_Span* Proc_Sweep_Drop;\r
- Function_Sweep_Step* Proc_Sweep_Step;\r
-\r
- Byte dropOutControl; /* current drop_out control method */\r
-\r
- Bool second_pass; /* indicates whether a horizontal pass */\r
- /* should be performed to control */\r
- /* drop-out accurately when calling */\r
- /* Render_Glyph. Note that there is */\r
- /* no horizontal pass during gray */\r
- /* rendering. */\r
-\r
- TPoint arcs[3 * MaxBezier + 1]; /* The Bezier stack */\r
-\r
- TBand band_stack[16]; /* band stack used for sub-banding */\r
- Int band_top; /* band stack top */\r
-\r
-#ifdef FT_RASTER_OPTION_ANTI_ALIASING\r
-\r
- Byte* grays;\r
-\r
- Byte gray_lines[RASTER_GRAY_LINES];\r
- /* Intermediate table used to render the */\r
- /* graylevels pixmaps. */\r
- /* gray_lines is a buffer holding two */\r
- /* monochrome scanlines */\r
-\r
- Short gray_width; /* width in bytes of one monochrome */\r
- /* intermediate scanline of gray_lines. */\r
- /* Each gray pixel takes 2 bits long there */\r
-\r
- /* The gray_lines must hold 2 lines, thus with size */\r
- /* in bytes of at least `gray_width*2'. */\r
-\r
-#endif /* FT_RASTER_ANTI_ALIASING */\r
-\r
- };\r
-\r
-\r
- typedef struct TRaster_\r
- {\r
- char* buffer;\r
- long buffer_size;\r
- void* memory;\r
- PWorker worker;\r
- Byte grays[5];\r
- Short gray_width;\r
-\r
- } TRaster, *PRaster;\r
-\r
-#ifdef FT_STATIC_RASTER\r
-\r
- static TWorker cur_ras;\r
-#define ras cur_ras\r
-\r
-#else\r
-\r
-#define ras (*worker)\r
-\r
-#endif /* FT_STATIC_RASTER */\r
-\r
-\r
-static const char count_table[256] =\r
-{\r
- 0 , 1 , 1 , 2 , 1 , 2 , 2 , 3 , 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4,\r
- 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,\r
- 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,\r
- 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,\r
- 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,\r
- 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,\r
- 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,\r
- 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,\r
- 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,\r
- 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,\r
- 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,\r
- 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,\r
- 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,\r
- 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,\r
- 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,\r
- 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 , 5 , 6 , 6 , 7 , 6 , 7 , 7 , 8 };\r
-\r
-\r
-\r
- /*************************************************************************/\r
- /*************************************************************************/\r
- /** **/\r
- /** PROFILES COMPUTATION **/\r
- /** **/\r
- /*************************************************************************/\r
- /*************************************************************************/\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Set_High_Precision */\r
- /* */\r
- /* <Description> */\r
- /* Sets precision variables according to param flag. */\r
- /* */\r
- /* <Input> */\r
- /* High :: Set to True for high precision (typically for ppem < 18), */\r
- /* false otherwise. */\r
- /* */\r
- static void\r
- Set_High_Precision( RAS_ARGS Int High )\r
- {\r
- if ( High )\r
- {\r
- ras.precision_bits = 10;\r
- ras.precision_step = 128;\r
- ras.precision_jitter = 24;\r
- }\r
- else\r
- {\r
- ras.precision_bits = 6;\r
- ras.precision_step = 32;\r
- ras.precision_jitter = 2;\r
- }\r
-\r
- FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" ));\r
-\r
- ras.precision = 1 << ras.precision_bits;\r
- ras.precision_half = ras.precision / 2;\r
- ras.precision_shift = ras.precision_bits - Pixel_Bits;\r
- ras.precision_mask = -ras.precision;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* New_Profile */\r
- /* */\r
- /* <Description> */\r
- /* Creates a new profile in the render pool. */\r
- /* */\r
- /* <Input> */\r
- /* aState :: The state/orientation of the new profile. */\r
- /* */\r
- /* <Return> */\r
- /* SUCCESS on success. FAILURE in case of overflow or of incoherent */\r
- /* profile. */\r
- /* */\r
- static Bool\r
- New_Profile( RAS_ARGS TStates aState )\r
- {\r
- if ( !ras.fProfile )\r
- {\r
- ras.cProfile = (PProfile)ras.top;\r
- ras.fProfile = ras.cProfile;\r
- ras.top += AlignProfileSize;\r
- }\r
-\r
- if ( ras.top >= ras.maxBuff )\r
- {\r
- ras.error = Raster_Err_Overflow;\r
- return FAILURE;\r
- }\r
-\r
- switch ( aState )\r
- {\r
- case Ascending_State:\r
- ras.cProfile->flow = Flow_Up;\r
- FT_TRACE6(( "New ascending profile = %lx\n", (long)ras.cProfile ));\r
- break;\r
-\r
- case Descending_State:\r
- ras.cProfile->flow = Flow_Down;\r
- FT_TRACE6(( "New descending profile = %lx\n", (long)ras.cProfile ));\r
- break;\r
-\r
- default:\r
- FT_ERROR(( "New_Profile: invalid profile direction!\n" ));\r
- ras.error = Raster_Err_Invalid;\r
- return FAILURE;\r
- }\r
-\r
- ras.cProfile->start = 0;\r
- ras.cProfile->height = 0;\r
- ras.cProfile->offset = ras.top;\r
- ras.cProfile->link = (PProfile)0;\r
- ras.cProfile->next = (PProfile)0;\r
-\r
- if ( !ras.gProfile )\r
- ras.gProfile = ras.cProfile;\r
-\r
- ras.state = aState;\r
- ras.fresh = TRUE;\r
- ras.joint = FALSE;\r
-\r
- return SUCCESS;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* End_Profile */\r
- /* */\r
- /* <Description> */\r
- /* Finalizes the current profile. */\r
- /* */\r
- /* <Return> */\r
- /* SUCCESS on success. FAILURE in case of overflow or incoherency. */\r
- /* */\r
- static Bool\r
- End_Profile( RAS_ARG )\r
- {\r
- Long h;\r
- PProfile oldProfile;\r
-\r
-\r
- h = (Long)( ras.top - ras.cProfile->offset );\r
-\r
- if ( h < 0 )\r
- {\r
- FT_ERROR(( "End_Profile: negative height encountered!\n" ));\r
- ras.error = Raster_Err_Neg_Height;\r
- return FAILURE;\r
- }\r
-\r
- if ( h > 0 )\r
- {\r
- FT_TRACE6(( "Ending profile %lx, start = %ld, height = %ld\n",\r
- (long)ras.cProfile, ras.cProfile->start, h ));\r
-\r
- oldProfile = ras.cProfile;\r
- ras.cProfile->height = h;\r
- ras.cProfile = (PProfile)ras.top;\r
-\r
- ras.top += AlignProfileSize;\r
-\r
- ras.cProfile->height = 0;\r
- ras.cProfile->offset = ras.top;\r
- oldProfile->next = ras.cProfile;\r
- ras.num_Profs++;\r
- }\r
-\r
- if ( ras.top >= ras.maxBuff )\r
- {\r
- FT_TRACE1(( "overflow in End_Profile\n" ));\r
- ras.error = Raster_Err_Overflow;\r
- return FAILURE;\r
- }\r
-\r
- ras.joint = FALSE;\r
-\r
- return SUCCESS;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Insert_Y_Turn */\r
- /* */\r
- /* <Description> */\r
- /* Inserts a salient into the sorted list placed on top of the render */\r
- /* pool. */\r
- /* */\r
- /* <Input> */\r
- /* New y scanline position. */\r
- /* */\r
- /* <Return> */\r
- /* SUCCESS on success. FAILURE in case of overflow. */\r
- /* */\r
- static Bool\r
- Insert_Y_Turn( RAS_ARGS Int y )\r
- {\r
- PLong y_turns;\r
- Int y2, n;\r
-\r
-\r
- n = ras.numTurns - 1;\r
- y_turns = ras.sizeBuff - ras.numTurns;\r
-\r
- /* look for first y value that is <= */\r
- while ( n >= 0 && y < y_turns[n] )\r
- n--;\r
-\r
- /* if it is <, simply insert it, ignore if == */\r
- if ( n >= 0 && y > y_turns[n] )\r
- while ( n >= 0 )\r
- {\r
- y2 = (Int)y_turns[n];\r
- y_turns[n] = y;\r
- y = y2;\r
- n--;\r
- }\r
-\r
- if ( n < 0 )\r
- {\r
- ras.maxBuff--;\r
- if ( ras.maxBuff <= ras.top )\r
- {\r
- ras.error = Raster_Err_Overflow;\r
- return FAILURE;\r
- }\r
- ras.numTurns++;\r
- ras.sizeBuff[-ras.numTurns] = y;\r
- }\r
-\r
- return SUCCESS;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Finalize_Profile_Table */\r
- /* */\r
- /* <Description> */\r
- /* Adjusts all links in the profiles list. */\r
- /* */\r
- /* <Return> */\r
- /* SUCCESS on success. FAILURE in case of overflow. */\r
- /* */\r
- static Bool\r
- Finalize_Profile_Table( RAS_ARG )\r
- {\r
- Int bottom, top;\r
- UShort n;\r
- PProfile p;\r
-\r
-\r
- n = ras.num_Profs;\r
-\r
- if ( n > 1 )\r
- {\r
- p = ras.fProfile;\r
- while ( n > 0 )\r
- {\r
- if ( n > 1 )\r
- p->link = (PProfile)( p->offset + p->height );\r
- else\r
- p->link = NULL;\r
-\r
- switch ( p->flow )\r
- {\r
- case Flow_Down:\r
- bottom = (Int)( p->start - p->height + 1 );\r
- top = (Int)p->start;\r
- p->start = bottom;\r
- p->offset += p->height - 1;\r
- break;\r
-\r
- case Flow_Up:\r
- default:\r
- bottom = (Int)p->start;\r
- top = (Int)( p->start + p->height - 1 );\r
- }\r
-\r
- if ( Insert_Y_Turn( RAS_VARS bottom ) ||\r
- Insert_Y_Turn( RAS_VARS top + 1 ) )\r
- return FAILURE;\r
-\r
- p = p->link;\r
- n--;\r
- }\r
- }\r
- else\r
- ras.fProfile = NULL;\r
-\r
- return SUCCESS;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Split_Conic */\r
- /* */\r
- /* <Description> */\r
- /* Subdivides one conic Bezier into two joint sub-arcs in the Bezier */\r
- /* stack. */\r
- /* */\r
- /* <Input> */\r
- /* None (subdivided Bezier is taken from the top of the stack). */\r
- /* */\r
- /* <Note> */\r
- /* This routine is the `beef' of this component. It is _the_ inner */\r
- /* loop that should be optimized to hell to get the best performance. */\r
- /* */\r
- static void\r
- Split_Conic( TPoint* base )\r
- {\r
- Long a, b;\r
-\r
-\r
- base[4].x = base[2].x;\r
- b = base[1].x;\r
- a = base[3].x = ( base[2].x + b ) / 2;\r
- b = base[1].x = ( base[0].x + b ) / 2;\r
- base[2].x = ( a + b ) / 2;\r
-\r
- base[4].y = base[2].y;\r
- b = base[1].y;\r
- a = base[3].y = ( base[2].y + b ) / 2;\r
- b = base[1].y = ( base[0].y + b ) / 2;\r
- base[2].y = ( a + b ) / 2;\r
-\r
- /* hand optimized. gcc doesn't seem to be too good at common */\r
- /* expression substitution and instruction scheduling ;-) */\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Split_Cubic */\r
- /* */\r
- /* <Description> */\r
- /* Subdivides a third-order Bezier arc into two joint sub-arcs in the */\r
- /* Bezier stack. */\r
- /* */\r
- /* <Note> */\r
- /* This routine is the `beef' of the component. It is one of _the_ */\r
- /* inner loops that should be optimized like hell to get the best */\r
- /* performance. */\r
- /* */\r
- static void\r
- Split_Cubic( TPoint* base )\r
- {\r
- Long a, b, c, d;\r
-\r
-\r
- base[6].x = base[3].x;\r
- c = base[1].x;\r
- d = base[2].x;\r
- base[1].x = a = ( base[0].x + c + 1 ) >> 1;\r
- base[5].x = b = ( base[3].x + d + 1 ) >> 1;\r
- c = ( c + d + 1 ) >> 1;\r
- base[2].x = a = ( a + c + 1 ) >> 1;\r
- base[4].x = b = ( b + c + 1 ) >> 1;\r
- base[3].x = ( a + b + 1 ) >> 1;\r
-\r
- base[6].y = base[3].y;\r
- c = base[1].y;\r
- d = base[2].y;\r
- base[1].y = a = ( base[0].y + c + 1 ) >> 1;\r
- base[5].y = b = ( base[3].y + d + 1 ) >> 1;\r
- c = ( c + d + 1 ) >> 1;\r
- base[2].y = a = ( a + c + 1 ) >> 1;\r
- base[4].y = b = ( b + c + 1 ) >> 1;\r
- base[3].y = ( a + b + 1 ) >> 1;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Line_Up */\r
- /* */\r
- /* <Description> */\r
- /* Computes the x-coordinates of an ascending line segment and stores */\r
- /* them in the render pool. */\r
- /* */\r
- /* <Input> */\r
- /* x1 :: The x-coordinate of the segment's start point. */\r
- /* */\r
- /* y1 :: The y-coordinate of the segment's start point. */\r
- /* */\r
- /* x2 :: The x-coordinate of the segment's end point. */\r
- /* */\r
- /* y2 :: The y-coordinate of the segment's end point. */\r
- /* */\r
- /* miny :: A lower vertical clipping bound value. */\r
- /* */\r
- /* maxy :: An upper vertical clipping bound value. */\r
- /* */\r
- /* <Return> */\r
- /* SUCCESS on success, FAILURE on render pool overflow. */\r
- /* */\r
- static Bool\r
- Line_Up( RAS_ARGS Long x1,\r
- Long y1,\r
- Long x2,\r
- Long y2,\r
- Long miny,\r
- Long maxy )\r
- {\r
- Long Dx, Dy;\r
- Int e1, e2, f1, f2, size; /* XXX: is `Short' sufficient? */\r
- Long Ix, Rx, Ax;\r
-\r
- PLong top;\r
-\r
-\r
- Dx = x2 - x1;\r
- Dy = y2 - y1;\r
-\r
- if ( Dy <= 0 || y2 < miny || y1 > maxy )\r
- return SUCCESS;\r
-\r
- if ( y1 < miny )\r
- {\r
- /* Take care: miny-y1 can be a very large value; we use */\r
- /* a slow MulDiv function to avoid clipping bugs */\r
- x1 += SMulDiv( Dx, miny - y1, Dy );\r
- e1 = (Int)TRUNC( miny );\r
- f1 = 0;\r
- }\r
- else\r
- {\r
- e1 = (Int)TRUNC( y1 );\r
- f1 = (Int)FRAC( y1 );\r
- }\r
-\r
- if ( y2 > maxy )\r
- {\r
- /* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */\r
- e2 = (Int)TRUNC( maxy );\r
- f2 = 0;\r
- }\r
- else\r
- {\r
- e2 = (Int)TRUNC( y2 );\r
- f2 = (Int)FRAC( y2 );\r
- }\r
-\r
- if ( f1 > 0 )\r
- {\r
- if ( e1 == e2 )\r
- return SUCCESS;\r
- else\r
- {\r
- x1 += FMulDiv( Dx, ras.precision - f1, Dy );\r
- e1 += 1;\r
- }\r
- }\r
- else\r
- if ( ras.joint )\r
- {\r
- ras.top--;\r
- ras.joint = FALSE;\r
- }\r
-\r
- ras.joint = (char)( f2 == 0 );\r
-\r
- if ( ras.fresh )\r
- {\r
- ras.cProfile->start = e1;\r
- ras.fresh = FALSE;\r
- }\r
-\r
- size = e2 - e1 + 1;\r
- if ( ras.top + size >= ras.maxBuff )\r
- {\r
- ras.error = Raster_Err_Overflow;\r
- return FAILURE;\r
- }\r
-\r
- if ( Dx > 0 )\r
- {\r
- Ix = ( ras.precision * Dx ) / Dy;\r
- Rx = ( ras.precision * Dx ) % Dy;\r
- Dx = 1;\r
- }\r
- else\r
- {\r
- Ix = -( ( ras.precision * -Dx ) / Dy );\r
- Rx = ( ras.precision * -Dx ) % Dy;\r
- Dx = -1;\r
- }\r
-\r
- Ax = -Dy;\r
- top = ras.top;\r
-\r
- while ( size > 0 )\r
- {\r
- *top++ = x1;\r
-\r
- x1 += Ix;\r
- Ax += Rx;\r
- if ( Ax >= 0 )\r
- {\r
- Ax -= Dy;\r
- x1 += Dx;\r
- }\r
- size--;\r
- }\r
-\r
- ras.top = top;\r
- return SUCCESS;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Line_Down */\r
- /* */\r
- /* <Description> */\r
- /* Computes the x-coordinates of an descending line segment and */\r
- /* stores them in the render pool. */\r
- /* */\r
- /* <Input> */\r
- /* x1 :: The x-coordinate of the segment's start point. */\r
- /* */\r
- /* y1 :: The y-coordinate of the segment's start point. */\r
- /* */\r
- /* x2 :: The x-coordinate of the segment's end point. */\r
- /* */\r
- /* y2 :: The y-coordinate of the segment's end point. */\r
- /* */\r
- /* miny :: A lower vertical clipping bound value. */\r
- /* */\r
- /* maxy :: An upper vertical clipping bound value. */\r
- /* */\r
- /* <Return> */\r
- /* SUCCESS on success, FAILURE on render pool overflow. */\r
- /* */\r
- static Bool\r
- Line_Down( RAS_ARGS Long x1,\r
- Long y1,\r
- Long x2,\r
- Long y2,\r
- Long miny,\r
- Long maxy )\r
- {\r
- Bool result, fresh;\r
-\r
-\r
- fresh = ras.fresh;\r
-\r
- result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );\r
-\r
- if ( fresh && !ras.fresh )\r
- ras.cProfile->start = -ras.cProfile->start;\r
-\r
- return result;\r
- }\r
-\r
-\r
- /* A function type describing the functions used to split Bezier arcs */\r
- typedef void (*TSplitter)( TPoint* base );\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Bezier_Up */\r
- /* */\r
- /* <Description> */\r
- /* Computes the x-coordinates of an ascending Bezier arc and stores */\r
- /* them in the render pool. */\r
- /* */\r
- /* <Input> */\r
- /* degree :: The degree of the Bezier arc (either 2 or 3). */\r
- /* */\r
- /* splitter :: The function to split Bezier arcs. */\r
- /* */\r
- /* miny :: A lower vertical clipping bound value. */\r
- /* */\r
- /* maxy :: An upper vertical clipping bound value. */\r
- /* */\r
- /* <Return> */\r
- /* SUCCESS on success, FAILURE on render pool overflow. */\r
- /* */\r
- static Bool\r
- Bezier_Up( RAS_ARGS Int degree,\r
- TSplitter splitter,\r
- Long miny,\r
- Long maxy )\r
- {\r
- Long y1, y2, e, e2, e0;\r
- Short f1;\r
-\r
- TPoint* arc;\r
- TPoint* start_arc;\r
-\r
- PLong top;\r
-\r
-\r
- arc = ras.arc;\r
- y1 = arc[degree].y;\r
- y2 = arc[0].y;\r
- top = ras.top;\r
-\r
- if ( y2 < miny || y1 > maxy )\r
- goto Fin;\r
-\r
- e2 = FLOOR( y2 );\r
-\r
- if ( e2 > maxy )\r
- e2 = maxy;\r
-\r
- e0 = miny;\r
-\r
- if ( y1 < miny )\r
- e = miny;\r
- else\r
- {\r
- e = CEILING( y1 );\r
- f1 = (Short)( FRAC( y1 ) );\r
- e0 = e;\r
-\r
- if ( f1 == 0 )\r
- {\r
- if ( ras.joint )\r
- {\r
- top--;\r
- ras.joint = FALSE;\r
- }\r
-\r
- *top++ = arc[degree].x;\r
-\r
- e += ras.precision;\r
- }\r
- }\r
-\r
- if ( ras.fresh )\r
- {\r
- ras.cProfile->start = TRUNC( e0 );\r
- ras.fresh = FALSE;\r
- }\r
-\r
- if ( e2 < e )\r
- goto Fin;\r
-\r
- if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff )\r
- {\r
- ras.top = top;\r
- ras.error = Raster_Err_Overflow;\r
- return FAILURE;\r
- }\r
-\r
- start_arc = arc;\r
-\r
- while ( arc >= start_arc && e <= e2 )\r
- {\r
- ras.joint = FALSE;\r
-\r
- y2 = arc[0].y;\r
-\r
- if ( y2 > e )\r
- {\r
- y1 = arc[degree].y;\r
- if ( y2 - y1 >= ras.precision_step )\r
- {\r
- splitter( arc );\r
- arc += degree;\r
- }\r
- else\r
- {\r
- *top++ = arc[degree].x + FMulDiv( arc[0].x-arc[degree].x,\r
- e - y1, y2 - y1 );\r
- arc -= degree;\r
- e += ras.precision;\r
- }\r
- }\r
- else\r
- {\r
- if ( y2 == e )\r
- {\r
- ras.joint = TRUE;\r
- *top++ = arc[0].x;\r
-\r
- e += ras.precision;\r
- }\r
- arc -= degree;\r
- }\r
- }\r
-\r
- Fin:\r
- ras.top = top;\r
- ras.arc -= degree;\r
- return SUCCESS;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Bezier_Down */\r
- /* */\r
- /* <Description> */\r
- /* Computes the x-coordinates of an descending Bezier arc and stores */\r
- /* them in the render pool. */\r
- /* */\r
- /* <Input> */\r
- /* degree :: The degree of the Bezier arc (either 2 or 3). */\r
- /* */\r
- /* splitter :: The function to split Bezier arcs. */\r
- /* */\r
- /* miny :: A lower vertical clipping bound value. */\r
- /* */\r
- /* maxy :: An upper vertical clipping bound value. */\r
- /* */\r
- /* <Return> */\r
- /* SUCCESS on success, FAILURE on render pool overflow. */\r
- /* */\r
- static Bool\r
- Bezier_Down( RAS_ARGS Int degree,\r
- TSplitter splitter,\r
- Long miny,\r
- Long maxy )\r
- {\r
- TPoint* arc = ras.arc;\r
- Bool result, fresh;\r
-\r
-\r
- arc[0].y = -arc[0].y;\r
- arc[1].y = -arc[1].y;\r
- arc[2].y = -arc[2].y;\r
- if ( degree > 2 )\r
- arc[3].y = -arc[3].y;\r
-\r
- fresh = ras.fresh;\r
-\r
- result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny );\r
-\r
- if ( fresh && !ras.fresh )\r
- ras.cProfile->start = -ras.cProfile->start;\r
-\r
- arc[0].y = -arc[0].y;\r
- return result;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Line_To */\r
- /* */\r
- /* <Description> */\r
- /* Injects a new line segment and adjusts Profiles list. */\r
- /* */\r
- /* <Input> */\r
- /* x :: The x-coordinate of the segment's end point (its start point */\r
- /* is stored in `lastX'). */\r
- /* */\r
- /* y :: The y-coordinate of the segment's end point (its start point */\r
- /* is stored in `lastY'). */\r
- /* */\r
- /* <Return> */\r
- /* SUCCESS on success, FAILURE on render pool overflow or incorrect */\r
- /* profile. */\r
- /* */\r
- static Bool\r
- Line_To( RAS_ARGS Long x,\r
- Long y )\r
- {\r
- /* First, detect a change of direction */\r
-\r
- switch ( ras.state )\r
- {\r
- case Unknown_State:\r
- if ( y > ras.lastY )\r
- {\r
- if ( New_Profile( RAS_VARS Ascending_State ) )\r
- return FAILURE;\r
- }\r
- else\r
- {\r
- if ( y < ras.lastY )\r
- if ( New_Profile( RAS_VARS Descending_State ) )\r
- return FAILURE;\r
- }\r
- break;\r
-\r
- case Ascending_State:\r
- if ( y < ras.lastY )\r
- {\r
- if ( End_Profile( RAS_VAR ) ||\r
- New_Profile( RAS_VARS Descending_State ) )\r
- return FAILURE;\r
- }\r
- break;\r
-\r
- case Descending_State:\r
- if ( y > ras.lastY )\r
- {\r
- if ( End_Profile( RAS_VAR ) ||\r
- New_Profile( RAS_VARS Ascending_State ) )\r
- return FAILURE;\r
- }\r
- break;\r
-\r
- default:\r
- ;\r
- }\r
-\r
- /* Then compute the lines */\r
-\r
- switch ( ras.state )\r
- {\r
- case Ascending_State:\r
- if ( Line_Up( RAS_VARS ras.lastX, ras.lastY,\r
- x, y, ras.minY, ras.maxY ) )\r
- return FAILURE;\r
- break;\r
-\r
- case Descending_State:\r
- if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,\r
- x, y, ras.minY, ras.maxY ) )\r
- return FAILURE;\r
- break;\r
-\r
- default:\r
- ;\r
- }\r
-\r
- ras.lastX = x;\r
- ras.lastY = y;\r
-\r
- return SUCCESS;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Conic_To */\r
- /* */\r
- /* <Description> */\r
- /* Injects a new conic arc and adjusts the profile list. */\r
- /* */\r
- /* <Input> */\r
- /* cx :: The x-coordinate of the arc's new control point. */\r
- /* */\r
- /* cy :: The y-coordinate of the arc's new control point. */\r
- /* */\r
- /* x :: The x-coordinate of the arc's end point (its start point is */\r
- /* stored in `lastX'). */\r
- /* */\r
- /* y :: The y-coordinate of the arc's end point (its start point is */\r
- /* stored in `lastY'). */\r
- /* */\r
- /* <Return> */\r
- /* SUCCESS on success, FAILURE on render pool overflow or incorrect */\r
- /* profile. */\r
- /* */\r
- static Bool\r
- Conic_To( RAS_ARGS Long cx,\r
- Long cy,\r
- Long x,\r
- Long y )\r
- {\r
- Long y1, y2, y3, x3, ymin, ymax;\r
- TStates state_bez;\r
-\r
-\r
- ras.arc = ras.arcs;\r
- ras.arc[2].x = ras.lastX;\r
- ras.arc[2].y = ras.lastY;\r
- ras.arc[1].x = cx; ras.arc[1].y = cy;\r
- ras.arc[0].x = x; ras.arc[0].y = y;\r
-\r
- do\r
- {\r
- y1 = ras.arc[2].y;\r
- y2 = ras.arc[1].y;\r
- y3 = ras.arc[0].y;\r
- x3 = ras.arc[0].x;\r
-\r
- /* first, categorize the Bezier arc */\r
-\r
- if ( y1 <= y3 )\r
- {\r
- ymin = y1;\r
- ymax = y3;\r
- }\r
- else\r
- {\r
- ymin = y3;\r
- ymax = y1;\r
- }\r
-\r
- if ( y2 < ymin || y2 > ymax )\r
- {\r
- /* this arc has no given direction, split it! */\r
- Split_Conic( ras.arc );\r
- ras.arc += 2;\r
- }\r
- else if ( y1 == y3 )\r
- {\r
- /* this arc is flat, ignore it and pop it from the Bezier stack */\r
- ras.arc -= 2;\r
- }\r
- else\r
- {\r
- /* the arc is y-monotonous, either ascending or descending */\r
- /* detect a change of direction */\r
- state_bez = y1 < y3 ? Ascending_State : Descending_State;\r
- if ( ras.state != state_bez )\r
- {\r
- /* finalize current profile if any */\r
- if ( ras.state != Unknown_State &&\r
- End_Profile( RAS_VAR ) )\r
- goto Fail;\r
-\r
- /* create a new profile */\r
- if ( New_Profile( RAS_VARS state_bez ) )\r
- goto Fail;\r
- }\r
-\r
- /* now call the appropriate routine */\r
- if ( state_bez == Ascending_State )\r
- {\r
- if ( Bezier_Up( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )\r
- goto Fail;\r
- }\r
- else\r
- if ( Bezier_Down( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )\r
- goto Fail;\r
- }\r
-\r
- } while ( ras.arc >= ras.arcs );\r
-\r
- ras.lastX = x3;\r
- ras.lastY = y3;\r
-\r
- return SUCCESS;\r
-\r
- Fail:\r
- return FAILURE;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Cubic_To */\r
- /* */\r
- /* <Description> */\r
- /* Injects a new cubic arc and adjusts the profile list. */\r
- /* */\r
- /* <Input> */\r
- /* cx1 :: The x-coordinate of the arc's first new control point. */\r
- /* */\r
- /* cy1 :: The y-coordinate of the arc's first new control point. */\r
- /* */\r
- /* cx2 :: The x-coordinate of the arc's second new control point. */\r
- /* */\r
- /* cy2 :: The y-coordinate of the arc's second new control point. */\r
- /* */\r
- /* x :: The x-coordinate of the arc's end point (its start point is */\r
- /* stored in `lastX'). */\r
- /* */\r
- /* y :: The y-coordinate of the arc's end point (its start point is */\r
- /* stored in `lastY'). */\r
- /* */\r
- /* <Return> */\r
- /* SUCCESS on success, FAILURE on render pool overflow or incorrect */\r
- /* profile. */\r
- /* */\r
- static Bool\r
- Cubic_To( RAS_ARGS Long cx1,\r
- Long cy1,\r
- Long cx2,\r
- Long cy2,\r
- Long x,\r
- Long y )\r
- {\r
- Long y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;\r
- TStates state_bez;\r
-\r
-\r
- ras.arc = ras.arcs;\r
- ras.arc[3].x = ras.lastX;\r
- ras.arc[3].y = ras.lastY;\r
- ras.arc[2].x = cx1; ras.arc[2].y = cy1;\r
- ras.arc[1].x = cx2; ras.arc[1].y = cy2;\r
- ras.arc[0].x = x; ras.arc[0].y = y;\r
-\r
- do\r
- {\r
- y1 = ras.arc[3].y;\r
- y2 = ras.arc[2].y;\r
- y3 = ras.arc[1].y;\r
- y4 = ras.arc[0].y;\r
- x4 = ras.arc[0].x;\r
-\r
- /* first, categorize the Bezier arc */\r
-\r
- if ( y1 <= y4 )\r
- {\r
- ymin1 = y1;\r
- ymax1 = y4;\r
- }\r
- else\r
- {\r
- ymin1 = y4;\r
- ymax1 = y1;\r
- }\r
-\r
- if ( y2 <= y3 )\r
- {\r
- ymin2 = y2;\r
- ymax2 = y3;\r
- }\r
- else\r
- {\r
- ymin2 = y3;\r
- ymax2 = y2;\r
- }\r
-\r
- if ( ymin2 < ymin1 || ymax2 > ymax1 )\r
- {\r
- /* this arc has no given direction, split it! */\r
- Split_Cubic( ras.arc );\r
- ras.arc += 3;\r
- }\r
- else if ( y1 == y4 )\r
- {\r
- /* this arc is flat, ignore it and pop it from the Bezier stack */\r
- ras.arc -= 3;\r
- }\r
- else\r
- {\r
- state_bez = ( y1 <= y4 ) ? Ascending_State : Descending_State;\r
-\r
- /* detect a change of direction */\r
- if ( ras.state != state_bez )\r
- {\r
- if ( ras.state != Unknown_State &&\r
- End_Profile( RAS_VAR ) )\r
- goto Fail;\r
-\r
- if ( New_Profile( RAS_VARS state_bez ) )\r
- goto Fail;\r
- }\r
-\r
- /* compute intersections */\r
- if ( state_bez == Ascending_State )\r
- {\r
- if ( Bezier_Up( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )\r
- goto Fail;\r
- }\r
- else\r
- if ( Bezier_Down( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )\r
- goto Fail;\r
- }\r
-\r
- } while ( ras.arc >= ras.arcs );\r
-\r
- ras.lastX = x4;\r
- ras.lastY = y4;\r
-\r
- return SUCCESS;\r
-\r
- Fail:\r
- return FAILURE;\r
- }\r
-\r
-\r
-#undef SWAP_\r
-#define SWAP_( x, y ) do \\r
- { \\r
- Long swap = x; \\r
- \\r
- \\r
- x = y; \\r
- y = swap; \\r
- } while ( 0 )\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Decompose_Curve */\r
- /* */\r
- /* <Description> */\r
- /* Scans the outline arrays in order to emit individual segments and */\r
- /* Beziers by calling Line_To() and Bezier_To(). It handles all */\r
- /* weird cases, like when the first point is off the curve, or when */\r
- /* there are simply no `on' points in the contour! */\r
- /* */\r
- /* <Input> */\r
- /* first :: The index of the first point in the contour. */\r
- /* */\r
- /* last :: The index of the last point in the contour. */\r
- /* */\r
- /* flipped :: If set, flip the direction of the curve. */\r
- /* */\r
- /* <Return> */\r
- /* SUCCESS on success, FAILURE on error. */\r
- /* */\r
- static Bool\r
- Decompose_Curve( RAS_ARGS UShort first,\r
- UShort last,\r
- int flipped )\r
- {\r
- FT_Vector v_last;\r
- FT_Vector v_control;\r
- FT_Vector v_start;\r
-\r
- FT_Vector* points;\r
- FT_Vector* point;\r
- FT_Vector* limit;\r
- char* tags;\r
-\r
- unsigned tag; /* current point's state */\r
-\r
-\r
- points = ras.outline.points;\r
- limit = points + last;\r
-\r
- v_start.x = SCALED( points[first].x );\r
- v_start.y = SCALED( points[first].y );\r
- v_last.x = SCALED( points[last].x );\r
- v_last.y = SCALED( points[last].y );\r
-\r
- if ( flipped )\r
- {\r
- SWAP_( v_start.x, v_start.y );\r
- SWAP_( v_last.x, v_last.y );\r
- }\r
-\r
- v_control = v_start;\r
-\r
- point = points + first;\r
- tags = ras.outline.tags + first;\r
- tag = FT_CURVE_TAG( tags[0] );\r
-\r
- /* A contour cannot start with a cubic control point! */\r
- if ( tag == FT_CURVE_TAG_CUBIC )\r
- goto Invalid_Outline;\r
-\r
- /* check first point to determine origin */\r
- if ( tag == FT_CURVE_TAG_CONIC )\r
- {\r
- /* first point is conic control. Yes, this happens. */\r
- if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_CURVE_TAG_ON )\r
- {\r
- /* start at last point if it is on the curve */\r
- v_start = v_last;\r
- limit--;\r
- }\r
- else\r
- {\r
- /* if both first and last points are conic, */\r
- /* start at their middle and record its position */\r
- /* for closure */\r
- v_start.x = ( v_start.x + v_last.x ) / 2;\r
- v_start.y = ( v_start.y + v_last.y ) / 2;\r
-\r
- v_last = v_start;\r
- }\r
- point--;\r
- tags--;\r
- }\r
-\r
- ras.lastX = v_start.x;\r
- ras.lastY = v_start.y;\r
-\r
- while ( point < limit )\r
- {\r
- point++;\r
- tags++;\r
-\r
- tag = FT_CURVE_TAG( tags[0] );\r
-\r
- switch ( tag )\r
- {\r
- case FT_CURVE_TAG_ON: /* emit a single line_to */\r
- {\r
- Long x, y;\r
-\r
-\r
- x = SCALED( point->x );\r
- y = SCALED( point->y );\r
- if ( flipped )\r
- SWAP_( x, y );\r
-\r
- if ( Line_To( RAS_VARS x, y ) )\r
- goto Fail;\r
- continue;\r
- }\r
-\r
- case FT_CURVE_TAG_CONIC: /* consume conic arcs */\r
- v_control.x = SCALED( point[0].x );\r
- v_control.y = SCALED( point[0].y );\r
-\r
- if ( flipped )\r
- SWAP_( v_control.x, v_control.y );\r
-\r
- Do_Conic:\r
- if ( point < limit )\r
- {\r
- FT_Vector v_middle;\r
- Long x, y;\r
-\r
-\r
- point++;\r
- tags++;\r
- tag = FT_CURVE_TAG( tags[0] );\r
-\r
- x = SCALED( point[0].x );\r
- y = SCALED( point[0].y );\r
-\r
- if ( flipped )\r
- SWAP_( x, y );\r
-\r
- if ( tag == FT_CURVE_TAG_ON )\r
- {\r
- if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) )\r
- goto Fail;\r
- continue;\r
- }\r
-\r
- if ( tag != FT_CURVE_TAG_CONIC )\r
- goto Invalid_Outline;\r
-\r
- v_middle.x = ( v_control.x + x ) / 2;\r
- v_middle.y = ( v_control.y + y ) / 2;\r
-\r
- if ( Conic_To( RAS_VARS v_control.x, v_control.y,\r
- v_middle.x, v_middle.y ) )\r
- goto Fail;\r
-\r
- v_control.x = x;\r
- v_control.y = y;\r
-\r
- goto Do_Conic;\r
- }\r
-\r
- if ( Conic_To( RAS_VARS v_control.x, v_control.y,\r
- v_start.x, v_start.y ) )\r
- goto Fail;\r
-\r
- goto Close;\r
-\r
- default: /* FT_CURVE_TAG_CUBIC */\r
- {\r
- Long x1, y1, x2, y2, x3, y3;\r
-\r
-\r
- if ( point + 1 > limit ||\r
- FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )\r
- goto Invalid_Outline;\r
-\r
- point += 2;\r
- tags += 2;\r
-\r
- x1 = SCALED( point[-2].x );\r
- y1 = SCALED( point[-2].y );\r
- x2 = SCALED( point[-1].x );\r
- y2 = SCALED( point[-1].y );\r
- x3 = SCALED( point[ 0].x );\r
- y3 = SCALED( point[ 0].y );\r
-\r
- if ( flipped )\r
- {\r
- SWAP_( x1, y1 );\r
- SWAP_( x2, y2 );\r
- SWAP_( x3, y3 );\r
- }\r
-\r
- if ( point <= limit )\r
- {\r
- if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) )\r
- goto Fail;\r
- continue;\r
- }\r
-\r
- if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) )\r
- goto Fail;\r
- goto Close;\r
- }\r
- }\r
- }\r
-\r
- /* close the contour with a line segment */\r
- if ( Line_To( RAS_VARS v_start.x, v_start.y ) )\r
- goto Fail;\r
-\r
- Close:\r
- return SUCCESS;\r
-\r
- Invalid_Outline:\r
- ras.error = Raster_Err_Invalid;\r
-\r
- Fail:\r
- return FAILURE;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Convert_Glyph */\r
- /* */\r
- /* <Description> */\r
- /* Converts a glyph into a series of segments and arcs and makes a */\r
- /* profiles list with them. */\r
- /* */\r
- /* <Input> */\r
- /* flipped :: If set, flip the direction of curve. */\r
- /* */\r
- /* <Return> */\r
- /* SUCCESS on success, FAILURE if any error was encountered during */\r
- /* rendering. */\r
- /* */\r
- static Bool\r
- Convert_Glyph( RAS_ARGS int flipped )\r
- {\r
- int i;\r
- unsigned start;\r
-\r
- PProfile lastProfile;\r
-\r
-\r
- ras.fProfile = NULL;\r
- ras.joint = FALSE;\r
- ras.fresh = FALSE;\r
-\r
- ras.maxBuff = ras.sizeBuff - AlignProfileSize;\r
-\r
- ras.numTurns = 0;\r
-\r
- ras.cProfile = (PProfile)ras.top;\r
- ras.cProfile->offset = ras.top;\r
- ras.num_Profs = 0;\r
-\r
- start = 0;\r
-\r
- for ( i = 0; i < ras.outline.n_contours; i++ )\r
- {\r
- ras.state = Unknown_State;\r
- ras.gProfile = NULL;\r
-\r
- if ( Decompose_Curve( RAS_VARS (unsigned short)start,\r
- ras.outline.contours[i],\r
- flipped ) )\r
- return FAILURE;\r
-\r
- start = ras.outline.contours[i] + 1;\r
-\r
- /* We must now see whether the extreme arcs join or not */\r
- if ( FRAC( ras.lastY ) == 0 &&\r
- ras.lastY >= ras.minY &&\r
- ras.lastY <= ras.maxY )\r
- if ( ras.gProfile && ras.gProfile->flow == ras.cProfile->flow )\r
- ras.top--;\r
- /* Note that ras.gProfile can be nil if the contour was too small */\r
- /* to be drawn. */\r
-\r
- lastProfile = ras.cProfile;\r
- if ( End_Profile( RAS_VAR ) )\r
- return FAILURE;\r
-\r
- /* close the `next profile in contour' linked list */\r
- if ( ras.gProfile )\r
- lastProfile->next = ras.gProfile;\r
- }\r
-\r
- if ( Finalize_Profile_Table( RAS_VAR ) )\r
- return FAILURE;\r
-\r
- return (Bool)( ras.top < ras.maxBuff ? SUCCESS : FAILURE );\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /*************************************************************************/\r
- /** **/\r
- /** SCAN-LINE SWEEPS AND DRAWING **/\r
- /** **/\r
- /*************************************************************************/\r
- /*************************************************************************/\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* Init_Linked */\r
- /* */\r
- /* Initializes an empty linked list. */\r
- /* */\r
- static void\r
- Init_Linked( TProfileList* l )\r
- {\r
- *l = NULL;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* InsNew */\r
- /* */\r
- /* Inserts a new profile in a linked list. */\r
- /* */\r
- static void\r
- InsNew( PProfileList list,\r
- PProfile profile )\r
- {\r
- PProfile *old, current;\r
- Long x;\r
-\r
-\r
- old = list;\r
- current = *old;\r
- x = profile->X;\r
-\r
- while ( current )\r
- {\r
- if ( x < current->X )\r
- break;\r
- old = ¤t->link;\r
- current = *old;\r
- }\r
-\r
- profile->link = current;\r
- *old = profile;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* DelOld */\r
- /* */\r
- /* Removes an old profile from a linked list. */\r
- /* */\r
- static void\r
- DelOld( PProfileList list,\r
- PProfile profile )\r
- {\r
- PProfile *old, current;\r
-\r
-\r
- old = list;\r
- current = *old;\r
-\r
- while ( current )\r
- {\r
- if ( current == profile )\r
- {\r
- *old = current->link;\r
- return;\r
- }\r
-\r
- old = ¤t->link;\r
- current = *old;\r
- }\r
-\r
- /* we should never get there, unless the profile was not part of */\r
- /* the list. */\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* Sort */\r
- /* */\r
- /* Sorts a trace list. In 95%, the list is already sorted. We need */\r
- /* an algorithm which is fast in this case. Bubble sort is enough */\r
- /* and simple. */\r
- /* */\r
- static void\r
- Sort( PProfileList list )\r
- {\r
- PProfile *old, current, next;\r
-\r
-\r
- /* First, set the new X coordinate of each profile */\r
- current = *list;\r
- while ( current )\r
- {\r
- current->X = *current->offset;\r
- current->offset += current->flow;\r
- current->height--;\r
- current = current->link;\r
- }\r
-\r
- /* Then sort them */\r
- old = list;\r
- current = *old;\r
-\r
- if ( !current )\r
- return;\r
-\r
- next = current->link;\r
-\r
- while ( next )\r
- {\r
- if ( current->X <= next->X )\r
- {\r
- old = ¤t->link;\r
- current = *old;\r
-\r
- if ( !current )\r
- return;\r
- }\r
- else\r
- {\r
- *old = next;\r
- current->link = next->link;\r
- next->link = current;\r
-\r
- old = list;\r
- current = *old;\r
- }\r
-\r
- next = current->link;\r
- }\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* Vertical Sweep Procedure Set */\r
- /* */\r
- /* These four routines are used during the vertical black/white sweep */\r
- /* phase by the generic Draw_Sweep() function. */\r
- /* */\r
- /*************************************************************************/\r
-\r
- static void\r
- Vertical_Sweep_Init( RAS_ARGS Short* min,\r
- Short* max )\r
- {\r
- Long pitch = ras.target.pitch;\r
-\r
- FT_UNUSED( max );\r
-\r
-\r
- ras.traceIncr = (Short)-pitch;\r
- ras.traceOfs = -*min * pitch;\r
- if ( pitch > 0 )\r
- ras.traceOfs += ( ras.target.rows - 1 ) * pitch;\r
-\r
- ras.gray_min_x = 0;\r
- ras.gray_max_x = 0;\r
- }\r
-\r
-\r
- static void\r
- Vertical_Sweep_Span( RAS_ARGS Short y,\r
- FT_F26Dot6 x1,\r
- FT_F26Dot6 x2,\r
- PProfile left,\r
- PProfile right )\r
- {\r
- Long e1, e2;\r
- int c1, c2;\r
- Byte f1, f2;\r
- Byte* target;\r
-\r
- FT_UNUSED( y );\r
- FT_UNUSED( left );\r
- FT_UNUSED( right );\r
-\r
-\r
- /* Drop-out control */\r
-\r
- e1 = TRUNC( CEILING( x1 ) );\r
-\r
- if ( x2 - x1 - ras.precision <= ras.precision_jitter )\r
- e2 = e1;\r
- else\r
- e2 = TRUNC( FLOOR( x2 ) );\r
-\r
- if ( e2 >= 0 && e1 < ras.bWidth )\r
- {\r
- if ( e1 < 0 )\r
- e1 = 0;\r
- if ( e2 >= ras.bWidth )\r
- e2 = ras.bWidth - 1;\r
-\r
- c1 = (Short)( e1 >> 3 );\r
- c2 = (Short)( e2 >> 3 );\r
-\r
- f1 = (Byte) ( 0xFF >> ( e1 & 7 ) );\r
- f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) );\r
-\r
- if ( ras.gray_min_x > c1 ) ras.gray_min_x = (short)c1;\r
- if ( ras.gray_max_x < c2 ) ras.gray_max_x = (short)c2;\r
-\r
- target = ras.bTarget + ras.traceOfs + c1;\r
- c2 -= c1;\r
-\r
- if ( c2 > 0 )\r
- {\r
- target[0] |= f1;\r
-\r
- /* memset() is slower than the following code on many platforms. */\r
- /* This is due to the fact that, in the vast majority of cases, */\r
- /* the span length in bytes is relatively small. */\r
- c2--;\r
- while ( c2 > 0 )\r
- {\r
- *(++target) = 0xFF;\r
- c2--;\r
- }\r
- target[1] |= f2;\r
- }\r
- else\r
- *target |= ( f1 & f2 );\r
- }\r
- }\r
-\r
-\r
- static void\r
- Vertical_Sweep_Drop( RAS_ARGS Short y,\r
- FT_F26Dot6 x1,\r
- FT_F26Dot6 x2,\r
- PProfile left,\r
- PProfile right )\r
- {\r
- Long e1, e2;\r
- Short c1, f1;\r
-\r
-\r
- /* Drop-out control */\r
-\r
- e1 = CEILING( x1 );\r
- e2 = FLOOR ( x2 );\r
-\r
- if ( e1 > e2 )\r
- {\r
- if ( e1 == e2 + ras.precision )\r
- {\r
- switch ( ras.dropOutControl )\r
- {\r
- case 1:\r
- e1 = e2;\r
- break;\r
-\r
- case 4:\r
- e1 = CEILING( (x1 + x2 + 1) / 2 );\r
- break;\r
-\r
- case 2:\r
- case 5:\r
- /* Drop-out Control Rule #4 */\r
-\r
- /* The spec is not very clear regarding rule #4. It */\r
- /* presents a method that is way too costly to implement */\r
- /* while the general idea seems to get rid of `stubs'. */\r
- /* */\r
- /* Here, we only get rid of stubs recognized if: */\r
- /* */\r
- /* upper stub: */\r
- /* */\r
- /* - P_Left and P_Right are in the same contour */\r
- /* - P_Right is the successor of P_Left in that contour */\r
- /* - y is the top of P_Left and P_Right */\r
- /* */\r
- /* lower stub: */\r
- /* */\r
- /* - P_Left and P_Right are in the same contour */\r
- /* - P_Left is the successor of P_Right in that contour */\r
- /* - y is the bottom of P_Left */\r
- /* */\r
-\r
- /* FIXXXME: uncommenting this line solves the disappearing */\r
- /* bit problem in the `7' of verdana 10pts, but */\r
- /* makes a new one in the `C' of arial 14pts */\r
-\r
-#if 0\r
- if ( x2 - x1 < ras.precision_half )\r
-#endif\r
- {\r
- /* upper stub test */\r
- if ( left->next == right && left->height <= 0 )\r
- return;\r
-\r
- /* lower stub test */\r
- if ( right->next == left && left->start == y )\r
- return;\r
- }\r
-\r
- /* check that the rightmost pixel isn't set */\r
-\r
- e1 = TRUNC( e1 );\r
-\r
- c1 = (Short)( e1 >> 3 );\r
- f1 = (Short)( e1 & 7 );\r
-\r
- if ( e1 >= 0 && e1 < ras.bWidth &&\r
- ras.bTarget[ras.traceOfs + c1] & ( 0x80 >> f1 ) )\r
- return;\r
-\r
- if ( ras.dropOutControl == 2 )\r
- e1 = e2;\r
- else\r
- e1 = CEILING( ( x1 + x2 + 1 ) / 2 );\r
-\r
- break;\r
-\r
- default:\r
- return; /* unsupported mode */\r
- }\r
- }\r
- else\r
- return;\r
- }\r
-\r
- e1 = TRUNC( e1 );\r
-\r
- if ( e1 >= 0 && e1 < ras.bWidth )\r
- {\r
- c1 = (Short)( e1 >> 3 );\r
- f1 = (Short)( e1 & 7 );\r
-\r
- if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1;\r
- if ( ras.gray_max_x < c1 ) ras.gray_max_x = c1;\r
-\r
- ras.bTarget[ras.traceOfs + c1] |= (char)( 0x80 >> f1 );\r
- }\r
- }\r
-\r
-\r
- static void\r
- Vertical_Sweep_Step( RAS_ARG )\r
- {\r
- ras.traceOfs += ras.traceIncr;\r
- }\r
-\r
-\r
- /***********************************************************************/\r
- /* */\r
- /* Horizontal Sweep Procedure Set */\r
- /* */\r
- /* These four routines are used during the horizontal black/white */\r
- /* sweep phase by the generic Draw_Sweep() function. */\r
- /* */\r
- /***********************************************************************/\r
-\r
- static void\r
- Horizontal_Sweep_Init( RAS_ARGS Short* min,\r
- Short* max )\r
- {\r
- /* nothing, really */\r
- FT_UNUSED_RASTER;\r
- FT_UNUSED( min );\r
- FT_UNUSED( max );\r
- }\r
-\r
-\r
- static void\r
- Horizontal_Sweep_Span( RAS_ARGS Short y,\r
- FT_F26Dot6 x1,\r
- FT_F26Dot6 x2,\r
- PProfile left,\r
- PProfile right )\r
- {\r
- Long e1, e2;\r
- PByte bits;\r
- Byte f1;\r
-\r
- FT_UNUSED( left );\r
- FT_UNUSED( right );\r
-\r
-\r
- if ( x2 - x1 < ras.precision )\r
- {\r
- e1 = CEILING( x1 );\r
- e2 = FLOOR ( x2 );\r
-\r
- if ( e1 == e2 )\r
- {\r
- bits = ras.bTarget + ( y >> 3 );\r
- f1 = (Byte)( 0x80 >> ( y & 7 ) );\r
-\r
- e1 = TRUNC( e1 );\r
-\r
- if ( e1 >= 0 && e1 < ras.target.rows )\r
- {\r
- PByte p;\r
-\r
-\r
- p = bits - e1*ras.target.pitch;\r
- if ( ras.target.pitch > 0 )\r
- p += ( ras.target.rows - 1 ) * ras.target.pitch;\r
-\r
- p[0] |= f1;\r
- }\r
- }\r
- }\r
- }\r
-\r
-\r
- static void\r
- Horizontal_Sweep_Drop( RAS_ARGS Short y,\r
- FT_F26Dot6 x1,\r
- FT_F26Dot6 x2,\r
- PProfile left,\r
- PProfile right )\r
- {\r
- Long e1, e2;\r
- PByte bits;\r
- Byte f1;\r
-\r
-\r
- /* During the horizontal sweep, we only take care of drop-outs */\r
-\r
- e1 = CEILING( x1 );\r
- e2 = FLOOR ( x2 );\r
-\r
- if ( e1 > e2 )\r
- {\r
- if ( e1 == e2 + ras.precision )\r
- {\r
- switch ( ras.dropOutControl )\r
- {\r
- case 1:\r
- e1 = e2;\r
- break;\r
-\r
- case 4:\r
- e1 = CEILING( ( x1 + x2 + 1 ) / 2 );\r
- break;\r
-\r
- case 2:\r
- case 5:\r
-\r
- /* Drop-out Control Rule #4 */\r
-\r
- /* The spec is not very clear regarding rule #4. It */\r
- /* presents a method that is way too costly to implement */\r
- /* while the general idea seems to get rid of `stubs'. */\r
- /* */\r
-\r
- /* rightmost stub test */\r
- if ( left->next == right && left->height <= 0 )\r
- return;\r
-\r
- /* leftmost stub test */\r
- if ( right->next == left && left->start == y )\r
- return;\r
-\r
- /* check that the rightmost pixel isn't set */\r
-\r
- e1 = TRUNC( e1 );\r
-\r
- bits = ras.bTarget + ( y >> 3 );\r
- f1 = (Byte)( 0x80 >> ( y & 7 ) );\r
-\r
- bits -= e1 * ras.target.pitch;\r
- if ( ras.target.pitch > 0 )\r
- bits += ( ras.target.rows - 1 ) * ras.target.pitch;\r
-\r
- if ( e1 >= 0 &&\r
- e1 < ras.target.rows &&\r
- *bits & f1 )\r
- return;\r
-\r
- if ( ras.dropOutControl == 2 )\r
- e1 = e2;\r
- else\r
- e1 = CEILING( ( x1 + x2 + 1 ) / 2 );\r
-\r
- break;\r
-\r
- default:\r
- return; /* unsupported mode */\r
- }\r
- }\r
- else\r
- return;\r
- }\r
-\r
- bits = ras.bTarget + ( y >> 3 );\r
- f1 = (Byte)( 0x80 >> ( y & 7 ) );\r
-\r
- e1 = TRUNC( e1 );\r
-\r
- if ( e1 >= 0 && e1 < ras.target.rows )\r
- {\r
- bits -= e1 * ras.target.pitch;\r
- if ( ras.target.pitch > 0 )\r
- bits += ( ras.target.rows - 1 ) * ras.target.pitch;\r
-\r
- bits[0] |= f1;\r
- }\r
- }\r
-\r
-\r
- static void\r
- Horizontal_Sweep_Step( RAS_ARG )\r
- {\r
- /* Nothing, really */\r
- FT_UNUSED_RASTER;\r
- }\r
-\r
-\r
-#ifdef FT_RASTER_OPTION_ANTI_ALIASING\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* Vertical Gray Sweep Procedure Set */\r
- /* */\r
- /* These two routines are used during the vertical gray-levels sweep */\r
- /* phase by the generic Draw_Sweep() function. */\r
- /* */\r
- /* NOTES */\r
- /* */\r
- /* - The target pixmap's width *must* be a multiple of 4. */\r
- /* */\r
- /* - You have to use the function Vertical_Sweep_Span() for the gray */\r
- /* span call. */\r
- /* */\r
- /*************************************************************************/\r
-\r
- static void\r
- Vertical_Gray_Sweep_Init( RAS_ARGS Short* min,\r
- Short* max )\r
- {\r
- Long pitch, byte_len;\r
-\r
-\r
- *min = *min & -2;\r
- *max = ( *max + 3 ) & -2;\r
-\r
- ras.traceOfs = 0;\r
- pitch = ras.target.pitch;\r
- byte_len = -pitch;\r
- ras.traceIncr = (Short)byte_len;\r
- ras.traceG = ( *min / 2 ) * byte_len;\r
-\r
- if ( pitch > 0 )\r
- {\r
- ras.traceG += ( ras.target.rows - 1 ) * pitch;\r
- byte_len = -byte_len;\r
- }\r
-\r
- ras.gray_min_x = (Short)byte_len;\r
- ras.gray_max_x = -(Short)byte_len;\r
- }\r
-\r
-\r
- static void\r
- Vertical_Gray_Sweep_Step( RAS_ARG )\r
- {\r
- Int c1, c2;\r
- PByte pix, bit, bit2;\r
- char* count = (char*)count_table;\r
- Byte* grays;\r
-\r
-\r
- ras.traceOfs += ras.gray_width;\r
-\r
- if ( ras.traceOfs > ras.gray_width )\r
- {\r
- pix = ras.gTarget + ras.traceG + ras.gray_min_x * 4;\r
- grays = ras.grays;\r
-\r
- if ( ras.gray_max_x >= 0 )\r
- {\r
- Long last_pixel = ras.target.width - 1;\r
- Int last_cell = last_pixel >> 2;\r
- Int last_bit = last_pixel & 3;\r
- Bool over = 0;\r
-\r
-\r
- if ( ras.gray_max_x >= last_cell && last_bit != 3 )\r
- {\r
- ras.gray_max_x = last_cell - 1;\r
- over = 1;\r
- }\r
-\r
- if ( ras.gray_min_x < 0 )\r
- ras.gray_min_x = 0;\r
-\r
- bit = ras.bTarget + ras.gray_min_x;\r
- bit2 = bit + ras.gray_width;\r
-\r
- c1 = ras.gray_max_x - ras.gray_min_x;\r
-\r
- while ( c1 >= 0 )\r
- {\r
- c2 = count[*bit] + count[*bit2];\r
-\r
- if ( c2 )\r
- {\r
- pix[0] = grays[(c2 >> 12) & 0x000F];\r
- pix[1] = grays[(c2 >> 8 ) & 0x000F];\r
- pix[2] = grays[(c2 >> 4 ) & 0x000F];\r
- pix[3] = grays[ c2 & 0x000F];\r
-\r
- *bit = 0;\r
- *bit2 = 0;\r
- }\r
-\r
- bit++;\r
- bit2++;\r
- pix += 4;\r
- c1--;\r
- }\r
-\r
- if ( over )\r
- {\r
- c2 = count[*bit] + count[*bit2];\r
- if ( c2 )\r
- {\r
- switch ( last_bit )\r
- {\r
- case 2:\r
- pix[2] = grays[(c2 >> 4 ) & 0x000F];\r
- case 1:\r
- pix[1] = grays[(c2 >> 8 ) & 0x000F];\r
- default:\r
- pix[0] = grays[(c2 >> 12) & 0x000F];\r
- }\r
-\r
- *bit = 0;\r
- *bit2 = 0;\r
- }\r
- }\r
- }\r
-\r
- ras.traceOfs = 0;\r
- ras.traceG += ras.traceIncr;\r
-\r
- ras.gray_min_x = 32000;\r
- ras.gray_max_x = -32000;\r
- }\r
- }\r
-\r
-\r
- static void\r
- Horizontal_Gray_Sweep_Span( RAS_ARGS Short y,\r
- FT_F26Dot6 x1,\r
- FT_F26Dot6 x2,\r
- PProfile left,\r
- PProfile right )\r
- {\r
- /* nothing, really */\r
- FT_UNUSED_RASTER;\r
- FT_UNUSED( y );\r
- FT_UNUSED( x1 );\r
- FT_UNUSED( x2 );\r
- FT_UNUSED( left );\r
- FT_UNUSED( right );\r
- }\r
-\r
-\r
- static void\r
- Horizontal_Gray_Sweep_Drop( RAS_ARGS Short y,\r
- FT_F26Dot6 x1,\r
- FT_F26Dot6 x2,\r
- PProfile left,\r
- PProfile right )\r
- {\r
- Long e1, e2;\r
- PByte pixel;\r
- Byte color;\r
-\r
-\r
- /* During the horizontal sweep, we only take care of drop-outs */\r
- e1 = CEILING( x1 );\r
- e2 = FLOOR ( x2 );\r
-\r
- if ( e1 > e2 )\r
- {\r
- if ( e1 == e2 + ras.precision )\r
- {\r
- switch ( ras.dropOutControl )\r
- {\r
- case 1:\r
- e1 = e2;\r
- break;\r
-\r
- case 4:\r
- e1 = CEILING( ( x1 + x2 + 1 ) / 2 );\r
- break;\r
-\r
- case 2:\r
- case 5:\r
-\r
- /* Drop-out Control Rule #4 */\r
-\r
- /* The spec is not very clear regarding rule #4. It */\r
- /* presents a method that is way too costly to implement */\r
- /* while the general idea seems to get rid of `stubs'. */\r
- /* */\r
-\r
- /* rightmost stub test */\r
- if ( left->next == right && left->height <= 0 )\r
- return;\r
-\r
- /* leftmost stub test */\r
- if ( right->next == left && left->start == y )\r
- return;\r
-\r
- if ( ras.dropOutControl == 2 )\r
- e1 = e2;\r
- else\r
- e1 = CEILING( ( x1 + x2 + 1 ) / 2 );\r
-\r
- break;\r
-\r
- default:\r
- return; /* unsupported mode */\r
- }\r
- }\r
- else\r
- return;\r
- }\r
-\r
- if ( e1 >= 0 )\r
- {\r
- if ( x2 - x1 >= ras.precision_half )\r
- color = ras.grays[2];\r
- else\r
- color = ras.grays[1];\r
-\r
- e1 = TRUNC( e1 ) / 2;\r
- if ( e1 < ras.target.rows )\r
- {\r
- pixel = ras.gTarget - e1 * ras.target.pitch + y / 2;\r
- if ( ras.target.pitch > 0 )\r
- pixel += ( ras.target.rows - 1 ) * ras.target.pitch;\r
-\r
- if ( pixel[0] == ras.grays[0] )\r
- pixel[0] = color;\r
- }\r
- }\r
- }\r
-\r
-\r
-#endif /* FT_RASTER_OPTION_ANTI_ALIASING */\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* Generic Sweep Drawing routine */\r
- /* */\r
- /*************************************************************************/\r
-\r
- static Bool\r
- Draw_Sweep( RAS_ARG )\r
- {\r
- Short y, y_change, y_height;\r
-\r
- PProfile P, Q, P_Left, P_Right;\r
-\r
- Short min_Y, max_Y, top, bottom, dropouts;\r
-\r
- Long x1, x2, xs, e1, e2;\r
-\r
- TProfileList waiting;\r
- TProfileList draw_left, draw_right;\r
-\r
-\r
- /* Init empty linked lists */\r
-\r
- Init_Linked( &waiting );\r
-\r
- Init_Linked( &draw_left );\r
- Init_Linked( &draw_right );\r
-\r
- /* first, compute min and max Y */\r
-\r
- P = ras.fProfile;\r
- max_Y = (Short)TRUNC( ras.minY );\r
- min_Y = (Short)TRUNC( ras.maxY );\r
-\r
- while ( P )\r
- {\r
- Q = P->link;\r
-\r
- bottom = (Short)P->start;\r
- top = (Short)( P->start + P->height - 1 );\r
-\r
- if ( min_Y > bottom ) min_Y = bottom;\r
- if ( max_Y < top ) max_Y = top;\r
-\r
- P->X = 0;\r
- InsNew( &waiting, P );\r
-\r
- P = Q;\r
- }\r
-\r
- /* Check the Y-turns */\r
- if ( ras.numTurns == 0 )\r
- {\r
- ras.error = Raster_Err_Invalid;\r
- return FAILURE;\r
- }\r
-\r
- /* Now inits the sweep */\r
-\r
- ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y );\r
-\r
- /* Then compute the distance of each profile from min_Y */\r
-\r
- P = waiting;\r
-\r
- while ( P )\r
- {\r
- P->countL = (UShort)( P->start - min_Y );\r
- P = P->link;\r
- }\r
-\r
- /* Let's go */\r
-\r
- y = min_Y;\r
- y_height = 0;\r
-\r
- if ( ras.numTurns > 0 &&\r
- ras.sizeBuff[-ras.numTurns] == min_Y )\r
- ras.numTurns--;\r
-\r
- while ( ras.numTurns > 0 )\r
- {\r
- /* look in the waiting list for new activations */\r
-\r
- P = waiting;\r
-\r
- while ( P )\r
- {\r
- Q = P->link;\r
- P->countL -= y_height;\r
- if ( P->countL == 0 )\r
- {\r
- DelOld( &waiting, P );\r
-\r
- switch ( P->flow )\r
- {\r
- case Flow_Up:\r
- InsNew( &draw_left, P );\r
- break;\r
-\r
- case Flow_Down:\r
- InsNew( &draw_right, P );\r
- break;\r
- }\r
- }\r
-\r
- P = Q;\r
- }\r
-\r
- /* Sort the drawing lists */\r
-\r
- Sort( &draw_left );\r
- Sort( &draw_right );\r
-\r
- y_change = (Short)ras.sizeBuff[-ras.numTurns--];\r
- y_height = (Short)( y_change - y );\r
-\r
- while ( y < y_change )\r
- {\r
- /* Let's trace */\r
-\r
- dropouts = 0;\r
-\r
- P_Left = draw_left;\r
- P_Right = draw_right;\r
-\r
- while ( P_Left )\r
- {\r
- x1 = P_Left ->X;\r
- x2 = P_Right->X;\r
-\r
- if ( x1 > x2 )\r
- {\r
- xs = x1;\r
- x1 = x2;\r
- x2 = xs;\r
- }\r
-\r
- if ( x2 - x1 <= ras.precision )\r
- {\r
- e1 = FLOOR( x1 );\r
- e2 = CEILING( x2 );\r
-\r
- if ( ras.dropOutControl != 0 &&\r
- ( e1 > e2 || e2 == e1 + ras.precision ) )\r
- {\r
- /* a drop out was detected */\r
-\r
- P_Left ->X = x1;\r
- P_Right->X = x2;\r
-\r
- /* mark profile for drop-out processing */\r
- P_Left->countL = 1;\r
- dropouts++;\r
-\r
- goto Skip_To_Next;\r
- }\r
- }\r
-\r
- ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right );\r
-\r
- Skip_To_Next:\r
-\r
- P_Left = P_Left->link;\r
- P_Right = P_Right->link;\r
- }\r
-\r
- /* now perform the dropouts _after_ the span drawing -- */\r
- /* drop-outs processing has been moved out of the loop */\r
- /* for performance tuning */\r
- if ( dropouts > 0 )\r
- goto Scan_DropOuts;\r
-\r
- Next_Line:\r
-\r
- ras.Proc_Sweep_Step( RAS_VAR );\r
-\r
- y++;\r
-\r
- if ( y < y_change )\r
- {\r
- Sort( &draw_left );\r
- Sort( &draw_right );\r
- }\r
- }\r
-\r
- /* Now finalize the profiles that needs it */\r
-\r
- P = draw_left;\r
- while ( P )\r
- {\r
- Q = P->link;\r
- if ( P->height == 0 )\r
- DelOld( &draw_left, P );\r
- P = Q;\r
- }\r
-\r
- P = draw_right;\r
- while ( P )\r
- {\r
- Q = P->link;\r
- if ( P->height == 0 )\r
- DelOld( &draw_right, P );\r
- P = Q;\r
- }\r
- }\r
-\r
- /* for gray-scaling, flushes the bitmap scanline cache */\r
- while ( y <= max_Y )\r
- {\r
- ras.Proc_Sweep_Step( RAS_VAR );\r
- y++;\r
- }\r
-\r
- return SUCCESS;\r
-\r
- Scan_DropOuts:\r
-\r
- P_Left = draw_left;\r
- P_Right = draw_right;\r
-\r
- while ( P_Left )\r
- {\r
- if ( P_Left->countL )\r
- {\r
- P_Left->countL = 0;\r
-#if 0\r
- dropouts--; /* -- this is useful when debugging only */\r
-#endif\r
- ras.Proc_Sweep_Drop( RAS_VARS y,\r
- P_Left->X,\r
- P_Right->X,\r
- P_Left,\r
- P_Right );\r
- }\r
-\r
- P_Left = P_Left->link;\r
- P_Right = P_Right->link;\r
- }\r
-\r
- goto Next_Line;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Render_Single_Pass */\r
- /* */\r
- /* <Description> */\r
- /* Performs one sweep with sub-banding. */\r
- /* */\r
- /* <Input> */\r
- /* flipped :: If set, flip the direction of the outline. */\r
- /* */\r
- /* <Return> */\r
- /* Renderer error code. */\r
- /* */\r
- static int\r
- Render_Single_Pass( RAS_ARGS Bool flipped )\r
- {\r
- Short i, j, k;\r
-\r
-\r
- while ( ras.band_top >= 0 )\r
- {\r
- ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision;\r
- ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision;\r
-\r
- ras.top = ras.buff;\r
-\r
- ras.error = Raster_Err_None;\r
-\r
- if ( Convert_Glyph( RAS_VARS flipped ) )\r
- {\r
- if ( ras.error != Raster_Err_Overflow )\r
- return FAILURE;\r
-\r
- ras.error = Raster_Err_None;\r
-\r
- /* sub-banding */\r
-\r
-#ifdef DEBUG_RASTER\r
- ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) );\r
-#endif\r
-\r
- i = ras.band_stack[ras.band_top].y_min;\r
- j = ras.band_stack[ras.band_top].y_max;\r
-\r
- k = (Short)( ( i + j ) / 2 );\r
-\r
- if ( ras.band_top >= 7 || k < i )\r
- {\r
- ras.band_top = 0;\r
- ras.error = Raster_Err_Invalid;\r
-\r
- return ras.error;\r
- }\r
-\r
- ras.band_stack[ras.band_top + 1].y_min = k;\r
- ras.band_stack[ras.band_top + 1].y_max = j;\r
-\r
- ras.band_stack[ras.band_top].y_max = (Short)( k - 1 );\r
-\r
- ras.band_top++;\r
- }\r
- else\r
- {\r
- if ( ras.fProfile )\r
- if ( Draw_Sweep( RAS_VAR ) )\r
- return ras.error;\r
- ras.band_top--;\r
- }\r
- }\r
-\r
- return SUCCESS;\r
- }\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Render_Glyph */\r
- /* */\r
- /* <Description> */\r
- /* Renders a glyph in a bitmap. Sub-banding if needed. */\r
- /* */\r
- /* <Return> */\r
- /* FreeType error code. 0 means success. */\r
- /* */\r
- FT_LOCAL_DEF( FT_Error )\r
- Render_Glyph( RAS_ARG )\r
- {\r
- FT_Error error;\r
-\r
-\r
- Set_High_Precision( RAS_VARS ras.outline.flags &\r
- FT_OUTLINE_HIGH_PRECISION );\r
- ras.scale_shift = ras.precision_shift;\r
- /* Drop-out mode 2 is hard-coded since this is the only mode used */\r
- /* on Windows platforms. Using other modes, as specified by the */\r
- /* font, results in misplaced pixels. */\r
- ras.dropOutControl = 2;\r
- ras.second_pass = (FT_Byte)( !( ras.outline.flags &\r
- FT_OUTLINE_SINGLE_PASS ) );\r
-\r
- /* Vertical Sweep */\r
- ras.Proc_Sweep_Init = Vertical_Sweep_Init;\r
- ras.Proc_Sweep_Span = Vertical_Sweep_Span;\r
- ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;\r
- ras.Proc_Sweep_Step = Vertical_Sweep_Step;\r
-\r
- ras.band_top = 0;\r
- ras.band_stack[0].y_min = 0;\r
- ras.band_stack[0].y_max = (short)( ras.target.rows - 1 );\r
-\r
- ras.bWidth = (unsigned short)ras.target.width;\r
- ras.bTarget = (Byte*)ras.target.buffer;\r
-\r
- if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 )\r
- return error;\r
-\r
- /* Horizontal Sweep */\r
- if ( ras.second_pass && ras.dropOutControl != 0 )\r
- {\r
- ras.Proc_Sweep_Init = Horizontal_Sweep_Init;\r
- ras.Proc_Sweep_Span = Horizontal_Sweep_Span;\r
- ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;\r
- ras.Proc_Sweep_Step = Horizontal_Sweep_Step;\r
-\r
- ras.band_top = 0;\r
- ras.band_stack[0].y_min = 0;\r
- ras.band_stack[0].y_max = (short)( ras.target.width - 1 );\r
-\r
- if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 )\r
- return error;\r
- }\r
-\r
- return Raster_Err_None;\r
- }\r
-\r
-\r
-#ifdef FT_RASTER_OPTION_ANTI_ALIASING\r
-\r
-\r
- /*************************************************************************/\r
- /* */\r
- /* <Function> */\r
- /* Render_Gray_Glyph */\r
- /* */\r
- /* <Description> */\r
- /* Renders a glyph with grayscaling. Sub-banding if needed. */\r
- /* */\r
- /* <Return> */\r
- /* FreeType error code. 0 means success. */\r
- /* */\r
- FT_LOCAL_DEF( FT_Error )\r
- Render_Gray_Glyph( RAS_ARG )\r
- {\r
- Long pixel_width;\r
- FT_Error error;\r
-\r
-\r
- Set_High_Precision( RAS_VARS ras.outline.flags &\r
- FT_OUTLINE_HIGH_PRECISION );\r
- ras.scale_shift = ras.precision_shift + 1;\r
- /* Drop-out mode 2 is hard-coded since this is the only mode used */\r
- /* on Windows platforms. Using other modes, as specified by the */\r
- /* font, results in misplaced pixels. */\r
- ras.dropOutControl = 2;\r
- ras.second_pass = !( ras.outline.flags & FT_OUTLINE_SINGLE_PASS );\r
-\r
- /* Vertical Sweep */\r
-\r
- ras.band_top = 0;\r
- ras.band_stack[0].y_min = 0;\r
- ras.band_stack[0].y_max = 2 * ras.target.rows - 1;\r
-\r
- ras.bWidth = ras.gray_width;\r
- pixel_width = 2 * ( ( ras.target.width + 3 ) >> 2 );\r
-\r
- if ( ras.bWidth > pixel_width )\r
- ras.bWidth = pixel_width;\r
-\r
- ras.bWidth = ras.bWidth * 8;\r
- ras.bTarget = (Byte*)ras.gray_lines;\r
- ras.gTarget = (Byte*)ras.target.buffer;\r
-\r
- ras.Proc_Sweep_Init = Vertical_Gray_Sweep_Init;\r
- ras.Proc_Sweep_Span = Vertical_Sweep_Span;\r
- ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;\r
- ras.Proc_Sweep_Step = Vertical_Gray_Sweep_Step;\r
-\r
- error = Render_Single_Pass( RAS_VARS 0 );\r
- if ( error )\r
- return error;\r
-\r
- /* Horizontal Sweep */\r
- if ( ras.second_pass && ras.dropOutControl != 0 )\r
- {\r
- ras.Proc_Sweep_Init = Horizontal_Sweep_Init;\r
- ras.Proc_Sweep_Span = Horizontal_Gray_Sweep_Span;\r
- ras.Proc_Sweep_Drop = Horizontal_Gray_Sweep_Drop;\r
- ras.Proc_Sweep_Step = Horizontal_Sweep_Step;\r
-\r
- ras.band_top = 0;\r
- ras.band_stack[0].y_min = 0;\r
- ras.band_stack[0].y_max = ras.target.width * 2 - 1;\r
-\r
- error = Render_Single_Pass( RAS_VARS 1 );\r
- if ( error )\r
- return error;\r
- }\r
-\r
- return Raster_Err_None;\r
- }\r
-\r
-#else /* !FT_RASTER_OPTION_ANTI_ALIASING */\r
-\r
- FT_LOCAL_DEF( FT_Error )\r
- Render_Gray_Glyph( RAS_ARG )\r
- {\r
- FT_UNUSED_RASTER;\r
-\r
- return Raster_Err_Unsupported;\r
- }\r
-\r
-#endif /* !FT_RASTER_OPTION_ANTI_ALIASING */\r
-\r
-\r
- static void\r
- ft_black_init( PRaster raster )\r
- {\r
- FT_UNUSED( raster );\r
-\r
-#ifdef FT_RASTER_OPTION_ANTI_ALIASING\r
- FT_UInt n;\r
-\r
-\r
- /* set default 5-levels gray palette */\r
- for ( n = 0; n < 5; n++ )\r
- raster->grays[n] = n * 255 / 4;\r
-\r
- raster->gray_width = RASTER_GRAY_LINES / 2;\r
-\r
-#endif\r
- }\r
-\r
-\r
- /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/\r
- /**** a static object. *****/\r
-\r
-\r
-#ifdef _STANDALONE_\r
-\r
-\r
- static int\r
- ft_black_new( void* memory,\r
- FT_Raster *araster )\r
- {\r
- static TRaster the_raster;\r
-\r
-\r
- *araster = (FT_Raster)&the_raster;\r
- FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) );\r
- ft_black_init( &the_raster );\r
-\r
- return 0;\r
- }\r
-\r
-\r
- static void\r
- ft_black_done( FT_Raster raster )\r
- {\r
- /* nothing */\r
- FT_UNUSED( raster );\r
- }\r
-\r
-\r
-#else /* _STANDALONE_ */\r
-\r
-\r
- static int\r
- ft_black_new( FT_Memory memory,\r
- PRaster *araster )\r
- {\r
- FT_Error error;\r
- PRaster raster;\r
-\r
-\r
- *araster = 0;\r
- if ( !FT_NEW( raster ) )\r
- {\r
- raster->memory = memory;\r
- ft_black_init( raster );\r
-\r
- *araster = raster;\r
- }\r
-\r
- return error;\r
- }\r
-\r
-\r
- static void\r
- ft_black_done( PRaster raster )\r
- {\r
- FT_Memory memory = (FT_Memory)raster->memory;\r
- FT_FREE( raster );\r
- }\r
-\r
-\r
-#endif /* _STANDALONE_ */\r
-\r
-\r
- static void\r
- ft_black_reset( PRaster raster,\r
- char* pool_base,\r
- long pool_size )\r
- {\r
- if ( raster )\r
- {\r
- if ( pool_base && pool_size >= (long)sizeof(TWorker) + 2048 )\r
- {\r
- PWorker worker = (PWorker)pool_base;\r
-\r
-\r
- raster->buffer = pool_base + ( (sizeof ( *worker ) + 7 ) & ~7 );\r
- raster->buffer_size = ( ( pool_base + pool_size ) -\r
- (char*)raster->buffer ) / sizeof ( Long );\r
- raster->worker = worker;\r
- }\r
- else\r
- {\r
- raster->buffer = NULL;\r
- raster->buffer_size = 0;\r
- raster->worker = NULL;\r
- }\r
- }\r
- }\r
-\r
-\r
- static void\r
- ft_black_set_mode( PRaster raster,\r
- unsigned long mode,\r
- const char* palette )\r
- {\r
-#ifdef FT_RASTER_OPTION_ANTI_ALIASING\r
-\r
- if ( mode == FT_MAKE_TAG( 'p', 'a', 'l', '5' ) )\r
- {\r
- /* set 5-levels gray palette */\r
- raster->grays[0] = palette[0];\r
- raster->grays[1] = palette[1];\r
- raster->grays[2] = palette[2];\r
- raster->grays[3] = palette[3];\r
- raster->grays[4] = palette[4];\r
- }\r
-\r
-#else\r
-\r
- FT_UNUSED( raster );\r
- FT_UNUSED( mode );\r
- FT_UNUSED( palette );\r
-\r
-#endif\r
- }\r
-\r
-\r
- static int\r
- ft_black_render( PRaster raster,\r
- const FT_Raster_Params* params )\r
- {\r
- const FT_Outline* outline = (const FT_Outline*)params->source;\r
- const FT_Bitmap* target_map = params->target;\r
- PWorker worker;\r
-\r
-\r
- if ( !raster || !raster->buffer || !raster->buffer_size )\r
- return Raster_Err_Not_Ini;\r
-\r
- /* return immediately if the outline is empty */\r
- if ( outline->n_points == 0 || outline->n_contours <= 0 )\r
- return Raster_Err_None;\r
-\r
- if ( !outline || !outline->contours || !outline->points )\r
- return Raster_Err_Invalid;\r
-\r
- if ( outline->n_points != outline->contours[outline->n_contours - 1] + 1 )\r
- return Raster_Err_Invalid;\r
-\r
- worker = raster->worker;\r
-\r
- /* this version of the raster does not support direct rendering, sorry */\r
- if ( params->flags & FT_RASTER_FLAG_DIRECT )\r
- return Raster_Err_Unsupported;\r
-\r
- if ( !target_map || !target_map->buffer )\r
- return Raster_Err_Invalid;\r
-\r
- ras.outline = *outline;\r
- ras.target = *target_map;\r
-\r
- worker->buff = (PLong) raster->buffer;\r
- worker->sizeBuff = worker->buff +\r
- raster->buffer_size / sizeof ( Long );\r
-#ifdef FT_RASTER_OPTION_ANTI_ALIASING\r
- worker->grays = raster->grays;\r
- worker->gray_width = raster->gray_width;\r
-#endif\r
-\r
- return ( ( params->flags & FT_RASTER_FLAG_AA )\r
- ? Render_Gray_Glyph( RAS_VAR )\r
- : Render_Glyph( RAS_VAR ) );\r
- }\r
-\r
-\r
- const FT_Raster_Funcs ft_standard_raster =\r
- {\r
- FT_GLYPH_FORMAT_OUTLINE,\r
- (FT_Raster_New_Func) ft_black_new,\r
- (FT_Raster_Reset_Func) ft_black_reset,\r
- (FT_Raster_Set_Mode_Func)ft_black_set_mode,\r
- (FT_Raster_Render_Func) ft_black_render,\r
- (FT_Raster_Done_Func) ft_black_done\r
- };\r
-\r
-\r
-/* END */\r
+/***************************************************************************/
+/* */
+/* ftraster.c */
+/* */
+/* The FreeType glyph rasterizer (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2005, 2007 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. */
+/* */
+/***************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* This file can be compiled without the rest of the FreeType engine, by */
+ /* defining the _STANDALONE_ macro when compiling it. You also need to */
+ /* put the files `ftimage.h' and `ftmisc.h' into the $(incdir) */
+ /* directory. Typically, you should do something like */
+ /* */
+ /* - copy `src/raster/ftraster.c' (this file) to your current directory */
+ /* */
+ /* - copy `include/freetype/ftimage.h' and `src/raster/ftmisc.h' */
+ /* to your current directory */
+ /* */
+ /* - compile `ftraster' with the _STANDALONE_ macro defined, as in */
+ /* */
+ /* cc -c -D_STANDALONE_ ftraster.c */
+ /* */
+ /* The renderer can be initialized with a call to */
+ /* `ft_standard_raster.raster_new'; a bitmap can be generated */
+ /* with a call to `ft_standard_raster.raster_render'. */
+ /* */
+ /* See the comments and documentation in the file `ftimage.h' for more */
+ /* details on how the raster works. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This is a rewrite of the FreeType 1.x scan-line converter */
+ /* */
+ /*************************************************************************/
+
+#ifdef _STANDALONE_
+
+#include "ftmisc.h"
+#include "ftimage.h"
+
+#else /* !_STANDALONE_ */
+
+#include <ft2build.h>
+#include "ftraster.h"
+#include FT_INTERNAL_CALC_H /* for FT_MulDiv only */
+
+#endif /* !_STANDALONE_ */
+
+
+ /*************************************************************************/
+ /* */
+ /* A simple technical note on how the raster works */
+ /* ----------------------------------------------- */
+ /* */
+ /* Converting an outline into a bitmap is achieved in several steps: */
+ /* */
+ /* 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 whether it 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. Additionally, 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 top */
+ /* */
+ /* The top of the profile stack is kept in the `top' 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 `y-turns' 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 | */
+ /* _ _ __________________|____________________| */
+ /* */
+ /* ^ ^ */
+ /* | | */
+ /* maxBuff sizeBuff = end of 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 properly. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /** **/
+ /** CONFIGURATION MACROS **/
+ /** **/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* define DEBUG_RASTER if you want to compile a debugging version */
+#define xxxDEBUG_RASTER
+
+ /* undefine FT_RASTER_OPTION_ANTI_ALIASING if you do not want to support */
+ /* 5-levels anti-aliasing */
+#ifdef FT_CONFIG_OPTION_5_GRAY_LEVELS
+#define FT_RASTER_OPTION_ANTI_ALIASING
+#endif
+
+ /* The size of the two-lines intermediate bitmap used */
+ /* for anti-aliasing, in bytes. */
+#define RASTER_GRAY_LINES 2048
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /** **/
+ /** OTHER MACROS (do not change) **/
+ /** **/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_raster
+
+
+#ifdef _STANDALONE_
+
+
+ /* This macro is used to indicate that a function parameter is unused. */
+ /* Its purpose is simply to reduce compiler warnings. Note also that */
+ /* simply defining it as `(void)x' doesn't avoid warnings with certain */
+ /* ANSI compilers (e.g. LCC). */
+#define FT_UNUSED( x ) (x) = (x)
+
+ /* Disable the tracing mechanism for simplicity -- developers can */
+ /* activate it easily by redefining these two macros. */
+#ifndef FT_ERROR
+#define FT_ERROR( x ) do ; while ( 0 ) /* nothing */
+#endif
+
+#ifndef FT_TRACE
+#define FT_TRACE( x ) do ; while ( 0 ) /* nothing */
+#define FT_TRACE1( x ) do ; while ( 0 ) /* nothing */
+#define FT_TRACE6( x ) do ; while ( 0 ) /* nothing */
+#endif
+
+#define Raster_Err_None 0
+#define Raster_Err_Not_Ini -1
+#define Raster_Err_Overflow -2
+#define Raster_Err_Neg_Height -3
+#define Raster_Err_Invalid -4
+#define Raster_Err_Unsupported -5
+
+#define ft_memset memset
+
+#else /* _STANDALONE_ */
+
+
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H /* for FT_TRACE() and FT_ERROR() */
+
+#include "rasterrs.h"
+
+#define Raster_Err_None Raster_Err_Ok
+#define Raster_Err_Not_Ini Raster_Err_Raster_Uninitialized
+#define Raster_Err_Overflow Raster_Err_Raster_Overflow
+#define Raster_Err_Neg_Height Raster_Err_Raster_Negative_Height
+#define Raster_Err_Invalid Raster_Err_Invalid_Outline
+#define Raster_Err_Unsupported Raster_Err_Cannot_Render_Glyph
+
+
+#endif /* _STANDALONE_ */
+
+
+#ifndef FT_MEM_SET
+#define FT_MEM_SET( d, s, c ) ft_memset( d, s, c )
+#endif
+
+#ifndef FT_MEM_ZERO
+#define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count )
+#endif
+
+ /* FMulDiv means `Fast MulDiv'; it is used in case where `b' is */
+ /* typically a small value and the result of a*b is known to fit into */
+ /* 32 bits. */
+#define FMulDiv( a, b, c ) ( (a) * (b) / (c) )
+
+ /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */
+ /* for clipping computations. It simply uses the FT_MulDiv() function */
+ /* defined in `ftcalc.h'. */
+#define SMulDiv FT_MulDiv
+
+ /* The rasterizer is a very general purpose component; please leave */
+ /* the following redefinitions there (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 SUCCESS
+#define SUCCESS 0
+#endif
+
+#ifndef FAILURE
+#define FAILURE 1
+#endif
+
+
+#define MaxBezier 32 /* The maximum number of stacked Bezier curves. */
+ /* Setting this constant to more than 32 is a */
+ /* pure waste of space. */
+
+#define Pixel_Bits 6 /* fractional bits of *input* coordinates */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /** **/
+ /** SIMPLE TYPE DECLARATIONS **/
+ /** **/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef int Int;
+ typedef unsigned int UInt;
+ typedef short Short;
+ typedef unsigned short UShort, *PUShort;
+ typedef long Long, *PLong;
+ typedef unsigned long ULong;
+
+ typedef unsigned char Byte, *PByte;
+ typedef char Bool;
+
+
+ typedef union Alignment_
+ {
+ long l;
+ void* p;
+ void (*f)(void);
+
+ } Alignment, *PAlignment;
+
+
+ typedef struct TPoint_
+ {
+ Long x;
+ Long y;
+
+ } TPoint;
+
+
+ typedef enum TFlow_
+ {
+ Flow_None = 0,
+ Flow_Up = 1,
+ Flow_Down = -1
+
+ } TFlow;
+
+
+ /* States of each line, arc, and profile */
+ typedef enum TStates_
+ {
+ Unknown_State,
+ Ascending_State,
+ Descending_State,
+ Flat_State
+
+ } TStates;
+
+
+ typedef struct TProfile_ TProfile;
+ typedef TProfile* PProfile;
+
+ struct TProfile_
+ {
+ FT_F26Dot6 X; /* current coordinate during sweep */
+ PProfile link; /* link to next profile - various purpose */
+ PLong offset; /* start of profile's data in render pool */
+ int flow; /* Profile orientation: Asc/Descending */
+ long height; /* profile's height in scanlines */
+ long start; /* profile's starting scanline */
+
+ unsigned 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;
+
+
+ /* Simple record used to implement a stack of bands, required */
+ /* by the sub-banding mechanism */
+ typedef struct TBand_
+ {
+ Short y_min; /* band's minimum */
+ Short y_max; /* band's maximum */
+
+ } TBand;
+
+
+#define AlignProfileSize \
+ ( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( long ) )
+
+
+#ifdef FT_STATIC_RASTER
+
+
+#define RAS_ARGS /* void */
+#define RAS_ARG /* void */
+
+#define RAS_VARS /* void */
+#define RAS_VAR /* void */
+
+#define FT_UNUSED_RASTER do ; while ( 0 )
+
+
+#else /* FT_STATIC_RASTER */
+
+
+#define RAS_ARGS PWorker worker,
+#define RAS_ARG PWorker worker
+
+#define RAS_VARS worker,
+#define RAS_VAR worker
+
+#define FT_UNUSED_RASTER FT_UNUSED( worker )
+
+
+#endif /* FT_STATIC_RASTER */
+
+
+ typedef struct TWorker_ TWorker, *PWorker;
+
+
+ /* prototypes used for sweep function dispatch */
+ typedef void
+ Function_Sweep_Init( RAS_ARGS Short* min,
+ Short* max );
+
+ typedef void
+ Function_Sweep_Span( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right );
+
+ typedef void
+ Function_Sweep_Step( RAS_ARG );
+
+
+ /* NOTE: These operations are only valid on 2's complement processors */
+
+#define FLOOR( x ) ( (x) & -ras.precision )
+#define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision )
+#define TRUNC( x ) ( (signed long)(x) >> ras.precision_bits )
+#define FRAC( x ) ( (x) & ( ras.precision - 1 ) )
+#define SCALED( x ) ( ( (x) << ras.scale_shift ) - ras.precision_half )
+
+ /* Note that I have moved the location of some fields in the */
+ /* structure to ensure that the most used variables are used */
+ /* at the top. Thus, their offset can be coded with less */
+ /* opcodes, and it results in a smaller executable. */
+
+ struct TWorker_
+ {
+ Int precision_bits; /* precision related variables */
+ Int precision;
+ Int precision_half;
+ Long precision_mask;
+ Int precision_shift;
+ Int precision_step;
+ Int precision_jitter;
+
+ Int scale_shift; /* == precision_shift for bitmaps */
+ /* == precision_shift+1 for pixmaps */
+
+ PLong buff; /* The profiles buffer */
+ PLong sizeBuff; /* Render pool size */
+ PLong maxBuff; /* Profiles buffer size */
+ PLong top; /* Current cursor in buffer */
+
+ FT_Error error;
+
+ Int numTurns; /* number of Y-turns in outline */
+
+ TPoint* arc; /* current Bezier arc pointer */
+
+ UShort bWidth; /* target bitmap width */
+ PByte bTarget; /* target bitmap buffer */
+ PByte gTarget; /* target pixmap buffer */
+
+ Long lastX, lastY, minY, maxY;
+
+ UShort num_Profs; /* current number of profiles */
+
+ Bool fresh; /* signals a fresh new profile which */
+ /* 'start' field must be completed */
+ Bool joint; /* signals that the last arc ended */
+ /* exactly on a scanline. Allows */
+ /* removal of doublets */
+ PProfile cProfile; /* current profile */
+ PProfile fProfile; /* head of linked list of profiles */
+ PProfile gProfile; /* contour's first profile in case */
+ /* of impact */
+
+ TStates state; /* rendering state */
+
+ FT_Bitmap target; /* description of target bit/pixmap */
+ FT_Outline outline;
+
+ Long traceOfs; /* current offset in target bitmap */
+ Long traceG; /* current offset in target pixmap */
+
+ Short traceIncr; /* sweep's increment in target bitmap */
+
+ Short gray_min_x; /* current min x during gray rendering */
+ Short gray_max_x; /* current max x during gray rendering */
+
+ /* dispatch variables */
+
+ Function_Sweep_Init* Proc_Sweep_Init;
+ Function_Sweep_Span* Proc_Sweep_Span;
+ Function_Sweep_Span* Proc_Sweep_Drop;
+ Function_Sweep_Step* Proc_Sweep_Step;
+
+ Byte dropOutControl; /* current drop_out control method */
+
+ Bool 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. */
+
+ TPoint arcs[3 * MaxBezier + 1]; /* The Bezier stack */
+
+ TBand band_stack[16]; /* band stack used for sub-banding */
+ Int band_top; /* band stack top */
+
+#ifdef FT_RASTER_OPTION_ANTI_ALIASING
+
+ Byte* grays;
+
+ Byte gray_lines[RASTER_GRAY_LINES];
+ /* Intermediate table used to render the */
+ /* graylevels pixmaps. */
+ /* gray_lines is a buffer holding two */
+ /* monochrome scanlines */
+
+ Short gray_width; /* width in bytes of one monochrome */
+ /* intermediate scanline of gray_lines. */
+ /* Each gray pixel takes 2 bits long there */
+
+ /* The gray_lines must hold 2 lines, thus with size */
+ /* in bytes of at least `gray_width*2'. */
+
+#endif /* FT_RASTER_ANTI_ALIASING */
+
+ };
+
+
+ typedef struct TRaster_
+ {
+ char* buffer;
+ long buffer_size;
+ void* memory;
+ PWorker worker;
+ Byte grays[5];
+ Short gray_width;
+
+ } TRaster, *PRaster;
+
+#ifdef FT_STATIC_RASTER
+
+ static TWorker cur_ras;
+#define ras cur_ras
+
+#else
+
+#define ras (*worker)
+
+#endif /* FT_STATIC_RASTER */
+
+
+static const char count_table[256] =
+{
+ 0 , 1 , 1 , 2 , 1 , 2 , 2 , 3 , 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4,
+ 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,
+ 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,
+ 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
+ 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,
+ 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
+ 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
+ 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,
+ 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,
+ 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
+ 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
+ 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,
+ 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
+ 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,
+ 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,
+ 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 , 5 , 6 , 6 , 7 , 6 , 7 , 7 , 8 };
+
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /** **/
+ /** PROFILES COMPUTATION **/
+ /** **/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Set_High_Precision */
+ /* */
+ /* <Description> */
+ /* Sets precision variables according to param flag. */
+ /* */
+ /* <Input> */
+ /* High :: Set to True for high precision (typically for ppem < 18), */
+ /* false otherwise. */
+ /* */
+ static void
+ Set_High_Precision( RAS_ARGS Int 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;
+ }
+
+ FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" ));
+
+ 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;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* New_Profile */
+ /* */
+ /* <Description> */
+ /* Creates a new profile in the render pool. */
+ /* */
+ /* <Input> */
+ /* aState :: The state/orientation of the new profile. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success. FAILURE in case of overflow or of incoherent */
+ /* profile. */
+ /* */
+ static Bool
+ New_Profile( RAS_ARGS TStates aState )
+ {
+ if ( !ras.fProfile )
+ {
+ ras.cProfile = (PProfile)ras.top;
+ ras.fProfile = ras.cProfile;
+ ras.top += AlignProfileSize;
+ }
+
+ if ( ras.top >= ras.maxBuff )
+ {
+ ras.error = Raster_Err_Overflow;
+ return FAILURE;
+ }
+
+ switch ( aState )
+ {
+ case Ascending_State:
+ ras.cProfile->flow = Flow_Up;
+ FT_TRACE6(( "New ascending profile = %lx\n", (long)ras.cProfile ));
+ break;
+
+ case Descending_State:
+ ras.cProfile->flow = Flow_Down;
+ FT_TRACE6(( "New descending profile = %lx\n", (long)ras.cProfile ));
+ break;
+
+ default:
+ FT_ERROR(( "New_Profile: invalid profile direction!\n" ));
+ ras.error = Raster_Err_Invalid;
+ return FAILURE;
+ }
+
+ ras.cProfile->start = 0;
+ ras.cProfile->height = 0;
+ ras.cProfile->offset = ras.top;
+ ras.cProfile->link = (PProfile)0;
+ ras.cProfile->next = (PProfile)0;
+
+ if ( !ras.gProfile )
+ ras.gProfile = ras.cProfile;
+
+ ras.state = aState;
+ ras.fresh = TRUE;
+ ras.joint = FALSE;
+
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* End_Profile */
+ /* */
+ /* <Description> */
+ /* Finalizes the current profile. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success. FAILURE in case of overflow or incoherency. */
+ /* */
+ static Bool
+ End_Profile( RAS_ARG )
+ {
+ Long h;
+ PProfile oldProfile;
+
+
+ h = (Long)( ras.top - ras.cProfile->offset );
+
+ if ( h < 0 )
+ {
+ FT_ERROR(( "End_Profile: negative height encountered!\n" ));
+ ras.error = Raster_Err_Neg_Height;
+ return FAILURE;
+ }
+
+ if ( h > 0 )
+ {
+ FT_TRACE6(( "Ending profile %lx, start = %ld, height = %ld\n",
+ (long)ras.cProfile, ras.cProfile->start, h ));
+
+ oldProfile = ras.cProfile;
+ ras.cProfile->height = h;
+ ras.cProfile = (PProfile)ras.top;
+
+ ras.top += AlignProfileSize;
+
+ ras.cProfile->height = 0;
+ ras.cProfile->offset = ras.top;
+ oldProfile->next = ras.cProfile;
+ ras.num_Profs++;
+ }
+
+ if ( ras.top >= ras.maxBuff )
+ {
+ FT_TRACE1(( "overflow in End_Profile\n" ));
+ ras.error = Raster_Err_Overflow;
+ return FAILURE;
+ }
+
+ ras.joint = FALSE;
+
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Insert_Y_Turn */
+ /* */
+ /* <Description> */
+ /* Inserts a salient into the sorted list placed on top of the render */
+ /* pool. */
+ /* */
+ /* <Input> */
+ /* New y scanline position. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success. FAILURE in case of overflow. */
+ /* */
+ static Bool
+ Insert_Y_Turn( RAS_ARGS Int y )
+ {
+ PLong y_turns;
+ Int y2, n;
+
+
+ n = ras.numTurns - 1;
+ y_turns = ras.sizeBuff - ras.numTurns;
+
+ /* look for first y value that is <= */
+ while ( n >= 0 && y < y_turns[n] )
+ n--;
+
+ /* if it is <, simply insert it, ignore if == */
+ if ( n >= 0 && y > y_turns[n] )
+ while ( n >= 0 )
+ {
+ y2 = (Int)y_turns[n];
+ y_turns[n] = y;
+ y = y2;
+ n--;
+ }
+
+ if ( n < 0 )
+ {
+ ras.maxBuff--;
+ if ( ras.maxBuff <= ras.top )
+ {
+ ras.error = Raster_Err_Overflow;
+ return FAILURE;
+ }
+ ras.numTurns++;
+ ras.sizeBuff[-ras.numTurns] = y;
+ }
+
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Finalize_Profile_Table */
+ /* */
+ /* <Description> */
+ /* Adjusts all links in the profiles list. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success. FAILURE in case of overflow. */
+ /* */
+ static Bool
+ Finalize_Profile_Table( RAS_ARG )
+ {
+ Int bottom, top;
+ UShort n;
+ PProfile p;
+
+
+ n = ras.num_Profs;
+
+ if ( n > 1 )
+ {
+ p = ras.fProfile;
+ while ( n > 0 )
+ {
+ if ( n > 1 )
+ p->link = (PProfile)( p->offset + p->height );
+ else
+ p->link = NULL;
+
+ switch ( p->flow )
+ {
+ case Flow_Down:
+ bottom = (Int)( p->start - p->height + 1 );
+ top = (Int)p->start;
+ p->start = bottom;
+ p->offset += p->height - 1;
+ break;
+
+ case Flow_Up:
+ default:
+ bottom = (Int)p->start;
+ top = (Int)( p->start + p->height - 1 );
+ }
+
+ if ( Insert_Y_Turn( RAS_VARS bottom ) ||
+ Insert_Y_Turn( RAS_VARS top + 1 ) )
+ return FAILURE;
+
+ p = p->link;
+ n--;
+ }
+ }
+ else
+ ras.fProfile = NULL;
+
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Split_Conic */
+ /* */
+ /* <Description> */
+ /* Subdivides one conic Bezier into two joint sub-arcs in the Bezier */
+ /* stack. */
+ /* */
+ /* <Input> */
+ /* None (subdivided Bezier is taken from the top of the stack). */
+ /* */
+ /* <Note> */
+ /* This routine is the `beef' of this component. It is _the_ inner */
+ /* loop that should be optimized to hell to get the best performance. */
+ /* */
+ static void
+ Split_Conic( TPoint* base )
+ {
+ Long 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;
+
+ /* hand optimized. gcc doesn't seem to be too good at common */
+ /* expression substitution and instruction scheduling ;-) */
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <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 )
+ {
+ Long 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> */
+ /* Line_Up */
+ /* */
+ /* <Description> */
+ /* Computes the x-coordinates of an ascending line segment and stores */
+ /* them in the render pool. */
+ /* */
+ /* <Input> */
+ /* x1 :: The x-coordinate of the segment's start point. */
+ /* */
+ /* y1 :: The y-coordinate of the segment's start point. */
+ /* */
+ /* x2 :: The x-coordinate of the segment's end point. */
+ /* */
+ /* y2 :: The y-coordinate of the segment's end point. */
+ /* */
+ /* miny :: A lower vertical clipping bound value. */
+ /* */
+ /* maxy :: An upper vertical clipping bound value. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on render pool overflow. */
+ /* */
+ static Bool
+ Line_Up( RAS_ARGS Long x1,
+ Long y1,
+ Long x2,
+ Long y2,
+ Long miny,
+ Long maxy )
+ {
+ Long Dx, Dy;
+ Int e1, e2, f1, f2, size; /* XXX: is `Short' sufficient? */
+ Long Ix, Rx, Ax;
+
+ PLong top;
+
+
+ Dx = x2 - x1;
+ Dy = y2 - y1;
+
+ if ( Dy <= 0 || y2 < miny || y1 > maxy )
+ return SUCCESS;
+
+ if ( y1 < miny )
+ {
+ /* Take care: miny-y1 can be a very large value; we use */
+ /* a slow MulDiv function to avoid clipping bugs */
+ x1 += SMulDiv( Dx, miny - y1, Dy );
+ e1 = (Int)TRUNC( miny );
+ f1 = 0;
+ }
+ else
+ {
+ e1 = (Int)TRUNC( y1 );
+ f1 = (Int)FRAC( y1 );
+ }
+
+ if ( y2 > maxy )
+ {
+ /* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */
+ e2 = (Int)TRUNC( maxy );
+ f2 = 0;
+ }
+ else
+ {
+ e2 = (Int)TRUNC( y2 );
+ f2 = (Int)FRAC( y2 );
+ }
+
+ if ( f1 > 0 )
+ {
+ if ( e1 == e2 )
+ return SUCCESS;
+ else
+ {
+ x1 += FMulDiv( Dx, ras.precision - f1, Dy );
+ e1 += 1;
+ }
+ }
+ else
+ if ( ras.joint )
+ {
+ ras.top--;
+ ras.joint = FALSE;
+ }
+
+ ras.joint = (char)( f2 == 0 );
+
+ if ( ras.fresh )
+ {
+ ras.cProfile->start = e1;
+ ras.fresh = FALSE;
+ }
+
+ size = e2 - e1 + 1;
+ if ( ras.top + size >= ras.maxBuff )
+ {
+ ras.error = Raster_Err_Overflow;
+ return FAILURE;
+ }
+
+ if ( Dx > 0 )
+ {
+ Ix = ( ras.precision * Dx ) / Dy;
+ Rx = ( ras.precision * Dx ) % Dy;
+ Dx = 1;
+ }
+ else
+ {
+ Ix = -( ( ras.precision * -Dx ) / Dy );
+ Rx = ( ras.precision * -Dx ) % Dy;
+ Dx = -1;
+ }
+
+ Ax = -Dy;
+ top = ras.top;
+
+ while ( size > 0 )
+ {
+ *top++ = x1;
+
+ x1 += Ix;
+ Ax += Rx;
+ if ( Ax >= 0 )
+ {
+ Ax -= Dy;
+ x1 += Dx;
+ }
+ size--;
+ }
+
+ ras.top = top;
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Line_Down */
+ /* */
+ /* <Description> */
+ /* Computes the x-coordinates of an descending line segment and */
+ /* stores them in the render pool. */
+ /* */
+ /* <Input> */
+ /* x1 :: The x-coordinate of the segment's start point. */
+ /* */
+ /* y1 :: The y-coordinate of the segment's start point. */
+ /* */
+ /* x2 :: The x-coordinate of the segment's end point. */
+ /* */
+ /* y2 :: The y-coordinate of the segment's end point. */
+ /* */
+ /* miny :: A lower vertical clipping bound value. */
+ /* */
+ /* maxy :: An upper vertical clipping bound value. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on render pool overflow. */
+ /* */
+ static Bool
+ Line_Down( RAS_ARGS Long x1,
+ Long y1,
+ Long x2,
+ Long y2,
+ Long miny,
+ Long maxy )
+ {
+ Bool result, fresh;
+
+
+ fresh = ras.fresh;
+
+ result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );
+
+ if ( fresh && !ras.fresh )
+ ras.cProfile->start = -ras.cProfile->start;
+
+ return result;
+ }
+
+
+ /* A function type describing the functions used to split Bezier arcs */
+ typedef void (*TSplitter)( TPoint* base );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Bezier_Up */
+ /* */
+ /* <Description> */
+ /* Computes the x-coordinates of an ascending Bezier arc and stores */
+ /* them in the render pool. */
+ /* */
+ /* <Input> */
+ /* degree :: The degree of the Bezier arc (either 2 or 3). */
+ /* */
+ /* splitter :: The function to split Bezier arcs. */
+ /* */
+ /* miny :: A lower vertical clipping bound value. */
+ /* */
+ /* maxy :: An upper vertical clipping bound value. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on render pool overflow. */
+ /* */
+ static Bool
+ Bezier_Up( RAS_ARGS Int degree,
+ TSplitter splitter,
+ Long miny,
+ Long maxy )
+ {
+ Long y1, y2, e, e2, e0;
+ Short f1;
+
+ TPoint* arc;
+ TPoint* start_arc;
+
+ PLong top;
+
+
+ arc = ras.arc;
+ y1 = arc[degree].y;
+ y2 = arc[0].y;
+ top = ras.top;
+
+ if ( y2 < miny || y1 > maxy )
+ goto Fin;
+
+ e2 = FLOOR( y2 );
+
+ if ( e2 > maxy )
+ e2 = maxy;
+
+ e0 = miny;
+
+ if ( y1 < miny )
+ e = miny;
+ else
+ {
+ e = CEILING( y1 );
+ f1 = (Short)( FRAC( y1 ) );
+ e0 = e;
+
+ if ( f1 == 0 )
+ {
+ if ( ras.joint )
+ {
+ top--;
+ ras.joint = FALSE;
+ }
+
+ *top++ = arc[degree].x;
+
+ e += ras.precision;
+ }
+ }
+
+ if ( ras.fresh )
+ {
+ ras.cProfile->start = TRUNC( e0 );
+ ras.fresh = FALSE;
+ }
+
+ if ( e2 < e )
+ goto Fin;
+
+ if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff )
+ {
+ ras.top = top;
+ ras.error = Raster_Err_Overflow;
+ return FAILURE;
+ }
+
+ start_arc = arc;
+
+ while ( arc >= start_arc && e <= e2 )
+ {
+ ras.joint = FALSE;
+
+ y2 = arc[0].y;
+
+ if ( y2 > e )
+ {
+ y1 = arc[degree].y;
+ if ( y2 - y1 >= ras.precision_step )
+ {
+ splitter( arc );
+ arc += degree;
+ }
+ else
+ {
+ *top++ = arc[degree].x + FMulDiv( arc[0].x-arc[degree].x,
+ e - y1, y2 - y1 );
+ arc -= degree;
+ e += ras.precision;
+ }
+ }
+ else
+ {
+ if ( y2 == e )
+ {
+ ras.joint = TRUE;
+ *top++ = arc[0].x;
+
+ e += ras.precision;
+ }
+ arc -= degree;
+ }
+ }
+
+ Fin:
+ ras.top = top;
+ ras.arc -= degree;
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Bezier_Down */
+ /* */
+ /* <Description> */
+ /* Computes the x-coordinates of an descending Bezier arc and stores */
+ /* them in the render pool. */
+ /* */
+ /* <Input> */
+ /* degree :: The degree of the Bezier arc (either 2 or 3). */
+ /* */
+ /* splitter :: The function to split Bezier arcs. */
+ /* */
+ /* miny :: A lower vertical clipping bound value. */
+ /* */
+ /* maxy :: An upper vertical clipping bound value. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on render pool overflow. */
+ /* */
+ static Bool
+ Bezier_Down( RAS_ARGS Int degree,
+ TSplitter splitter,
+ Long miny,
+ Long maxy )
+ {
+ TPoint* arc = ras.arc;
+ Bool 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_VARS degree, splitter, -maxy, -miny );
+
+ if ( fresh && !ras.fresh )
+ ras.cProfile->start = -ras.cProfile->start;
+
+ arc[0].y = -arc[0].y;
+ return result;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Line_To */
+ /* */
+ /* <Description> */
+ /* Injects a new line segment and adjusts Profiles list. */
+ /* */
+ /* <Input> */
+ /* x :: The x-coordinate of the segment's end point (its start point */
+ /* is stored in `lastX'). */
+ /* */
+ /* y :: The y-coordinate of the segment's end point (its start point */
+ /* is stored in `lastY'). */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
+ /* profile. */
+ /* */
+ static Bool
+ Line_To( RAS_ARGS Long x,
+ Long y )
+ {
+ /* First, detect a change of direction */
+
+ switch ( ras.state )
+ {
+ case Unknown_State:
+ if ( y > ras.lastY )
+ {
+ if ( New_Profile( RAS_VARS Ascending_State ) )
+ return FAILURE;
+ }
+ else
+ {
+ if ( y < ras.lastY )
+ if ( New_Profile( RAS_VARS Descending_State ) )
+ return FAILURE;
+ }
+ break;
+
+ case Ascending_State:
+ if ( y < ras.lastY )
+ {
+ if ( End_Profile( RAS_VAR ) ||
+ New_Profile( RAS_VARS Descending_State ) )
+ return FAILURE;
+ }
+ break;
+
+ case Descending_State:
+ if ( y > ras.lastY )
+ {
+ if ( End_Profile( RAS_VAR ) ||
+ New_Profile( RAS_VARS Ascending_State ) )
+ return FAILURE;
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ /* Then compute the lines */
+
+ switch ( ras.state )
+ {
+ case Ascending_State:
+ if ( Line_Up( RAS_VARS ras.lastX, ras.lastY,
+ x, y, ras.minY, ras.maxY ) )
+ return FAILURE;
+ break;
+
+ case Descending_State:
+ if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
+ x, y, ras.minY, ras.maxY ) )
+ return FAILURE;
+ break;
+
+ default:
+ ;
+ }
+
+ ras.lastX = x;
+ ras.lastY = y;
+
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Conic_To */
+ /* */
+ /* <Description> */
+ /* Injects a new conic arc and adjusts the profile list. */
+ /* */
+ /* <Input> */
+ /* cx :: The x-coordinate of the arc's new control point. */
+ /* */
+ /* cy :: The y-coordinate of the arc's new control point. */
+ /* */
+ /* x :: The x-coordinate of the arc's end point (its start point is */
+ /* stored in `lastX'). */
+ /* */
+ /* y :: The y-coordinate of the arc's end point (its start point is */
+ /* stored in `lastY'). */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
+ /* profile. */
+ /* */
+ static Bool
+ Conic_To( RAS_ARGS Long cx,
+ Long cy,
+ Long x,
+ Long y )
+ {
+ Long y1, y2, y3, x3, ymin, ymax;
+ TStates state_bez;
+
+
+ ras.arc = ras.arcs;
+ ras.arc[2].x = ras.lastX;
+ ras.arc[2].y = ras.lastY;
+ ras.arc[1].x = cx; ras.arc[1].y = cy;
+ ras.arc[0].x = x; ras.arc[0].y = y;
+
+ 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_State : Descending_State;
+ if ( ras.state != state_bez )
+ {
+ /* finalize current profile if any */
+ if ( ras.state != Unknown_State &&
+ End_Profile( RAS_VAR ) )
+ goto Fail;
+
+ /* create a new profile */
+ if ( New_Profile( RAS_VARS state_bez ) )
+ goto Fail;
+ }
+
+ /* now call the appropriate routine */
+ if ( state_bez == Ascending_State )
+ {
+ if ( Bezier_Up( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
+ goto Fail;
+ }
+ else
+ if ( Bezier_Down( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
+ goto Fail;
+ }
+
+ } while ( ras.arc >= ras.arcs );
+
+ ras.lastX = x3;
+ ras.lastY = y3;
+
+ return SUCCESS;
+
+ Fail:
+ return FAILURE;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Cubic_To */
+ /* */
+ /* <Description> */
+ /* Injects a new cubic arc and adjusts the profile list. */
+ /* */
+ /* <Input> */
+ /* cx1 :: The x-coordinate of the arc's first new control point. */
+ /* */
+ /* cy1 :: The y-coordinate of the arc's first new control point. */
+ /* */
+ /* cx2 :: The x-coordinate of the arc's second new control point. */
+ /* */
+ /* cy2 :: The y-coordinate of the arc's second new control point. */
+ /* */
+ /* x :: The x-coordinate of the arc's end point (its start point is */
+ /* stored in `lastX'). */
+ /* */
+ /* y :: The y-coordinate of the arc's end point (its start point is */
+ /* stored in `lastY'). */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
+ /* profile. */
+ /* */
+ static Bool
+ Cubic_To( RAS_ARGS Long cx1,
+ Long cy1,
+ Long cx2,
+ Long cy2,
+ Long x,
+ Long y )
+ {
+ Long y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
+ TStates state_bez;
+
+
+ ras.arc = ras.arcs;
+ ras.arc[3].x = ras.lastX;
+ ras.arc[3].y = ras.lastY;
+ ras.arc[2].x = cx1; ras.arc[2].y = cy1;
+ ras.arc[1].x = cx2; ras.arc[1].y = cy2;
+ ras.arc[0].x = x; ras.arc[0].y = y;
+
+ 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_State : Descending_State;
+
+ /* detect a change of direction */
+ if ( ras.state != state_bez )
+ {
+ if ( ras.state != Unknown_State &&
+ End_Profile( RAS_VAR ) )
+ goto Fail;
+
+ if ( New_Profile( RAS_VARS state_bez ) )
+ goto Fail;
+ }
+
+ /* compute intersections */
+ if ( state_bez == Ascending_State )
+ {
+ if ( Bezier_Up( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
+ goto Fail;
+ }
+ else
+ if ( Bezier_Down( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
+ goto Fail;
+ }
+
+ } while ( ras.arc >= ras.arcs );
+
+ ras.lastX = x4;
+ ras.lastY = y4;
+
+ return SUCCESS;
+
+ Fail:
+ return FAILURE;
+ }
+
+
+#undef SWAP_
+#define SWAP_( x, y ) do \
+ { \
+ Long swap = x; \
+ \
+ \
+ x = y; \
+ y = swap; \
+ } while ( 0 )
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Decompose_Curve */
+ /* */
+ /* <Description> */
+ /* Scans the outline arrays in order to emit individual segments and */
+ /* Beziers by calling Line_To() and Bezier_To(). It handles all */
+ /* weird cases, like when the first point is off the curve, or when */
+ /* there are simply no `on' points in the contour! */
+ /* */
+ /* <Input> */
+ /* first :: The index of the first point in the contour. */
+ /* */
+ /* last :: The index of the last point in the contour. */
+ /* */
+ /* flipped :: If set, flip the direction of the curve. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on error. */
+ /* */
+ static Bool
+ Decompose_Curve( RAS_ARGS UShort first,
+ UShort last,
+ int flipped )
+ {
+ FT_Vector v_last;
+ FT_Vector v_control;
+ FT_Vector v_start;
+
+ FT_Vector* points;
+ FT_Vector* point;
+ FT_Vector* limit;
+ char* tags;
+
+ unsigned tag; /* current point's state */
+
+
+ points = ras.outline.points;
+ limit = points + last;
+
+ v_start.x = SCALED( points[first].x );
+ v_start.y = SCALED( points[first].y );
+ v_last.x = SCALED( points[last].x );
+ v_last.y = SCALED( points[last].y );
+
+ if ( flipped )
+ {
+ SWAP_( v_start.x, v_start.y );
+ SWAP_( v_last.x, v_last.y );
+ }
+
+ v_control = v_start;
+
+ point = points + first;
+ tags = ras.outline.tags + first;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ /* A contour cannot start with a cubic control point! */
+ if ( tag == FT_CURVE_TAG_CUBIC )
+ goto 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( ras.outline.tags[last] ) == FT_CURVE_TAG_ON )
+ {
+ /* start at last point if it is on the curve */
+ v_start = v_last;
+ limit--;
+ }
+ 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;
+ }
+ point--;
+ tags--;
+ }
+
+ ras.lastX = v_start.x;
+ ras.lastY = v_start.y;
+
+ while ( point < limit )
+ {
+ point++;
+ tags++;
+
+ tag = FT_CURVE_TAG( tags[0] );
+
+ switch ( tag )
+ {
+ case FT_CURVE_TAG_ON: /* emit a single line_to */
+ {
+ Long x, y;
+
+
+ x = SCALED( point->x );
+ y = SCALED( point->y );
+ if ( flipped )
+ SWAP_( x, y );
+
+ if ( Line_To( RAS_VARS x, y ) )
+ goto Fail;
+ continue;
+ }
+
+ case FT_CURVE_TAG_CONIC: /* consume conic arcs */
+ v_control.x = SCALED( point[0].x );
+ v_control.y = SCALED( point[0].y );
+
+ if ( flipped )
+ SWAP_( v_control.x, v_control.y );
+
+ Do_Conic:
+ if ( point < limit )
+ {
+ FT_Vector v_middle;
+ Long x, y;
+
+
+ point++;
+ tags++;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ x = SCALED( point[0].x );
+ y = SCALED( point[0].y );
+
+ if ( flipped )
+ SWAP_( x, y );
+
+ if ( tag == FT_CURVE_TAG_ON )
+ {
+ if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) )
+ goto Fail;
+ continue;
+ }
+
+ if ( tag != FT_CURVE_TAG_CONIC )
+ goto Invalid_Outline;
+
+ v_middle.x = ( v_control.x + x ) / 2;
+ v_middle.y = ( v_control.y + y ) / 2;
+
+ if ( Conic_To( RAS_VARS v_control.x, v_control.y,
+ v_middle.x, v_middle.y ) )
+ goto Fail;
+
+ v_control.x = x;
+ v_control.y = y;
+
+ goto Do_Conic;
+ }
+
+ if ( Conic_To( RAS_VARS v_control.x, v_control.y,
+ v_start.x, v_start.y ) )
+ goto Fail;
+
+ goto Close;
+
+ default: /* FT_CURVE_TAG_CUBIC */
+ {
+ Long x1, y1, x2, y2, x3, y3;
+
+
+ if ( point + 1 > limit ||
+ FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ point += 2;
+ tags += 2;
+
+ x1 = SCALED( point[-2].x );
+ y1 = SCALED( point[-2].y );
+ x2 = SCALED( point[-1].x );
+ y2 = SCALED( point[-1].y );
+ x3 = SCALED( point[ 0].x );
+ y3 = SCALED( point[ 0].y );
+
+ if ( flipped )
+ {
+ SWAP_( x1, y1 );
+ SWAP_( x2, y2 );
+ SWAP_( x3, y3 );
+ }
+
+ if ( point <= limit )
+ {
+ if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) )
+ goto Fail;
+ continue;
+ }
+
+ if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) )
+ goto Fail;
+ goto Close;
+ }
+ }
+ }
+
+ /* close the contour with a line segment */
+ if ( Line_To( RAS_VARS v_start.x, v_start.y ) )
+ goto Fail;
+
+ Close:
+ return SUCCESS;
+
+ Invalid_Outline:
+ ras.error = Raster_Err_Invalid;
+
+ Fail:
+ return FAILURE;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Convert_Glyph */
+ /* */
+ /* <Description> */
+ /* Converts a glyph into a series of segments and arcs and makes a */
+ /* profiles list with them. */
+ /* */
+ /* <Input> */
+ /* flipped :: If set, flip the direction of curve. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE if any error was encountered during */
+ /* rendering. */
+ /* */
+ static Bool
+ Convert_Glyph( RAS_ARGS int flipped )
+ {
+ int i;
+ unsigned start;
+
+ PProfile lastProfile;
+
+
+ ras.fProfile = NULL;
+ ras.joint = FALSE;
+ ras.fresh = FALSE;
+
+ ras.maxBuff = ras.sizeBuff - AlignProfileSize;
+
+ ras.numTurns = 0;
+
+ ras.cProfile = (PProfile)ras.top;
+ ras.cProfile->offset = ras.top;
+ ras.num_Profs = 0;
+
+ start = 0;
+
+ for ( i = 0; i < ras.outline.n_contours; i++ )
+ {
+ ras.state = Unknown_State;
+ ras.gProfile = NULL;
+
+ if ( Decompose_Curve( RAS_VARS (unsigned short)start,
+ ras.outline.contours[i],
+ flipped ) )
+ return FAILURE;
+
+ start = ras.outline.contours[i] + 1;
+
+ /* We must now see whether the extreme arcs join or not */
+ if ( FRAC( ras.lastY ) == 0 &&
+ ras.lastY >= ras.minY &&
+ ras.lastY <= ras.maxY )
+ if ( ras.gProfile && ras.gProfile->flow == ras.cProfile->flow )
+ ras.top--;
+ /* Note that ras.gProfile can be nil if the contour was too small */
+ /* to be drawn. */
+
+ lastProfile = ras.cProfile;
+ if ( End_Profile( RAS_VAR ) )
+ return FAILURE;
+
+ /* close the `next profile in contour' linked list */
+ if ( ras.gProfile )
+ lastProfile->next = ras.gProfile;
+ }
+
+ if ( Finalize_Profile_Table( RAS_VAR ) )
+ return FAILURE;
+
+ return (Bool)( ras.top < ras.maxBuff ? SUCCESS : FAILURE );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /** **/
+ /** SCAN-LINE SWEEPS AND DRAWING **/
+ /** **/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* Init_Linked */
+ /* */
+ /* Initializes 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;
+ Long 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 get there, unless the profile was not part of */
+ /* the list. */
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* 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. */
+ /* */
+ static void
+ Sort( PProfileList list )
+ {
+ PProfile *old, current, next;
+
+
+ /* First, set the new X coordinate of each profile */
+ current = *list;
+ while ( current )
+ {
+ current->X = *current->offset;
+ current->offset += current->flow;
+ current->height--;
+ current = current->link;
+ }
+
+ /* 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 Sweep Procedure Set */
+ /* */
+ /* These four routines are used during the vertical black/white sweep */
+ /* phase by the generic Draw_Sweep() function. */
+ /* */
+ /*************************************************************************/
+
+ static void
+ Vertical_Sweep_Init( RAS_ARGS Short* min,
+ Short* max )
+ {
+ Long pitch = ras.target.pitch;
+
+ FT_UNUSED( max );
+
+
+ ras.traceIncr = (Short)-pitch;
+ ras.traceOfs = -*min * pitch;
+ if ( pitch > 0 )
+ ras.traceOfs += ( ras.target.rows - 1 ) * pitch;
+
+ ras.gray_min_x = 0;
+ ras.gray_max_x = 0;
+ }
+
+
+ static void
+ Vertical_Sweep_Span( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ Long e1, e2;
+ int c1, c2;
+ Byte f1, f2;
+ Byte* target;
+
+ FT_UNUSED( y );
+ FT_UNUSED( left );
+ FT_UNUSED( right );
+
+
+ /* Drop-out control */
+
+ e1 = TRUNC( CEILING( x1 ) );
+
+ if ( x2 - x1 - ras.precision <= ras.precision_jitter )
+ e2 = e1;
+ else
+ e2 = TRUNC( FLOOR( x2 ) );
+
+ if ( e2 >= 0 && e1 < ras.bWidth )
+ {
+ if ( e1 < 0 )
+ e1 = 0;
+ if ( e2 >= ras.bWidth )
+ e2 = ras.bWidth - 1;
+
+ c1 = (Short)( e1 >> 3 );
+ c2 = (Short)( e2 >> 3 );
+
+ f1 = (Byte) ( 0xFF >> ( e1 & 7 ) );
+ f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) );
+
+ if ( ras.gray_min_x > c1 ) ras.gray_min_x = (short)c1;
+ if ( ras.gray_max_x < c2 ) ras.gray_max_x = (short)c2;
+
+ target = ras.bTarget + ras.traceOfs + 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 );
+ }
+ }
+
+
+ static void
+ Vertical_Sweep_Drop( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ Long e1, e2;
+ Short c1, f1;
+
+
+ /* Drop-out control */
+
+ e1 = CEILING( x1 );
+ e2 = FLOOR ( x2 );
+
+ if ( e1 > e2 )
+ {
+ if ( e1 == e2 + ras.precision )
+ {
+ switch ( ras.dropOutControl )
+ {
+ 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 if: */
+ /* */
+ /* 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 */
+ /* */
+
+ /* FIXXXME: uncommenting this line solves the disappearing */
+ /* bit problem in the `7' of verdana 10pts, but */
+ /* makes a new one in the `C' of arial 14pts */
+
+#if 0
+ if ( x2 - x1 < ras.precision_half )
+#endif
+ {
+ /* upper stub test */
+ if ( left->next == right && left->height <= 0 )
+ return;
+
+ /* lower stub test */
+ if ( right->next == left && left->start == y )
+ return;
+ }
+
+ /* check that the rightmost pixel isn't set */
+
+ e1 = TRUNC( e1 );
+
+ c1 = (Short)( e1 >> 3 );
+ f1 = (Short)( e1 & 7 );
+
+ if ( e1 >= 0 && e1 < ras.bWidth &&
+ ras.bTarget[ras.traceOfs + c1] & ( 0x80 >> f1 ) )
+ return;
+
+ if ( ras.dropOutControl == 2 )
+ e1 = e2;
+ else
+ e1 = CEILING( ( x1 + x2 + 1 ) / 2 );
+
+ break;
+
+ default:
+ return; /* unsupported mode */
+ }
+ }
+ else
+ return;
+ }
+
+ e1 = TRUNC( e1 );
+
+ if ( e1 >= 0 && e1 < ras.bWidth )
+ {
+ c1 = (Short)( e1 >> 3 );
+ f1 = (Short)( e1 & 7 );
+
+ if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1;
+ if ( ras.gray_max_x < c1 ) ras.gray_max_x = c1;
+
+ ras.bTarget[ras.traceOfs + c1] |= (char)( 0x80 >> f1 );
+ }
+ }
+
+
+ static void
+ Vertical_Sweep_Step( RAS_ARG )
+ {
+ ras.traceOfs += ras.traceIncr;
+ }
+
+
+ /***********************************************************************/
+ /* */
+ /* Horizontal Sweep Procedure Set */
+ /* */
+ /* These four routines are used during the horizontal black/white */
+ /* sweep phase by the generic Draw_Sweep() function. */
+ /* */
+ /***********************************************************************/
+
+ static void
+ Horizontal_Sweep_Init( RAS_ARGS Short* min,
+ Short* max )
+ {
+ /* nothing, really */
+ FT_UNUSED_RASTER;
+ FT_UNUSED( min );
+ FT_UNUSED( max );
+ }
+
+
+ static void
+ Horizontal_Sweep_Span( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ Long e1, e2;
+ PByte bits;
+ Byte f1;
+
+ FT_UNUSED( left );
+ FT_UNUSED( right );
+
+
+ if ( x2 - x1 < ras.precision )
+ {
+ e1 = CEILING( x1 );
+ e2 = FLOOR ( x2 );
+
+ if ( e1 == e2 )
+ {
+ bits = ras.bTarget + ( y >> 3 );
+ f1 = (Byte)( 0x80 >> ( y & 7 ) );
+
+ e1 = TRUNC( e1 );
+
+ if ( e1 >= 0 && e1 < ras.target.rows )
+ {
+ PByte p;
+
+
+ p = bits - e1*ras.target.pitch;
+ if ( ras.target.pitch > 0 )
+ p += ( ras.target.rows - 1 ) * ras.target.pitch;
+
+ p[0] |= f1;
+ }
+ }
+ }
+ }
+
+
+ static void
+ Horizontal_Sweep_Drop( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ Long e1, e2;
+ PByte bits;
+ Byte f1;
+
+
+ /* During the horizontal sweep, we only take care of drop-outs */
+
+ e1 = CEILING( x1 );
+ e2 = FLOOR ( x2 );
+
+ if ( e1 > e2 )
+ {
+ if ( e1 == e2 + ras.precision )
+ {
+ switch ( ras.dropOutControl )
+ {
+ 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'. */
+ /* */
+
+ /* rightmost stub test */
+ if ( left->next == right && left->height <= 0 )
+ return;
+
+ /* leftmost stub test */
+ if ( right->next == left && left->start == y )
+ return;
+
+ /* check that the rightmost pixel isn't set */
+
+ e1 = TRUNC( e1 );
+
+ bits = ras.bTarget + ( y >> 3 );
+ f1 = (Byte)( 0x80 >> ( y & 7 ) );
+
+ bits -= e1 * ras.target.pitch;
+ if ( ras.target.pitch > 0 )
+ bits += ( ras.target.rows - 1 ) * ras.target.pitch;
+
+ if ( e1 >= 0 &&
+ e1 < ras.target.rows &&
+ *bits & f1 )
+ return;
+
+ if ( ras.dropOutControl == 2 )
+ e1 = e2;
+ else
+ e1 = CEILING( ( x1 + x2 + 1 ) / 2 );
+
+ break;
+
+ default:
+ return; /* unsupported mode */
+ }
+ }
+ else
+ return;
+ }
+
+ bits = ras.bTarget + ( y >> 3 );
+ f1 = (Byte)( 0x80 >> ( y & 7 ) );
+
+ e1 = TRUNC( e1 );
+
+ if ( e1 >= 0 && e1 < ras.target.rows )
+ {
+ bits -= e1 * ras.target.pitch;
+ if ( ras.target.pitch > 0 )
+ bits += ( ras.target.rows - 1 ) * ras.target.pitch;
+
+ bits[0] |= f1;
+ }
+ }
+
+
+ static void
+ Horizontal_Sweep_Step( RAS_ARG )
+ {
+ /* Nothing, really */
+ FT_UNUSED_RASTER;
+ }
+
+
+#ifdef FT_RASTER_OPTION_ANTI_ALIASING
+
+
+ /*************************************************************************/
+ /* */
+ /* Vertical Gray Sweep Procedure Set */
+ /* */
+ /* These two routines are used during the vertical gray-levels sweep */
+ /* phase by the generic Draw_Sweep() function. */
+ /* */
+ /* NOTES */
+ /* */
+ /* - The target pixmap's width *must* be a multiple of 4. */
+ /* */
+ /* - You have to use the function Vertical_Sweep_Span() for the gray */
+ /* span call. */
+ /* */
+ /*************************************************************************/
+
+ static void
+ Vertical_Gray_Sweep_Init( RAS_ARGS Short* min,
+ Short* max )
+ {
+ Long pitch, byte_len;
+
+
+ *min = *min & -2;
+ *max = ( *max + 3 ) & -2;
+
+ ras.traceOfs = 0;
+ pitch = ras.target.pitch;
+ byte_len = -pitch;
+ ras.traceIncr = (Short)byte_len;
+ ras.traceG = ( *min / 2 ) * byte_len;
+
+ if ( pitch > 0 )
+ {
+ ras.traceG += ( ras.target.rows - 1 ) * pitch;
+ byte_len = -byte_len;
+ }
+
+ ras.gray_min_x = (Short)byte_len;
+ ras.gray_max_x = -(Short)byte_len;
+ }
+
+
+ static void
+ Vertical_Gray_Sweep_Step( RAS_ARG )
+ {
+ Int c1, c2;
+ PByte pix, bit, bit2;
+ char* count = (char*)count_table;
+ Byte* grays;
+
+
+ ras.traceOfs += ras.gray_width;
+
+ if ( ras.traceOfs > ras.gray_width )
+ {
+ pix = ras.gTarget + ras.traceG + ras.gray_min_x * 4;
+ grays = ras.grays;
+
+ if ( ras.gray_max_x >= 0 )
+ {
+ Long last_pixel = ras.target.width - 1;
+ Int last_cell = last_pixel >> 2;
+ Int last_bit = last_pixel & 3;
+ Bool over = 0;
+
+
+ if ( ras.gray_max_x >= last_cell && last_bit != 3 )
+ {
+ ras.gray_max_x = last_cell - 1;
+ over = 1;
+ }
+
+ if ( ras.gray_min_x < 0 )
+ ras.gray_min_x = 0;
+
+ bit = ras.bTarget + ras.gray_min_x;
+ bit2 = bit + ras.gray_width;
+
+ c1 = ras.gray_max_x - ras.gray_min_x;
+
+ while ( c1 >= 0 )
+ {
+ c2 = count[*bit] + count[*bit2];
+
+ if ( c2 )
+ {
+ pix[0] = grays[(c2 >> 12) & 0x000F];
+ pix[1] = grays[(c2 >> 8 ) & 0x000F];
+ pix[2] = grays[(c2 >> 4 ) & 0x000F];
+ pix[3] = grays[ c2 & 0x000F];
+
+ *bit = 0;
+ *bit2 = 0;
+ }
+
+ bit++;
+ bit2++;
+ pix += 4;
+ c1--;
+ }
+
+ if ( over )
+ {
+ c2 = count[*bit] + count[*bit2];
+ if ( c2 )
+ {
+ switch ( last_bit )
+ {
+ case 2:
+ pix[2] = grays[(c2 >> 4 ) & 0x000F];
+ case 1:
+ pix[1] = grays[(c2 >> 8 ) & 0x000F];
+ default:
+ pix[0] = grays[(c2 >> 12) & 0x000F];
+ }
+
+ *bit = 0;
+ *bit2 = 0;
+ }
+ }
+ }
+
+ ras.traceOfs = 0;
+ ras.traceG += ras.traceIncr;
+
+ ras.gray_min_x = 32000;
+ ras.gray_max_x = -32000;
+ }
+ }
+
+
+ static void
+ Horizontal_Gray_Sweep_Span( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ /* nothing, really */
+ FT_UNUSED_RASTER;
+ FT_UNUSED( y );
+ FT_UNUSED( x1 );
+ FT_UNUSED( x2 );
+ FT_UNUSED( left );
+ FT_UNUSED( right );
+ }
+
+
+ static void
+ Horizontal_Gray_Sweep_Drop( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ Long e1, e2;
+ PByte pixel;
+ Byte color;
+
+
+ /* During the horizontal sweep, we only take care of drop-outs */
+ e1 = CEILING( x1 );
+ e2 = FLOOR ( x2 );
+
+ if ( e1 > e2 )
+ {
+ if ( e1 == e2 + ras.precision )
+ {
+ switch ( ras.dropOutControl )
+ {
+ 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'. */
+ /* */
+
+ /* rightmost stub test */
+ if ( left->next == right && left->height <= 0 )
+ return;
+
+ /* leftmost stub test */
+ if ( right->next == left && left->start == y )
+ return;
+
+ if ( ras.dropOutControl == 2 )
+ e1 = e2;
+ else
+ e1 = CEILING( ( x1 + x2 + 1 ) / 2 );
+
+ break;
+
+ default:
+ return; /* unsupported mode */
+ }
+ }
+ else
+ return;
+ }
+
+ if ( e1 >= 0 )
+ {
+ if ( x2 - x1 >= ras.precision_half )
+ color = ras.grays[2];
+ else
+ color = ras.grays[1];
+
+ e1 = TRUNC( e1 ) / 2;
+ if ( e1 < ras.target.rows )
+ {
+ pixel = ras.gTarget - e1 * ras.target.pitch + y / 2;
+ if ( ras.target.pitch > 0 )
+ pixel += ( ras.target.rows - 1 ) * ras.target.pitch;
+
+ if ( pixel[0] == ras.grays[0] )
+ pixel[0] = color;
+ }
+ }
+ }
+
+
+#endif /* FT_RASTER_OPTION_ANTI_ALIASING */
+
+
+ /*************************************************************************/
+ /* */
+ /* Generic Sweep Drawing routine */
+ /* */
+ /*************************************************************************/
+
+ static Bool
+ Draw_Sweep( RAS_ARG )
+ {
+ Short y, y_change, y_height;
+
+ PProfile P, Q, P_Left, P_Right;
+
+ Short min_Y, max_Y, top, bottom, dropouts;
+
+ Long x1, x2, xs, e1, e2;
+
+ TProfileList waiting;
+ TProfileList draw_left, draw_right;
+
+
+ /* Init empty linked lists */
+
+ Init_Linked( &waiting );
+
+ Init_Linked( &draw_left );
+ Init_Linked( &draw_right );
+
+ /* first, compute min and max Y */
+
+ P = ras.fProfile;
+ max_Y = (Short)TRUNC( ras.minY );
+ min_Y = (Short)TRUNC( ras.maxY );
+
+ while ( P )
+ {
+ Q = P->link;
+
+ bottom = (Short)P->start;
+ top = (Short)( P->start + P->height - 1 );
+
+ if ( min_Y > bottom ) min_Y = bottom;
+ if ( max_Y < top ) max_Y = top;
+
+ P->X = 0;
+ InsNew( &waiting, P );
+
+ P = Q;
+ }
+
+ /* Check the Y-turns */
+ if ( ras.numTurns == 0 )
+ {
+ ras.error = Raster_Err_Invalid;
+ return FAILURE;
+ }
+
+ /* Now inits the sweep */
+
+ ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y );
+
+ /* Then compute the distance of each profile from min_Y */
+
+ P = waiting;
+
+ while ( P )
+ {
+ P->countL = (UShort)( P->start - min_Y );
+ P = P->link;
+ }
+
+ /* Let's go */
+
+ y = min_Y;
+ y_height = 0;
+
+ if ( ras.numTurns > 0 &&
+ ras.sizeBuff[-ras.numTurns] == min_Y )
+ ras.numTurns--;
+
+ while ( ras.numTurns > 0 )
+ {
+ /* look in the waiting list for new activations */
+
+ P = waiting;
+
+ while ( P )
+ {
+ Q = P->link;
+ P->countL -= y_height;
+ if ( P->countL == 0 )
+ {
+ DelOld( &waiting, P );
+
+ switch ( P->flow )
+ {
+ case Flow_Up:
+ InsNew( &draw_left, P );
+ break;
+
+ case Flow_Down:
+ InsNew( &draw_right, P );
+ break;
+ }
+ }
+
+ P = Q;
+ }
+
+ /* Sort the drawing lists */
+
+ Sort( &draw_left );
+ Sort( &draw_right );
+
+ y_change = (Short)ras.sizeBuff[-ras.numTurns--];
+ y_height = (Short)( y_change - y );
+
+ while ( y < y_change )
+ {
+ /* Let's trace */
+
+ dropouts = 0;
+
+ P_Left = draw_left;
+ P_Right = draw_right;
+
+ while ( P_Left )
+ {
+ x1 = P_Left ->X;
+ x2 = P_Right->X;
+
+ if ( x1 > x2 )
+ {
+ xs = x1;
+ x1 = x2;
+ x2 = xs;
+ }
+
+ if ( x2 - x1 <= ras.precision )
+ {
+ e1 = FLOOR( x1 );
+ e2 = CEILING( x2 );
+
+ if ( ras.dropOutControl != 0 &&
+ ( e1 > e2 || e2 == e1 + ras.precision ) )
+ {
+ /* a drop out was detected */
+
+ P_Left ->X = x1;
+ P_Right->X = x2;
+
+ /* mark profile for drop-out processing */
+ P_Left->countL = 1;
+ dropouts++;
+
+ goto Skip_To_Next;
+ }
+ }
+
+ ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right );
+
+ Skip_To_Next:
+
+ P_Left = P_Left->link;
+ P_Right = P_Right->link;
+ }
+
+ /* 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.Proc_Sweep_Step( RAS_VAR );
+
+ y++;
+
+ if ( y < y_change )
+ {
+ Sort( &draw_left );
+ Sort( &draw_right );
+ }
+ }
+
+ /* Now finalize the profiles that needs it */
+
+ P = draw_left;
+ while ( P )
+ {
+ Q = P->link;
+ if ( P->height == 0 )
+ DelOld( &draw_left, P );
+ P = Q;
+ }
+
+ P = draw_right;
+ while ( P )
+ {
+ Q = P->link;
+ if ( P->height == 0 )
+ DelOld( &draw_right, P );
+ P = Q;
+ }
+ }
+
+ /* for gray-scaling, flushes the bitmap scanline cache */
+ while ( y <= max_Y )
+ {
+ ras.Proc_Sweep_Step( RAS_VAR );
+ y++;
+ }
+
+ return SUCCESS;
+
+ Scan_DropOuts:
+
+ P_Left = draw_left;
+ P_Right = draw_right;
+
+ while ( P_Left )
+ {
+ if ( P_Left->countL )
+ {
+ P_Left->countL = 0;
+#if 0
+ dropouts--; /* -- this is useful when debugging only */
+#endif
+ ras.Proc_Sweep_Drop( RAS_VARS y,
+ P_Left->X,
+ P_Right->X,
+ P_Left,
+ P_Right );
+ }
+
+ P_Left = P_Left->link;
+ P_Right = P_Right->link;
+ }
+
+ goto Next_Line;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Render_Single_Pass */
+ /* */
+ /* <Description> */
+ /* Performs one sweep with sub-banding. */
+ /* */
+ /* <Input> */
+ /* flipped :: If set, flip the direction of the outline. */
+ /* */
+ /* <Return> */
+ /* Renderer error code. */
+ /* */
+ static int
+ Render_Single_Pass( RAS_ARGS Bool flipped )
+ {
+ Short i, j, k;
+
+
+ while ( ras.band_top >= 0 )
+ {
+ ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision;
+ ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision;
+
+ ras.top = ras.buff;
+
+ ras.error = Raster_Err_None;
+
+ if ( Convert_Glyph( RAS_VARS flipped ) )
+ {
+ if ( ras.error != Raster_Err_Overflow )
+ return FAILURE;
+
+ ras.error = Raster_Err_None;
+
+ /* sub-banding */
+
+#ifdef DEBUG_RASTER
+ ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) );
+#endif
+
+ i = ras.band_stack[ras.band_top].y_min;
+ j = ras.band_stack[ras.band_top].y_max;
+
+ k = (Short)( ( i + j ) / 2 );
+
+ if ( ras.band_top >= 7 || k < i )
+ {
+ ras.band_top = 0;
+ ras.error = Raster_Err_Invalid;
+
+ return ras.error;
+ }
+
+ ras.band_stack[ras.band_top + 1].y_min = k;
+ ras.band_stack[ras.band_top + 1].y_max = j;
+
+ ras.band_stack[ras.band_top].y_max = (Short)( k - 1 );
+
+ ras.band_top++;
+ }
+ else
+ {
+ if ( ras.fProfile )
+ if ( Draw_Sweep( RAS_VAR ) )
+ return ras.error;
+ ras.band_top--;
+ }
+ }
+
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Render_Glyph */
+ /* */
+ /* <Description> */
+ /* Renders a glyph in a bitmap. Sub-banding if needed. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ Render_Glyph( RAS_ARG )
+ {
+ FT_Error error;
+
+
+ Set_High_Precision( RAS_VARS ras.outline.flags &
+ FT_OUTLINE_HIGH_PRECISION );
+ ras.scale_shift = ras.precision_shift;
+ /* Drop-out mode 2 is hard-coded since this is the only mode used */
+ /* on Windows platforms. Using other modes, as specified by the */
+ /* font, results in misplaced pixels. */
+ ras.dropOutControl = 2;
+ ras.second_pass = (FT_Byte)( !( ras.outline.flags &
+ FT_OUTLINE_SINGLE_PASS ) );
+
+ /* Vertical Sweep */
+ ras.Proc_Sweep_Init = Vertical_Sweep_Init;
+ ras.Proc_Sweep_Span = Vertical_Sweep_Span;
+ ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
+ ras.Proc_Sweep_Step = Vertical_Sweep_Step;
+
+ ras.band_top = 0;
+ ras.band_stack[0].y_min = 0;
+ ras.band_stack[0].y_max = (short)( ras.target.rows - 1 );
+
+ ras.bWidth = (unsigned short)ras.target.width;
+ ras.bTarget = (Byte*)ras.target.buffer;
+
+ if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 )
+ return error;
+
+ /* Horizontal Sweep */
+ if ( ras.second_pass && ras.dropOutControl != 0 )
+ {
+ ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
+ ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
+ ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;
+ ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
+
+ ras.band_top = 0;
+ ras.band_stack[0].y_min = 0;
+ ras.band_stack[0].y_max = (short)( ras.target.width - 1 );
+
+ if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 )
+ return error;
+ }
+
+ return Raster_Err_None;
+ }
+
+
+#ifdef FT_RASTER_OPTION_ANTI_ALIASING
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Render_Gray_Glyph */
+ /* */
+ /* <Description> */
+ /* Renders a glyph with grayscaling. Sub-banding if needed. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ Render_Gray_Glyph( RAS_ARG )
+ {
+ Long pixel_width;
+ FT_Error error;
+
+
+ Set_High_Precision( RAS_VARS ras.outline.flags &
+ FT_OUTLINE_HIGH_PRECISION );
+ ras.scale_shift = ras.precision_shift + 1;
+ /* Drop-out mode 2 is hard-coded since this is the only mode used */
+ /* on Windows platforms. Using other modes, as specified by the */
+ /* font, results in misplaced pixels. */
+ ras.dropOutControl = 2;
+ ras.second_pass = !( ras.outline.flags & FT_OUTLINE_SINGLE_PASS );
+
+ /* Vertical Sweep */
+
+ ras.band_top = 0;
+ ras.band_stack[0].y_min = 0;
+ ras.band_stack[0].y_max = 2 * ras.target.rows - 1;
+
+ ras.bWidth = ras.gray_width;
+ pixel_width = 2 * ( ( ras.target.width + 3 ) >> 2 );
+
+ if ( ras.bWidth > pixel_width )
+ ras.bWidth = pixel_width;
+
+ ras.bWidth = ras.bWidth * 8;
+ ras.bTarget = (Byte*)ras.gray_lines;
+ ras.gTarget = (Byte*)ras.target.buffer;
+
+ ras.Proc_Sweep_Init = Vertical_Gray_Sweep_Init;
+ ras.Proc_Sweep_Span = Vertical_Sweep_Span;
+ ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
+ ras.Proc_Sweep_Step = Vertical_Gray_Sweep_Step;
+
+ error = Render_Single_Pass( RAS_VARS 0 );
+ if ( error )
+ return error;
+
+ /* Horizontal Sweep */
+ if ( ras.second_pass && ras.dropOutControl != 0 )
+ {
+ ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
+ ras.Proc_Sweep_Span = Horizontal_Gray_Sweep_Span;
+ ras.Proc_Sweep_Drop = Horizontal_Gray_Sweep_Drop;
+ ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
+
+ ras.band_top = 0;
+ ras.band_stack[0].y_min = 0;
+ ras.band_stack[0].y_max = ras.target.width * 2 - 1;
+
+ error = Render_Single_Pass( RAS_VARS 1 );
+ if ( error )
+ return error;
+ }
+
+ return Raster_Err_None;
+ }
+
+#else /* !FT_RASTER_OPTION_ANTI_ALIASING */
+
+ FT_LOCAL_DEF( FT_Error )
+ Render_Gray_Glyph( RAS_ARG )
+ {
+ FT_UNUSED_RASTER;
+
+ return Raster_Err_Unsupported;
+ }
+
+#endif /* !FT_RASTER_OPTION_ANTI_ALIASING */
+
+
+ static void
+ ft_black_init( PRaster raster )
+ {
+ FT_UNUSED( raster );
+
+#ifdef FT_RASTER_OPTION_ANTI_ALIASING
+ FT_UInt n;
+
+
+ /* set default 5-levels gray palette */
+ for ( n = 0; n < 5; n++ )
+ raster->grays[n] = n * 255 / 4;
+
+ raster->gray_width = RASTER_GRAY_LINES / 2;
+
+#endif
+ }
+
+
+ /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
+ /**** a static object. *****/
+
+
+#ifdef _STANDALONE_
+
+
+ static int
+ ft_black_new( void* memory,
+ FT_Raster *araster )
+ {
+ static TRaster the_raster;
+
+
+ *araster = (FT_Raster)&the_raster;
+ FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) );
+ ft_black_init( &the_raster );
+
+ return 0;
+ }
+
+
+ static void
+ ft_black_done( FT_Raster raster )
+ {
+ /* nothing */
+ FT_UNUSED( raster );
+ }
+
+
+#else /* _STANDALONE_ */
+
+
+ static int
+ ft_black_new( FT_Memory memory,
+ PRaster *araster )
+ {
+ FT_Error error;
+ PRaster raster;
+
+
+ *araster = 0;
+ if ( !FT_NEW( raster ) )
+ {
+ raster->memory = memory;
+ ft_black_init( raster );
+
+ *araster = raster;
+ }
+
+ return error;
+ }
+
+
+ static void
+ ft_black_done( PRaster raster )
+ {
+ FT_Memory memory = (FT_Memory)raster->memory;
+ FT_FREE( raster );
+ }
+
+
+#endif /* _STANDALONE_ */
+
+
+ static void
+ ft_black_reset( PRaster raster,
+ char* pool_base,
+ long pool_size )
+ {
+ if ( raster )
+ {
+ if ( pool_base && pool_size >= (long)sizeof(TWorker) + 2048 )
+ {
+ PWorker worker = (PWorker)pool_base;
+
+
+ raster->buffer = pool_base + ( (sizeof ( *worker ) + 7 ) & ~7 );
+ raster->buffer_size = ( ( pool_base + pool_size ) -
+ (char*)raster->buffer ) / sizeof ( Long );
+ raster->worker = worker;
+ }
+ else
+ {
+ raster->buffer = NULL;
+ raster->buffer_size = 0;
+ raster->worker = NULL;
+ }
+ }
+ }
+
+
+ static void
+ ft_black_set_mode( PRaster raster,
+ unsigned long mode,
+ const char* palette )
+ {
+#ifdef FT_RASTER_OPTION_ANTI_ALIASING
+
+ if ( mode == FT_MAKE_TAG( 'p', 'a', 'l', '5' ) )
+ {
+ /* set 5-levels gray palette */
+ raster->grays[0] = palette[0];
+ raster->grays[1] = palette[1];
+ raster->grays[2] = palette[2];
+ raster->grays[3] = palette[3];
+ raster->grays[4] = palette[4];
+ }
+
+#else
+
+ FT_UNUSED( raster );
+ FT_UNUSED( mode );
+ FT_UNUSED( palette );
+
+#endif
+ }
+
+
+ static int
+ ft_black_render( PRaster raster,
+ const FT_Raster_Params* params )
+ {
+ const FT_Outline* outline = (const FT_Outline*)params->source;
+ const FT_Bitmap* target_map = params->target;
+ PWorker worker;
+
+
+ if ( !raster || !raster->buffer || !raster->buffer_size )
+ return Raster_Err_Not_Ini;
+
+ /* return immediately if the outline is empty */
+ if ( outline->n_points == 0 || outline->n_contours <= 0 )
+ return Raster_Err_None;
+
+ if ( !outline || !outline->contours || !outline->points )
+ return Raster_Err_Invalid;
+
+ if ( outline->n_points != outline->contours[outline->n_contours - 1] + 1 )
+ return Raster_Err_Invalid;
+
+ worker = raster->worker;
+
+ /* this version of the raster does not support direct rendering, sorry */
+ if ( params->flags & FT_RASTER_FLAG_DIRECT )
+ return Raster_Err_Unsupported;
+
+ if ( !target_map || !target_map->buffer )
+ return Raster_Err_Invalid;
+
+ ras.outline = *outline;
+ ras.target = *target_map;
+
+ worker->buff = (PLong) raster->buffer;
+ worker->sizeBuff = worker->buff +
+ raster->buffer_size / sizeof ( Long );
+#ifdef FT_RASTER_OPTION_ANTI_ALIASING
+ worker->grays = raster->grays;
+ worker->gray_width = raster->gray_width;
+#endif
+
+ return ( ( params->flags & FT_RASTER_FLAG_AA )
+ ? Render_Gray_Glyph( RAS_VAR )
+ : Render_Glyph( RAS_VAR ) );
+ }
+
+
+ const FT_Raster_Funcs ft_standard_raster =
+ {
+ FT_GLYPH_FORMAT_OUTLINE,
+ (FT_Raster_New_Func) ft_black_new,
+ (FT_Raster_Reset_Func) ft_black_reset,
+ (FT_Raster_Set_Mode_Func)ft_black_set_mode,
+ (FT_Raster_Render_Func) ft_black_render,
+ (FT_Raster_Done_Func) ft_black_done
+ };
+
+
+/* END */