From 4fce93e0cb5294786c482c8cbb9233070349686f Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 3 May 2000 18:15:40 +0000 Subject: [PATCH] still working on that damn rasterizer bug !! ;-) --- demos/Makefile | 7 +- demos/src/ftrast.c | 69 + demos/src/ftrast2.c | 3971 +++++++++++++++++++++++++++++++++++++++++++ demos/src/ftrast2.h | 42 + demos/src/ftview.c | 4 +- 5 files changed, 4089 insertions(+), 4 deletions(-) create mode 100644 demos/src/ftrast2.c create mode 100644 demos/src/ftrast2.h diff --git a/demos/Makefile b/demos/Makefile index 5a1b90528..3a69279de 100644 --- a/demos/Makefile +++ b/demos/Makefile @@ -190,6 +190,9 @@ else $(OBJ_)ftrast.$O: $(SRC_DIR_)ftrast.c $(COMPILE) $T$@ $< + $(OBJ_)ftrast2.$O: $(SRC_DIR_)ftrast2.c + $(COMPILE) $T$@ $< + $(OBJ_)fttry.$O: $(SRC_DIR_)fttry.c $(COMPILE) $T$@ $< @@ -264,8 +267,8 @@ else $(LINK) - $(BIN_)ftview$E: $(OBJ_)ftview.$O $(FTLIB) $(GRAPH_LIB) $(COMMON_OBJ) $(OBJ_)ftrast.$O - $(GRAPH_LINK) + $(BIN_)ftview$E: $(OBJ_)ftview.$O $(FTLIB) $(GRAPH_LIB) $(COMMON_OBJ) $(OBJ_)ftrast2.$O + $(GRAPH_LINK) $(OBJ_)ftrast2.$O $(BIN_)ftstring$E: $(OBJ_)ftstring.$O $(FTLIB) $(GRAPH_LIB) $(COMMON_OBJ) $(GRAPH_LINK) diff --git a/demos/src/ftrast.c b/demos/src/ftrast.c index 07c8df437..6f86a7230 100644 --- a/demos/src/ftrast.c +++ b/demos/src/ftrast.c @@ -23,6 +23,75 @@ #include "ftrast.h" #include /* for FT_MulDiv only */ + /*************************************************************************/ + /* */ + /* A simple technical note on how the raster works: */ + /* */ + /* Converting an outline into a bitmap is achieved in several steps */ + /* which are: */ + /* */ + /* 1 - Decomposing the outline into successive `profiles'. Each */ + /* profile is simply an array of scanline intersections on a given */ + /* dimension. A profile's main attributes are */ + /* */ + /* o its scanline position boundaries, i.e. `Ymin' and `Ymax'. */ + /* */ + /* o an array of intersection coordinates for each scanline */ + /* between `Ymin' and `Ymax'. */ + /* */ + /* o a direction, indicating wether is was built going `up' or */ + /* `down', as this is very important for filling rules. */ + /* */ + /* 2 - Sweeping the target map's scanlines in order to compute segment */ + /* `spans' which are then filled. Additionaly, this pass performs */ + /* drop-out control. */ + /* */ + /* The outline data is parsed during step 1 only. The profiles are */ + /* built from the bottom of the render pool, used as a stack. The */ + /* following graphics shows the profile list under construction: */ + /* */ + /* ____________________________________________________________ _ _ */ + /* | | | | | */ + /* | profile | coordinates for | profile | coordinates for |--> */ + /* | 1 | profile 1 | 2 | profile 2 |--> */ + /* |_________|___________________|_________|_________________|__ _ _ */ + /* */ + /* ^ ^ */ + /* | | */ + /* start of render pool 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 propertly. */ + /* */ + /*************************************************************************/ + /****************************************************************/ /****************************************************************/ /** **/ diff --git a/demos/src/ftrast2.c b/demos/src/ftrast2.c new file mode 100644 index 000000000..7f8715cfc --- /dev/null +++ b/demos/src/ftrast2.c @@ -0,0 +1,3971 @@ +/******************************************************************* + * + * ftraster.c 2.0 + * + * The FreeType glyph rasterizer (body). + * + * Copyright 1996-1998 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used + * modified and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + * The "raster" component implements FreeType's scan-line converter, + * the one used to generate bitmaps and pixmaps from vectorial outlines + * descriptions. + * + * It has been rewritten entirely for FreeType 2.0, in order to become + * completely independent of the rest of the library. It should now be + * possible to include it more easily in all kinds of libraries and + * applications, which do not necessarily need the font engines and + * API. + * + * This version features : + * + * - support for third-order bezier arcs + * + * - improved performance of the 5-levels anti-aliasing algorithm + * + * - 17-levels anti-aliasing for smoother curves, though the + * difference isn't always noticeable, depending on your palette + * + * - an API to decompose a raster outline into a path (i.e. into + * a series of segments and arcs). + * + ******************************************************************/ + +#include "ftraster.h" +#include /* for FT_Outline_Decompose */ + +#ifndef EXPORT_FUNC +#define EXPORT_FUNC /* nothing */ +#endif + + +#ifndef _xxFREETYPE_ + +/**************************************************************************/ +/* */ +/* The following defines are used when the raster is compiled as a */ +/* stand-alone object. Each of them is commented, and you're free to */ +/* toggle them to suit your needs.. */ +/* */ + +/**************************************************************************/ +/* */ +/* FT_RASTER_OPTION_ANTI_ALIAS */ +/* */ +/* Define this configuration macro if you want to support anti-aliasing */ +/* */ +#define FT_RASTER_OPTION_ANTI_ALIAS + +/**************************************************************************/ +/* */ +/* FT_RASTER_OPTION_CONIC_BEZIERS */ +/* */ +/* Define this configuration macro if your source outlines contain */ +/* second-order Bezier arcs. Typically, these are TrueType outlines.. */ +/* */ +#define FT_RASTER_CONIC_BEZIERS + +/**************************************************************************/ +/* */ +/* FT_RASTER_OPTION_CUBIC_BEZIERS */ +/* */ +/* Define this configuration macro if your source outlines contain */ +/* third-order Bezier arcs. Typically, these are Type1 outlines.. */ +/* */ +#define FT_RASTER_CUBIC_BEZIERS + +/**************************************************************************/ +/* */ +/* FT_RASTER_ANTI_ALIAS_5 */ +/* */ +/* Define this configuration macro if you want to enable the 5-grays */ +/* anti-aliasing mode.. Ignored if FT_RASTER_OPTION_ANTI_ALIAS isn't */ +/* defined.. */ +/* */ +#define FT_RASTER_ANTI_ALIAS_5 + +/**************************************************************************/ +/* */ +/* FT_RASTER_ANTI_ALIAS_17 */ +/* */ +/* Define this configuration macro if you want to enable the 17-grays */ +/* anti-aliasing mode.. Ignored if FT_RASTER_OPTION_ANTI_ALIAS isn't */ +/* defined.. */ +/* */ +#define FT_RASTER_ANTI_ALIAS_17 + +/**************************************************************************/ +/* */ +/* FT_RASTER_LITTLE_ENDIAN */ +/* FT_RASTER_BIG_ENDIAN */ +/* */ +/* The default anti-alias routines are processor-independent, but slow. */ +/* Define one of these macros to suit your own system, and enjoy */ +/* greatly improved rendering speed */ +/* */ + +/* #define FT_RASTER_LITTLE_ENDIAN */ +/* #define FT_RASTER_BIG_ENDIAN */ + +#else /* _FREETYPE_ */ + +/**************************************************************************/ +/* */ +/* The following defines are used when the raster is compiled within */ +/* the FreeType base layer. Don't change these unless you really know */ +/* what you're doing.. */ +/* */ + +#ifdef FT_CONFIG_OPTION_ANTI_ALIAS +#define FT_RASTER_OPTION_ANTI_ALIAS +#endif + +#define FT_RASTER_CONIC_BEZIERS +#define FT_RASTER_CUBIC_BEZIERS + +#define FT_RASTER_ANTI_ALIAS_5 +#undef FT_RASTER_ANTI_ALIAS_17 + +#ifdef FT_CONFIG_OPTION_LITTLE_ENDIAN +#define FT_RASTER_LITTLE_ENDIAN +#endif + +#ifdef FT_CONFIG_OPTION_BIG_ENDIAN +#define FT_RASTER_BIG_ENDIAN +#endif + +#endif /* _FREETYPE_ */ + + +/* FT_RASTER_ANY_ENDIAN indicates that no endianess was defined */ +/* through one of the configuration macros */ +/* */ +#if !defined(FT_RASTER_LITTLE_ENDIAN) && !defined(FT_RASTER_BIG_ENDIAN) +#define FT_RASTER_ANY_ENDIAN +#endif + + +/* The rasterizer is a very general purpose component, please leave */ +/* the following redefinitions 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 + + +#undef FAILURE +#define FAILURE TRUE + +#undef SUCCESS +#define SUCCESS FALSE + + +/* Please don't touch the following macros. Their importance is historical */ +/* to FreeType, but they have some nice effects, like getting rid of all */ +/* '->' symbols when accessing the raster object.. (replacing them with */ +/* a simple '.' ) */ + +/* used in function signatures to define the _first_ argument */ +#define RAS_ARGS FT_Raster raster, +#define RAS_ARG FT_Raster raster + +/* used to call a function within this component, first parameter */ +#define RAS_VARS raster, +#define RAS_VAR raster + +/* used to access the current raster object, with a '.' instead of a '->' */ +#define ras (*raster) + + +/* For anti-aliasing modes, we use a 2 or 4 lines intermediate bitmap which */ +/* is filtered repeatedly to render each pixmap row. The following macro */ +/* defines this buffer's size in bytes (which is part of raster objects) */ +#define ANTI_ALIAS_BUFFER_SIZE 2048 + + +/* Error codes returned by the scan-line converter/raster */ + +#define ErrRaster_Ok 0 +#define ErrRaster_Uninitialised_Object 1 +#define ErrRaster_Overflow 2 +#define ErrRaster_Negative_Height 3 +#define ErrRaster_Invalid_Outline 4 +#define ErrRaster_Invalid_Map 5 +#define ErrRaster_AntiAlias_Unsupported 6 +#define ErrRaster_Invalid_Pool 7 +#define ErrRaster_Unimplemented 8 +#define ErrRaster_Bad_Palette_Count 9 + + +#define SET_High_Precision(p) Set_High_Precision( RAS_VARS p ) + +/* Fast MulDiv, as 'b' is always < 64, don't use intermediate precision */ +#define FMulDiv( a, b, c ) ( (a) * (b) / (c) ) + + +/* Define DEBUG_RASTER if you want to generate a debug version of the */ +/* rasterizer. This will progressively draw the glyphs while all the */ +/* computation are done directly on the graphics screen (the glyphs */ +/* will be inverted). */ + +/* Note that DEBUG_RASTER should only be used for debugging with b/w */ +/* rendering, not with gray levels. */ + +/* The definition of DEBUG_RASTER should appear in the file */ +/* "ftconfig.h". */ + +#ifdef DEBUG_RASTER + extern char* vio; /* A pointer to VRAM or display buffer */ +#endif + + +#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 */ + /* We always use 26.6, but hackers are free */ + /* to experiment with different values */ + + + /* The type of the pixel coordinates used within the render pool during */ + /* scan-line conversion. We use longs to store either 26.6 or 22.10 fixed */ + /* float values, depending on the "precision" we want to use (resp. low */ + /* or high). These are ideals in order to subdivise bezier arcs in halves */ + /* though simple additions and shifts. */ + + typedef long TPos, *PPos; + + + /* The type of a scanline position/coordinate within a map */ + typedef int TScan, *PScan; + + + + + /* boolean type */ + typedef char TBool; + + /* unsigned char type and array */ + typedef unsigned char TByte, *PByte; + + /* unsigned short type and array */ + typedef unsigned short UShort, *PUShort; + + /* flow */ + enum _TFlow + { + FT_Flow_Error = 0, + FT_Flow_Down = -1, + FT_Flow_Up = 1 + }; + + + /* states/directions of each line, arc and profile */ + enum _TDirection + { + Unknown, + Ascending, + Descending, + Flat + }; + typedef enum _TDirection TDirection; + + + struct _TProfile; + typedef struct _TProfile TProfile; + typedef TProfile* PProfile; + + struct _TProfile + { + TPos X; /* current coordinate during sweep */ + PProfile link; /* link to next profile - various purpose */ + PPos offset; /* start of profile's data in render pool */ + int flow; /* Profile orientation: Asc/Descending */ + TScan height; /* profile's height in scanlines */ + TScan start; /* profile's starting scanline */ + + TScan countL; /* number of lines to step before this */ + /* profile becomes drawable */ + + PProfile next; /* next profile in same contour, used */ + /* during drop-out control */ + }; + + typedef PProfile TProfileList; + typedef PProfile* PProfileList; + + + /* Simple record used to implement a stack of bands, required */ + /* by the sub-banding mechanism */ + /* */ + struct _TBand + { + TScan y_min; /* band's minimum */ + TScan y_max; /* band's maximum */ + }; + + typedef struct _TBand TBand; + + +/* The size in _TPos_ of a profile record in the render pool */ +#define AlignProfileSize ((sizeof(TProfile)+sizeof(TPos)-1) / sizeof(TPos)) + + + /* prototypes used for sweep function dispatch */ + typedef void Function_Sweep_Init( RAS_ARGS int* min, int* max ); + + typedef void Function_Sweep_Span( RAS_ARGS TScan y, + TPos x1, + TPos x2 ); + + typedef int Function_Test_Pixel( RAS_ARGS TScan y, + int x ); + + typedef void Function_Set_Pixel( RAS_ARGS TScan y, + int x, + int color ); + + typedef void Function_Sweep_Step( RAS_ARG ); + + +/* compute lowest integer coordinate below a given x */ +#define FLOOR( x ) ( (x) & ras.precision_mask ) + +/* compute highest integer coordinate above a given x */ +#define CEILING( x ) ( ((x) + ras.precision - 1) & ras.precision_mask ) + +/* get integer coordinate of a given 26.6 or 22.10 'x' coordinate - no round */ +#define TRUNC( x ) ( (signed long)(x) >> ras.precision_bits ) + +/* get the fractional part of a given coordinate */ +#define FRAC( x ) ( (x) & (ras.precision - 1) ) + +/* scale an 'input coordinate' (as found in FT_Outline structures) into */ +/* a 'work coordinate', which depends on current resolution and render */ +/* mode.. */ +#define SCALED( x ) ( ((x) << ras.scale_shift) - ras.precision_half ) + + +/* DEBUG_PSET is used to plot a single pixel in VRam during debug mode */ +#ifdef DEBUG_RASTER +#define DEBUG_PSET Pset() +#else +#define DEBUG_PSET +#endif + + struct _TPoint + { + TPos x, y; + }; + typedef struct _TPoint TPoint; + + + /* 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 FT_RasterRec_ + { + PPos cursor; /* Current cursor in render pool */ + + PPos pool; /* The render pool base address */ + PPos pool_size; /* The render pool's size */ + PPos pool_limit; /* Limit of profiles zone in pool */ + + int bit_width; /* target bitmap width */ + PByte bit_buffer; /* target bitmap buffer */ + PByte pix_buffer; /* target pixmap buffer */ + + TPoint last; + long minY, maxY; + + int error; + + int precision_bits; /* precision related variables */ + int precision; + int precision_half; + long precision_mask; + int precision_shift; + int precision_step; + int precision_jitter; + + FT_Outline* outline; + + int n_points; /* number of points in current glyph */ + int n_contours; /* number of contours in current glyph */ + int n_turns; /* number of Y-turns in outline */ + + TPoint* arc; /* current Bezier arc pointer */ + + int num_profs; /* current number of profiles */ + + TBool fresh; /* signals a fresh new profile which */ + /* 'start' field must be completed */ + TBool joint; /* signals that the last arc ended */ + /* exactly on a scanline. Allows */ + /* removal of doublets */ + PProfile cur_prof; /* current profile */ + PProfile start_prof; /* head of linked list of profiles */ + PProfile first_prof; /* contour's first profile in case */ + /* of impact */ + TDirection state; /* rendering state */ + + FT_Bitmap target; /* description of target bit/pixmap */ + + int trace_bit; /* current offset in target bitmap */ + int trace_pix; /* current offset in target pixmap */ + + int trace_incr; /* sweep's increment in target bitmap */ + + int gray_min_x; /* current min x during gray rendering */ + int gray_max_x; /* current max x during gray rendering */ + + /* dispatch variables */ + + Function_Sweep_Init* Proc_Sweep_Init; + Function_Sweep_Span* Proc_Sweep_Span; + Function_Sweep_Step* Proc_Sweep_Step; + Function_Test_Pixel* Proc_Test_Pixel; + Function_Set_Pixel* Proc_Set_Pixel; + + int scale_shift; /* == 0 for bitmaps */ + /* == 1 for 5-levels pixmaps */ + /* == 2 for 17-levels pixmaps */ + + TByte dropout_mode; /* current drop_out control method */ + + TBool second_pass; /* indicates wether 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. */ + + TBool flipped; /* this flag is set during the rendering to */ + /* indicate the second pass.. */ + + TBand band_stack[16]; /* band stack used for sub-banding */ + int band_top; /* band stack top */ + + TPoint arcs[ 2*MaxBezier+1 ]; /* The Bezier stack */ + + void* memory; + +#if defined(FT_RASTER_OPTION_ANTI_ALIAS) + + long grays[20]; /* Palette of gray levels used for render */ + + int gray_width; /* length in bytes of the onochrome */ + /* intermediate scanline of gray_lines. */ + /* Each gray pixel takes 2 or 4 bits long */ + + /* The gray_lines must hold 2 lines, thus with size */ + /* in bytes of at least 'gray_width*2' */ + + int grays_count; /* number of entries in the palette */ + + char gray_lines[ANTI_ALIAS_BUFFER_SIZE]; + /* Intermediate table used to render the */ + /* graylevels pixmaps. */ + /* gray_lines is a buffer holding 2 or 4 */ + /* monochrome scanlines */ + + int count_table[256]; /* Look-up table used to quickly count */ + /* set bits in a gray 2x2 cell */ +#endif + }; + + + +#ifdef DEBUG_RASTER + + /************************************************/ + /* */ + /* Pset: */ + /* */ + /* Used for debugging only. Plots a point */ + /* in VRAM during rendering (not afterwards). */ + /* */ + /* NOTE: This procedure relies on the value */ + /* of cProfile->start, which may not */ + /* be set when Pset is called sometimes. */ + /* This will usually result in a dot */ + /* plotted on the first screen scanline */ + /* (far away its original position). */ + /* */ + /* This "bug" reflects nothing wrong */ + /* in the current implementation, and */ + /* the bitmap is rendered correctly, */ + /* so don't panic if you see 'flying' */ + /* dots in debugging mode. */ + /* */ + /* - David */ + /* */ + /************************************************/ + + static void Pset( RAS_ARG ) + { + long o; + long x; + + x = ras.cursor[-1]; + + switch ( ras.cur_prof->flow ) + { + case FT_Flow_Up: + o = Vio_ScanLineWidth * + ( ras.cursor - ras.cur_prof->offset + ras.cur_prof->start ) + + ( x / (ras.precision*8) ); + break; + + case FT_Flow_Down: + o = Vio_ScanLineWidth * + ( ras.cur_prof->start - ras.cursor + ras.cur_prof->offset ) + + ( x / (ras.precision*8) ); + break; + } + + if ( o > 0 ) + Vio[o] |= (unsigned)0x80 >> ( (x/ras.precision) & 7 ); + } + + + static void Clear_Band( RAS_ARGS Int y1, Int y2 ) + { + MEM_Set( Vio + y1*Vio_ScanLineWidth, (y2-y1+1)*Vio_ScanLineWidth, 0 ); + } + +#endif /* DEBUG_RASTER */ + + +/************************************************************************/ +/* */ +/* Set_High_Precision */ +/* */ +/* Sets precision variables according to param flag. */ +/* */ +/* 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; + } + + 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; + } + + +/**************************************************************************/ +/* */ +/* New_Profile */ +/* */ +/* Creates a new Profile in the render pool. */ +/* */ +/* */ +/* aState :: state/orientation of the new Profile */ +/* */ +/* */ +/* SUCCESS or FAILURE */ +/* */ +/**************************************************************************/ + + static + TBool New_Profile( RAS_ARGS TDirection direction ) + { + if ( ras.start_prof == NULL ) + { + ras.cur_prof = (PProfile)ras.cursor; + ras.start_prof = ras.cur_prof; + ras.cursor += AlignProfileSize; + } + + if ( ras.cursor >= ras.pool_limit ) + { + ras.error = ErrRaster_Overflow; + return FAILURE; + } + + switch ( direction ) + { + case Ascending: + ras.cur_prof->flow = FT_Flow_Up; + break; + + case Descending: + ras.cur_prof->flow = FT_Flow_Down; + break; + + default: + ras.error = ErrRaster_Invalid_Map; + return FAILURE; + } + + { + PProfile cur = ras.cur_prof; + + cur->start = 0; + cur->height = 0; + cur->offset = ras.cursor; + cur->link = (PProfile)0; + cur->next = (PProfile)0; + } + + if ( ras.first_prof == NULL ) + ras.first_prof = ras.cur_prof; + + ras.state = direction; + ras.fresh = TRUE; + ras.joint = FALSE; + + return SUCCESS; + } + + +/****************************************************************************/ +/* */ +/* End_Profile */ +/* */ +/* Finalizes the current Profile. */ +/* */ +/* */ +/* SUCCESS or FAILURE */ +/* */ +/****************************************************************************/ + + static + TBool End_Profile( RAS_ARG ) + { + int h; + + h = ras.cursor - ras.cur_prof->offset; + + if ( h < 0 ) + { + ras.error = ErrRaster_Negative_Height; + return FAILURE; + } + + if ( h > 0 ) + { + PProfile old, new; + + old = ras.cur_prof; + old->height = h; + ras.cur_prof = new = (PProfile)ras.cursor; + + ras.cursor += AlignProfileSize; + + new->height = 0; + new->offset = ras.cursor; + old->next = new; + + ras.num_profs++; + } + + if ( ras.cursor >= ras.pool_limit ) + { + ras.error = ErrRaster_Overflow; + return FAILURE; + } + + ras.joint = FALSE; + + return SUCCESS; + } + + +/****************************************************************************/ +/* */ +/* Insert_Y_Turn */ +/* */ +/* */ +/* Insert a salient into the sorted list placed on top */ +/* of the render pool */ +/* */ +/* */ +/* y :: new scanline position */ +/* */ +/****************************************************************************/ + + static + TBool Insert_Y_Turn( RAS_ARGS TScan y ) + { + PPos y_turns; + TScan y2; + int n; + + n = ras.n_turns-1; + y_turns = ras.pool_size - ras.n_turns; + + /* 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 = y_turns[n]; + y_turns[n] = y; + y = y2; + n--; + } + + if (n < 0) + { + ras.pool_limit--; + ras.n_turns++; + ras.pool_size[-ras.n_turns ] = y; + + if ( ras.pool_limit <= ras.cursor ) + { + ras.error = ErrRaster_Overflow; + return FAILURE; + } + } + return SUCCESS; + } + + +/****************************************************************************/ +/* */ +/* Finalize_Profile_Table */ +/* */ +/* */ +/* Adjusts all links in the Profiles list. */ +/* */ +/****************************************************************************/ + + static + TBool Finalize_Profile_Table( RAS_ARG ) + { + int n, bottom, top; + PProfile p; + + + n = ras.num_profs; + + if ( n > 1 ) + { + p = ras.start_prof; + while ( n > 0 ) + { + if (n > 1) + p->link = (PProfile)( p->offset + p->height ); + else + p->link = NULL; + + switch (p->flow) + { + case FT_Flow_Down: + bottom = p->start - p->height+1; + top = p->start; + p->start = bottom; + p->offset += p->height-1; + break; + + case FT_Flow_Up: + default: + bottom = p->start; + top = 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.start_prof = NULL; + + return SUCCESS; + } + + + +/****************************************************************************/ +/* */ +/* Line_Up */ +/* */ +/* */ +/* Computes the scan-line intersections of an ascending line segment */ +/* and stores them in the render pool. */ +/* */ +/* */ +/* x1 :: start x coordinate */ +/* y1 :: start y coordinates */ +/* x2 :: end x coordinate */ +/* y2 :: end y coordinate */ +/* miny :: minimum vertical grid coordinate */ +/* maxy :: maximum vertical grid coordinate */ +/* */ +/* */ +/* SUCCESS or FAILURE. */ +/* */ +/****************************************************************************/ + + static + TBool Line_Up( RAS_ARGS TPos x1, TPos y1, + TPos x2, TPos y2, + TPos miny, TPos maxy ) + { + TPos Dx, Dy; + int e1, e2, f1, f2, size; + TPos Ix, Rx, Ax; + + PPos top; + + + Dx = x2 - x1; + Dy = y2 - y1; + + if ( Dy <= 0 || y2 < miny || y1 > maxy ) + return SUCCESS; + + if ( y1 < miny ) + { + x1 += FMulDiv( Dx, miny - y1, Dy ); + e1 = TRUNC( miny ); + f1 = 0; + } + else + { + e1 = TRUNC( y1 ); + f1 = FRAC( y1 ); + } + + if ( y2 > maxy ) + { + /* x2 += FMulDiv( Dx, maxy-y2, Dy ); UNNECESSARY */ + e2 = TRUNC( maxy ); + f2 = 0; + } + else + { + e2 = TRUNC( y2 ); + f2 = 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.cursor--; + ras.joint = FALSE; + } + + ras.joint = ( f2 == 0 ); + + if ( ras.fresh ) + { + ras.cur_prof->start = e1; + ras.fresh = FALSE; + } + + size = e2 - e1 + 1; + if ( ras.cursor + size >= ras.pool_limit ) + { + ras.error = ErrRaster_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.cursor; + + while ( size > 0 ) + { + *top++ = x1; + + DEBUG_PSET; + + x1 += Ix; + Ax += Rx; + if ( Ax >= 0 ) + { + Ax -= Dy; + x1 += Dx; + } + size--; + } + + ras.cursor = top; + return SUCCESS; + } + + +/****************************************************************************/ +/* */ +/* Line_Down */ +/* */ +/* */ +/* Computes the scan-line intersections of a descending line segment */ +/* and stores them in the render pool. */ +/* */ +/* */ +/* x1 :: start x coordinate */ +/* y1 :: start y coordinates */ +/* x2 :: end x coordinate */ +/* y2 :: end y coordinate */ +/* miny :: minimum vertical grid coordinate */ +/* maxy :: maximum vertical grid coordinate */ +/* */ +/* */ +/* SUCCESS or FAILURE. */ +/* */ +/****************************************************************************/ + + static + TBool Line_Down( RAS_ARGS TPos x1, TPos y1, + TPos x2, TPos y2, + TPos miny, TPos maxy ) + { + TBool result, fresh; + + fresh = ras.fresh; + result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny ); + + if ( fresh && !ras.fresh ) + ras.cur_prof->start = -ras.cur_prof->start; + + return result; + } + + + + +#ifdef FT_RASTER_CONIC_BEZIERS +/****************************************************************************/ +/* */ +/* Split_Conic */ +/* */ +/* */ +/* Subdivides one second-order Bezier arc into two joint sub-arcs in */ +/* the Bezier stack. */ +/* */ +/* */ +/* This routine is the "beef" of the component. It is one of _the_ */ +/* inner loops that should be optimized like hell to get the best */ +/* performance.. */ +/* */ +/****************************************************************************/ + + static + void Split_Conic( TPoint* base ) + { + TPos a, b; + + base[4].x = base[2].x; + b = base[1].x; + a = base[3].x = ( base[2].x + b )/2; + b = base[1].x = ( base[0].x + b )/2; + base[2].x = (a+b)/2; + + base[4].y = base[2].y; + b = base[1].y; + a = base[3].y = ( base[2].y + b )/2; + b = base[1].y = ( base[0].y + b )/2; + base[2].y = (a+b)/2; + } + +/****************************************************************************/ +/* */ +/* Push_Conic */ +/* */ +/* Clears the Bezier stack and pushes a new arc on top of it */ +/* */ +/* */ +/* p2 :: pointer to second (control) point */ +/* p3 :: pointer to third (end) point */ +/* */ +/* */ +/* The first point is taken as "raster->last", so it doesn't appear */ +/* in the signature.. */ +/* */ +/****************************************************************************/ + + static + void Push_Conic( RAS_ARGS FT_Vector* p2, + FT_Vector* p3 ) + { +#undef STORE +#define STORE( _arc, point ) \ + { \ + TPos x = SCALED(point->x); \ + TPos y = SCALED(point->y); \ + if (ras.flipped) \ + { \ + _arc.x = y; \ + _arc.y = x; \ + } \ + else \ + { \ + _arc.x = x; \ + _arc.y = y; \ + } \ + } + + TPoint* arc; + ras.arc = arc = ras.arcs; + + arc[2] = ras.last; + STORE( arc[1], p2 ); + STORE( arc[0], p3 ); +#undef STORE + } + + +/****************************************************************************/ +/* */ +/* Conic_Up */ +/* */ +/* */ +/* Computes the scan-line intersections of an ascending second-order */ +/* Bezier arc and stores them in the render pool. The arc is taken */ +/* from the top of the stack.. */ +/* */ +/* */ +/* miny :: minimum vertical grid coordinate */ +/* maxy :: maximum vertical grid coordinate */ +/* */ +/* */ +/* SUCCESS or FAILURE */ +/* */ +/****************************************************************************/ + + static + TBool Conic_Up( RAS_ARGS TPos miny, TPos maxy ) + { + TPos y1, y2, e, e2, e0; + int f1; + + TPoint* arc; + TPoint* start_arc; + + PPos top; + + + arc = ras.arc; + y1 = arc[2].y; + y2 = arc[0].y; + top = ras.cursor; + + 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 = FRAC( y1 ); + e0 = e; + + if ( f1 == 0 ) + { + if ( ras.joint ) + { + top--; + ras.joint = FALSE; + } + + *top++ = arc[2].x; + + DEBUG_PSET; + + e += ras.precision; + } + } + + if ( ras.fresh ) + { + ras.cur_prof->start = TRUNC( e0 ); + ras.fresh = FALSE; + } + + if ( e2 < e ) + goto Fin; + + if ( ( top+TRUNC(e2-e)+1 ) >= ras.pool_limit ) + { + ras.cursor = top; + ras.error = ErrRaster_Overflow; + return FAILURE; + } + + start_arc = arc; + + while ( arc >= start_arc && e <= e2 ) + { + ras.joint = FALSE; + + y2 = arc[0].y; + + if ( y2 > e ) + { + y1 = arc[2].y; + if ( y2 - y1 >= ras.precision_step ) + { + Split_Conic( arc ); + arc += 2; + } + else + { + *top++ = arc[2].x + + FMulDiv( arc[0].x-arc[2].x, + e - y1, + y2 - y1 ); + DEBUG_PSET; + + arc -= 2; + e += ras.precision; + } + } + else + { + if ( y2 == e ) + { + ras.joint = TRUE; + *top++ = arc[0].x; + + DEBUG_PSET; + + e += ras.precision; + } + arc -= 2; + } + } + + Fin: + ras.cursor = top; + ras.arc -= 2; + return SUCCESS; + } + + +/****************************************************************************/ +/* */ +/* Conic_Down */ +/* */ +/* */ +/* Computes the scan-line intersections of a descending second-order */ +/* Bezier arc and stores them in the render pool. The arc is taken */ +/* from the top of the stack.. */ +/* */ +/* */ +/* miny :: minimum vertical grid coordinate */ +/* maxy :: maximum vertical grid coordinate */ +/* */ +/* */ +/* SUCCESS or FAILURE */ +/* */ +/****************************************************************************/ + + static + TBool Conic_Down( RAS_ARGS TPos miny, TPos maxy ) + { + TPoint* arc = ras.arc; + TBool result, fresh; + + + arc[0].y = -arc[0].y; + arc[1].y = -arc[1].y; + arc[2].y = -arc[2].y; + + fresh = ras.fresh; + + result = Conic_Up( RAS_VARS -maxy, -miny ); + + if ( fresh && !ras.fresh ) + ras.cur_prof->start = -ras.cur_prof->start; + + arc[0].y = -arc[0].y; + return result; + } + +#endif /* FT_RASTER_CONIC_BEZIERS */ + + + +#ifdef FT_RASTER_CUBIC_BEZIERS +/****************************************************************************/ +/* */ +/* Split_Cubic */ +/* */ +/* */ +/* Subdivides a third-order Bezier arc into two joint sub-arcs in the */ +/* Bezier stack. */ +/* */ +/* */ +/* This routine is the "beef" of the component. It is one of _the_ */ +/* inner loops that should be optimized like hell to get the best */ +/* performance.. */ +/* */ +/****************************************************************************/ + + static + void Split_Cubic( TPoint* base ) + { + TPos a, b, c, d; + + base[6].x = base[3].x; + c = base[1].x; + d = base[2].x; + base[1].x = a = ( base[0].x + c )/2; + base[5].x = b = ( base[3].x + d )/2; + c = (c+d)/2; + base[2].x = a = (a+c)/2; + base[4].x = b = (b+c)/2; + base[3].x = (a+b)/2; + + base[6].y = base[3].y; + c = base[1].y; + d = base[2].y; + base[1].y = a = ( base[0].y + c )/2; + base[5].y = b = ( base[3].y + d )/2; + c = (c+d)/2; + base[2].y = a = (a+c)/2; + base[4].y = b = (b+c)/2; + base[3].y = (a+b)/2; + } + +/****************************************************************************/ +/* */ +/* Push_Cubic */ +/* */ +/* */ +/* Clears the bezier stack and pushes a new third-order bezier arc */ +/* on top of it */ +/* */ +/* */ +/* p2 :: pointer to second point (control) */ +/* p3 :: pointer to third point (control) */ +/* p4 :: pointer to last point (end) */ +/* */ +/* */ +/* The first point is taken as "raster->last", so it doesn't appear */ +/* in the signature.. */ +/* */ +/****************************************************************************/ + + static + void Push_Cubic( RAS_ARGS FT_Vector* p2, + FT_Vector* p3, + FT_Vector* p4 ) + { +#undef STORE +#define STORE( _arc, point ) \ + { \ + TPos x = SCALED(point->x); \ + TPos y = SCALED(point->y); \ + if (ras.flipped) \ + { \ + _arc.x = y; \ + _arc.y = x; \ + } \ + else \ + { \ + _arc.x = x; \ + _arc.y = y; \ + } \ + } + + TPoint* arc; + ras.arc = arc = ras.arcs; + + arc[3] = ras.last; + STORE( arc[2], p2 ); + STORE( arc[1], p3 ); + STORE( arc[0], p4 ); + +#undef STORE + } + + +/****************************************************************************/ +/* */ +/* Cubic_Up */ +/* */ +/* */ +/* Computes the scan-line intersections of an ascending third-order */ +/* bezier arc and stores them in the render pool */ +/* */ +/* */ +/* miny :: minimum vertical grid coordinate */ +/* maxy :: maximum vertical grid coordinate */ +/* */ +/* */ +/* SUCCESS or FAILURE */ +/* */ +/****************************************************************************/ + static + TBool Cubic_Up( RAS_ARGS TPos miny, TPos maxy ) + { + TPos y1, y2, e, e2, e0; + int f1; + + TPoint* arc; + TPoint* start_arc; + + TPos* top; + + + arc = ras.arc; + y1 = arc[3].y; + y2 = arc[0].y; + top = ras.cursor; + + 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 = FRAC( y1 ); + e0 = e; + + if ( f1 == 0 ) + { + if ( ras.joint ) + { + top--; + ras.joint = FALSE; + } + + *top++ = arc[3].x; + + DEBUG_PSET; + + e += ras.precision; + } + } + + if ( ras.fresh ) + { + ras.cur_prof->start = TRUNC( e0 ); + ras.fresh = FALSE; + } + + if ( e2 < e ) + goto Fin; + + if ( ( top+TRUNC(e2-e)+1 ) >= ras.pool_limit ) + { + ras.cursor = top; + ras.error = ErrRaster_Overflow; + return FAILURE; + } + + start_arc = arc; + + while ( arc >= start_arc && e <= e2 ) + { + ras.joint = FALSE; + + y2 = arc[0].y; + + if ( y2 > e ) + { + y1 = arc[3].y; + if ( y2 - y1 >= ras.precision_step ) + { + Split_Cubic( arc ); + arc += 3; + } + else + { + *top++ = arc[3].x + + FMulDiv( arc[0].x-arc[3].x, + e - y1, + y2 - y1 ); + DEBUG_PSET; + + arc -= 3; + e += ras.precision; + } + } + else + { + if ( y2 == e ) + { + ras.joint = TRUE; + *top++ = arc[0].x; + + DEBUG_PSET; + + e += ras.precision; + } + arc -= 3; + } + } + + Fin: + ras.cursor = top; + ras.arc -= 3; + return SUCCESS; + } + + +/****************************************************************************/ +/* */ +/* Cubic_Down */ +/* */ +/* */ +/* Computes the scan-line intersections of a descending third-order */ +/* bezier arc and stores them in the render pool */ +/* */ +/* */ +/* miny :: minimum vertical grid coordinate */ +/* maxy :: maximum vertical grid coordinate */ +/* */ +/* */ +/* SUCCESS or FAILURE */ +/* */ +/****************************************************************************/ + static + TBool Cubic_Down( RAS_ARGS TPos miny, TPos maxy ) + { + TPoint* arc = ras.arc; + TBool result, fresh; + + + arc[0].y = -arc[0].y; + arc[1].y = -arc[1].y; + arc[2].y = -arc[2].y; + arc[3].y = -arc[3].y; + + fresh = ras.fresh; + + result = Cubic_Up( RAS_VARS -maxy, -miny ); + + if ( fresh && !ras.fresh ) + ras.cur_prof->start = -ras.cur_prof->start; + + arc[0].y = -arc[0].y; + return result; + } + +#endif /* FT_RASTER_CUBIC_BEZIERS */ + +/****************************************************************************/ +/* */ +/* Check_Contour */ +/* */ +/* */ +/* perform some check at contour closure. */ +/* */ +/* */ +/* SUCCESS or FAILURE */ +/* */ +/****************************************************************************/ + + static + TBool Check_Contour( RAS_ARG ) + { + PProfile lastProfile; + + /* We must now see if the extreme arcs join or not */ + if ( ( FRAC( ras.last.y ) == 0 && + ras.last.y >= ras.minY && + ras.last.y <= ras.maxY ) ) + { + if ( ras.first_prof && ras.first_prof->flow == ras.cur_prof->flow ) + ras.cursor--; + } + + lastProfile = ras.cur_prof; + if ( End_Profile( RAS_VAR ) ) return FAILURE; + + /* close the 'next profile in contour' linked list */ + lastProfile->next = ras.first_prof; + + return SUCCESS; + } + +/****************************************************************************/ +/* */ +/* Move_To */ +/* */ +/* */ +/* This function injects a new contour in the render pool.. */ +/* */ +/* */ +/* to :: pointer to the contour's first point */ +/* raster :: a pointer to the current raster object */ +/* */ +/* */ +/* Error code, 0 means success */ +/* */ +/* */ +/* This function is used as a "FTRasterMoveTo_Func" by the outline */ +/* decomposer.. */ +/* */ +/****************************************************************************/ + + static + int Move_To( FT_Vector* to, + FT_Raster raster ) + { + /* if there was already a contour being built, perform some checks */ + if ( ras.start_prof ) + if ( Check_Contour( RAS_VAR ) ) + return FAILURE; + + /* set the "current last point" */ + if (ras.flipped) + { + ras.last.x = SCALED( to->y ); + ras.last.y = SCALED( to->x ); + } + else + { + ras.last.x = SCALED( to->x ); + ras.last.y = SCALED( to->y ); + } + + ras.state = Unknown; + ras.first_prof = NULL; + + return SUCCESS; + } + +/****************************************************************************/ +/* */ +/* Line_To */ +/* */ +/* */ +/* This function injects a new line segment in the render pool and */ +/* adjusts the profiles list accordingly.. */ +/* */ +/* */ +/* to :: pointer to the target position */ +/* raster :: a pointer to the current raster object */ +/* */ +/* */ +/* Error code, 0 means success */ +/* */ +/* */ +/* This function is used as a "FTRasterLineTo_Func" by the outline */ +/* decomposer.. */ +/* */ +/****************************************************************************/ + + static + int Line_To( FT_Vector* to, + FT_Raster raster ) + { + TPos x; + TPos y; + + if ( ras.flipped ) + { + x = SCALED(to->y); + y = SCALED(to->x); + } + else + { + x = SCALED(to->x); + y = SCALED(to->y); + } + + /* First, detect a change of direction */ + + switch ( ras.state ) + { + case Unknown: + if ( y > ras.last.y ) + { + if ( New_Profile( RAS_VARS Ascending ) ) return FAILURE; + } + else + { + if ( y < ras.last.y ) + if ( New_Profile( RAS_VARS Descending ) ) return FAILURE; + } + break; + + case Ascending: + if ( y < ras.last.y ) + { + if ( End_Profile( RAS_VAR ) || + New_Profile( RAS_VARS Descending ) ) return FAILURE; + } + break; + + case Descending: + if ( y > ras.last.y ) + { + if ( End_Profile( RAS_VAR ) || + New_Profile( RAS_VARS Ascending ) ) return FAILURE; + } + break; + + default: + ; + } + + /* Then compute the lines */ + + switch ( ras.state ) + { + case Ascending: + if ( Line_Up ( RAS_VARS ras.last.x, ras.last.y, + x, y, ras.minY, ras.maxY ) ) + return FAILURE; + break; + + case Descending: + if ( Line_Down( RAS_VARS ras.last.x, ras.last.y, + x, y, ras.minY, ras.maxY ) ) + return FAILURE; + break; + + default: + ; + } + + ras.last.x = x; + ras.last.y = y; + + return SUCCESS; + } + +#ifdef FT_RASTER_CONIC_BEZIERS +/****************************************************************************/ +/* */ +/* Conic_To */ +/* */ +/* */ +/* Injects a new conic bezier arc and adjusts the profile list */ +/* accordingly. */ +/* */ +/* */ +/* control :: pointer to intermediate control point */ +/* to :: pointer to end point */ +/* raster :: handle to current raster object */ +/* */ +/* */ +/* Error code, 0 means success */ +/* */ +/* */ +/* This function is used as a "FTRasterConicTo_Func" by the outline */ +/* decomposer.. */ +/* */ +/****************************************************************************/ + + static + int Conic_To( FT_Vector* control, + FT_Vector* to, + FT_Raster raster ) + { + TPos y1, y2, y3, x3; + TDirection state_bez; + + + Push_Conic( RAS_VARS control, to ); + + do + { + y1 = ras.arc[2].y; + y2 = ras.arc[1].y; + y3 = ras.arc[0].y; + x3 = ras.arc[0].x; + + /* first, categorize the bezier arc */ + + if( y1 == y3 ) + { + if ( y2 == y1 ) + state_bez = Flat; + else + state_bez = Unknown; + } + else if ( y1 < y3 ) + { + if ( y2 < y1 || y2 > y3 ) + state_bez = Unknown; + else + state_bez = Ascending; + } + else + { + if ( y2 < y3 || y2 > y1 ) + state_bez = Unknown; + else + state_bez = Descending; + } + + /* split non-monotonic arcs, ignore flat ones, or */ + /* computes the up and down ones */ + + switch ( state_bez ) + { + case Flat: + ras.arc -= 2; + break; + + case Unknown: + Split_Conic( ras.arc ); + ras.arc += 2; + break; + + default: + /* detect a change of direction */ + + if ( ras.state != state_bez ) + { + if ( ras.state != Unknown ) + if ( End_Profile( RAS_VAR ) ) return FAILURE; + + if ( New_Profile( RAS_VARS state_bez ) ) return FAILURE; + } + + /* compute intersections */ + switch ( ras.state ) + { + case Ascending: + if ( Conic_Up ( RAS_VARS ras.minY, ras.maxY ) ) + return FAILURE; + break; + + case Descending: + if ( Conic_Down( RAS_VARS ras.minY, ras.maxY ) ) + return FAILURE; + break; + + default: + ; + } + } + } while ( ras.arc >= ras.arcs ); + + ras.last.x = x3; + ras.last.y = y3; + + return 0; + } + +#else + static + int Conic_To( FT_Vector* control, + FT_Vector* to, + FT_Raster raster ) + { + (void)control; + (void)to; + (void)raster; + + return ErrRaster_Invalid_Outline; + } +#endif /* CONIC_BEZIERS */ + + +#ifdef FT_RASTER_CUBIC_BEZIERS +/****************************************************************************/ +/* */ +/* Cubic_To */ +/* */ +/* */ +/* Injects a new cubic bezier arc and adjusts the profile list */ +/* accordingly. */ +/* */ +/* */ +/* control1 :: pointer to first control point */ +/* control2 :: pointer to second control point */ +/* to :: pointer to end point */ +/* raster :: handle to current raster object */ +/* */ +/* */ +/* Error code, 0 means success */ +/* */ +/* */ +/* This function is used as a "FTRasterCubicTo_Func" by the outline */ +/* decomposer.. */ +/* */ +/****************************************************************************/ + + static + int Cubic_To( FT_Vector* control1, + FT_Vector* control2, + FT_Vector* to, + FT_Raster raster ) + { + TPos y1, y2, y3, y4, x4; + TDirection state_bez; + + + Push_Cubic( RAS_VARS control1, control2, to ); + + do + { + y1 = ras.arc[3].y; + y2 = ras.arc[2].y; + y3 = ras.arc[1].y; + y4 = ras.arc[0].y; + x4 = ras.arc[0].x; + + /* first, categorize the bezier arc */ + if ( y1 == y4 ) + { + if ( y1 == y2 && y1 == y3 ) + state_bez = Flat; + else + state_bez = Unknown; + } + else if ( y1 < y4 ) + { + if ( y2 < y1 || y2 > y4 || y3 < y1 || y3 > y4 ) + state_bez = Unknown; + else + state_bez = Ascending; + } + else + { + if ( y2 < y4 || y2 > y1 || y3 < y4 || y3 > y1 ) + state_bez = Unknown; + else + state_bez = Descending; + } + + /* split non-monotonic arcs, ignore flat ones, or */ + /* computes the up and down ones */ + + switch ( state_bez ) + { + case Flat: + ras.arc -= 3; + break; + + case Unknown: + Split_Cubic( ras.arc ); + ras.arc += 3; + break; + + default: + /* detect a change of direction */ + + if ( ras.state != state_bez ) + { + if ( ras.state != Unknown ) + if ( End_Profile( RAS_VAR ) ) return FAILURE; + + if ( New_Profile( RAS_VARS state_bez ) ) return FAILURE; + } + + /* compute */ + + switch ( ras.state ) + { + case Ascending: + if ( Cubic_Up ( RAS_VARS ras.minY, ras.maxY ) ) + return FAILURE; + break; + + case Descending: + if ( Cubic_Down( RAS_VARS ras.minY, ras.maxY ) ) + return FAILURE; + break; + + default: + ; + } + } + } while ( ras.arc >= ras.arcs ); + + ras.last.x = x4; + ras.last.y = y4; + + return 0; + } + +#else + int Cubic_To( FT_Vector* control1, + FT_Vector* control2, + FT_Vector* to, + FT_Raster raster ) + { + (void)control1; + (void)control2; + (void)to; + (void)raster; + + return ErrRaster_Invalid_Outline; + } +#endif /* CUBIC_BEZIERS */ + + +/****************************************************************************/ +/* */ +/* Convert_Glyph */ +/* */ +/* */ +/* Converts a glyph into a series of segments and arcs and makes */ +/* a profiles list with them.. */ +/* */ +/* */ +/* outline :: glyph outline */ +/* */ +/* */ +/* SUCCESS or FAILURE */ +/* */ +/****************************************************************************/ + + static + TBool Convert_Glyph( RAS_ARGS FT_Outline* outline ) + { + static + FT_Outline_Funcs interface = + { + (FT_Outline_MoveTo_Func)Move_To, + (FT_Outline_LineTo_Func)Line_To, + (FT_Outline_ConicTo_Func)Conic_To, + (FT_Outline_CubicTo_Func)Cubic_To + }; + + /* Set up state in the raster object */ + ras.start_prof = NULL; + ras.joint = FALSE; + ras.fresh = FALSE; + + ras.pool_limit = ras.pool_size - AlignProfileSize; + + ras.n_turns = 0; + + ras.cur_prof = (PProfile)ras.cursor; + ras.cur_prof->offset = ras.cursor; + ras.num_profs = 0; + + /* Now decompose curve */ + if ( FT_Outline_Decompose( outline, &interface, &ras ) ) return FAILURE; + /* XXX : the error condition is in ras.error */ + + /* Check the last contour if needed */ + if ( Check_Contour( RAS_VAR ) ) return FAILURE; + + /* Finalize profiles list */ + return Finalize_Profile_Table( RAS_VAR ); + } + + +/************************************************/ +/* */ +/* Init_Linked */ +/* */ +/* Inits an empty linked list. */ +/* */ +/************************************************/ + + static void Init_Linked( TProfileList* l ) + { + *l = NULL; + } + + +/************************************************/ +/* */ +/* InsNew : */ +/* */ +/* Inserts a new Profile in a linked list. */ +/* */ +/************************************************/ + + static void InsNew( PProfileList list, + PProfile profile ) + { + PProfile *old, current; + TPos x; + + + old = list; + current = *old; + x = profile->X; + + while ( current ) + { + if ( x < current->X ) + break; + old = ¤t->link; + current = *old; + } + + profile->link = current; + *old = profile; + } + + +/*************************************************/ +/* */ +/* DelOld : */ +/* */ +/* Removes an old Profile from a linked list. */ +/* */ +/*************************************************/ + + static void DelOld( PProfileList list, + PProfile profile ) + { + PProfile *old, current; + + + old = list; + current = *old; + + while ( current ) + { + if ( current == profile ) + { + *old = current->link; + return; + } + + old = ¤t->link; + current = *old; + } + + /* we should never get there, unless the Profile was not part of */ + /* the list. */ + } + +/************************************************/ +/* */ +/* Update : */ +/* */ +/* Update all X offsets of a drawing list */ +/* */ +/************************************************/ + + static void Update( PProfile first ) + { + PProfile current = first; + + while (current) + { + current->X = *current->offset; + current->offset += current->flow; + current->height--; + current = current->link; + } + } + +/************************************************/ +/* */ +/* Sort : */ +/* */ +/* Sorts a trace list. In 95%, the list */ +/* is already sorted. We need an algorithm */ +/* which is fast in this case. Bubble sort */ +/* is enough and simple. */ +/* */ +/************************************************/ + + static void Sort( PProfileList list ) + { + PProfile *old, current, next; + + + /* First, set the new X coordinate of each profile */ + Update( *list ); + + /* Then sort them */ + old = list; + current = *old; + + if ( !current ) + return; + + next = current->link; + + while ( next ) + { + if ( current->X <= next->X ) + { + old = ¤t->link; + current = *old; + + if ( !current ) + return; + } + else + { + *old = next; + current->link = next->link; + next->link = current; + + old = list; + current = *old; + } + + next = current->link; + } + } + + +/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/******** *********/ +/******** Vertical Bitmap Sweep Routines *********/ +/******** *********/ +/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ + + +/***********************************************************************/ +/* */ +/* Vertical_Sweep_Init */ +/* */ +/* */ +/* Initialise the vertical bitmap sweep. Called by the generic */ +/* sweep/draw routine before its loop.. */ +/* */ +/* */ +/* min :: address of current minimum scanline */ +/* max :: address of current maximum scanline */ +/* */ +/***********************************************************************/ + + static + void Vertical_Sweep_Init( RAS_ARGS int* min, int* max ) + { + long pitch = ras.target.pitch; + + ras.trace_incr = -pitch; + ras.trace_bit = -*min*pitch; + if (pitch > 0) + ras.trace_bit += (ras.target.rows-1)*pitch; + + ras.gray_min_x = 0; + ras.gray_max_x = 0; + } + +/***********************************************************************/ +/* */ +/* Vertical_Sweep_Span */ +/* */ +/* */ +/* draws a single horizontal bitmap span during the vertical */ +/* bitmap sweep. */ +/* */ +/* */ +/* y :: current scanline */ +/* x1 :: left span edge */ +/* x2 :: right span edge */ +/* */ +/***********************************************************************/ + + static void Vertical_Sweep_Span( RAS_ARGS TScan y, + TPos x1, + TPos x2 ) + { + TPos e1, e2; + int c1, c2; + TByte f1, f2; + TByte* target; + + /* Drop-out control */ + + e1 = TRUNC( CEILING( x1 ) ); + if ( x2-x1-ras.precision <= ras.precision_jitter ) + e2 = e1; + else + e2 = TRUNC( FLOOR( x2 ) ); + + if ( e1 <= e2 && e2 >= 0 && e1 < ras.bit_width ) + { + if ( e1 < 0 ) e1 = 0; + if ( e2 >= ras.bit_width ) e2 = ras.bit_width-1; + + c1 = e1 >> 3; + c2 = e2 >> 3; + + f1 = ((unsigned char)0xFF >> (e1 & 7)); + f2 = ~((unsigned char)0x7F >> (e2 & 7)); + + target = ras.bit_buffer + ras.trace_bit + c1; + c2 -= c1; + + if ( c2 > 0 ) + { + target[0] |= f1; + + /* memset is slower than the following code on many platforms */ + /* this is due to the fact that, in the vast majority of cases, */ + /* the span length in bytes is relatively small.. */ + c2--; + while (c2 > 0) + { + * ++target = 0xFF; + c2--; + } + target[1] |= f2; + } + else + *target |= ( f1 & f2 ); + } + } + +/***********************************************************************/ +/* */ +/* Vertical_Test_Pixel */ +/* */ +/* */ +/* test a pixel 'light' during the vertical bitmap sweep. Used */ +/* during drop-out control only.. */ +/* */ +/* */ +/* y :: current scanline */ +/* x :: current x coordinate */ +/* */ +/***********************************************************************/ + + static + int Vertical_Test_Pixel( RAS_ARGS TScan y, + int x ) + { + int c1 = x >> 3; + + return ( x >= 0 && x < ras.bit_width && + ras.bit_buffer[ras.trace_bit + c1] & (0x80 >> (x & 7)) ); + } + +/***********************************************************************/ +/* */ +/* Vertical_Set_Pixel */ +/* */ +/* */ +/* Sets a single pixel in a bitmap during the vertical sweep. */ +/* Used during drop-out control.. */ +/* */ +/* */ +/* y :: current scanline */ +/* x :: current x coordinate */ +/* color :: ignored by this function */ +/* */ +/***********************************************************************/ + + static + void Vertical_Set_Pixel( RAS_ARGS int y, + int x, + int color ) + { + (void)color; /* unused here */ + + if ( x >= 0 && x < ras.bit_width ) + { + int c1 = x >> 3; + + if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1; + if ( ras.gray_max_x < c1 ) ras.gray_max_x = c1; + + ras.bit_buffer[ras.trace_bit+c1] |= (char)(0x80 >> (x & 7)); + } + } + +/***********************************************************************/ +/* */ +/* Vertical_Sweep_Step */ +/* */ +/* */ +/* Called whenever the sweep jumps to anothr scanline. */ +/* Only updates the pointers in the vertical bitmap sweep */ +/* */ +/***********************************************************************/ + + static + void Vertical_Sweep_Step( RAS_ARG ) + { + ras.trace_bit += ras.trace_incr; + } + + +/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/******** *********/ +/******** Horizontal Bitmap Sweep Routines *********/ +/******** *********/ +/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ + +/***********************************************************************/ +/* */ +/* Horizontal_Sweep_Init */ +/* */ +/* */ +/* Initialise the horizontal bitmap sweep. Called by the generic */ +/* sweep/draw routine before its loop.. */ +/* */ +/* */ +/* min :: address of current minimum pixel column */ +/* max :: address of current maximum pixel column */ +/* */ +/***********************************************************************/ + + static void Horizontal_Sweep_Init( RAS_ARGS int* min, int* max ) + { + /* nothing, really */ + } + + +/***********************************************************************/ +/* */ +/* Horizontal_Sweep_Span */ +/* */ +/* */ +/* draws a single vertical bitmap span during the horizontal */ +/* bitmap sweep. Actually, this function is only used to */ +/* check for weird drop-out cases.. */ +/* */ +/* */ +/* y :: current pixel column */ +/* x1 :: top span edge */ +/* x2 :: bottom span edge */ +/* */ +/***********************************************************************/ + + static void Horizontal_Sweep_Span( RAS_ARGS TScan y, + TPos x1, + TPos x2 ) + { + TPos e1, e2; + PByte bits; + TByte f1; + + + /* During the horizontal sweep, we only take care of drop-outs */ + if ( x2-x1 < ras.precision ) + { + e1 = CEILING( x1 ); + e2 = FLOOR( x2 ); + + if ( e1 == e2 ) + { + bits = ras.bit_buffer + (y >> 3); + f1 = (TByte)(0x80 >> (y & 7)); + + e1 = TRUNC( e1 ); + + if ( e1 >= 0 && e1 < ras.target.rows ) + { + long pitch = ras.target.pitch; + + bits -= e1*pitch; + if (pitch > 0) + bits += (ras.target.rows-1)*pitch; + + bits[0] |= f1; + } + } + } + } + + +/***********************************************************************/ +/* */ +/* Horizontal_Test_Pixel */ +/* */ +/* */ +/* test a pixel 'light' during the horizontal bitmap sweep. Used */ +/* during drop-out control only.. */ +/* */ +/* */ +/* y :: current pixel column */ +/* x :: current row/scanline */ +/* */ +/***********************************************************************/ + + static + int Horizontal_Test_Pixel( RAS_ARGS int y, + int x ) + { + char* bits = (char*)ras.bit_buffer + (y >> 3); + int f1 = (TByte)(0x80 >> (y & 7)); + long pitch = ras.target.pitch; + + bits -= x*pitch; + if (pitch > 0) + bits += (ras.target.rows-1)*pitch; + + return ( x >= 0 && x < ras.target.rows && (bits[0] & f1) ); + } + +/***********************************************************************/ +/* */ +/* Horizontal_Set_Pixel */ +/* */ +/* */ +/* Sets a single pixel in a bitmap during the horizontal sweep. */ +/* Used during drop-out control.. */ +/* */ +/* */ +/* y :: current pixel column */ +/* x :: current row/scanline */ +/* color :: ignored by this function */ +/* */ +/***********************************************************************/ + + static + void Horizontal_Set_Pixel( RAS_ARGS int y, + int x, + int color ) + { + char* bits = (char*)ras.bit_buffer + (y >> 3); + int f1 = (TByte)(0x80 >> (y & 7)); + long pitch = ras.target.pitch; + + (void)color; /* unused here */ + + bits -= x*pitch; + if (pitch > 0) + bits += (ras.target.rows-1)*pitch; + + if ( x >= 0 && x < ras.target.rows ) + bits[0] |= f1; + } + +/***********************************************************************/ +/* */ +/* Horizontal_Sweep_Step */ +/* */ +/* */ +/* Called whenever the sweep jumps to another pixel column. */ +/* */ +/***********************************************************************/ + + static void Horizontal_Sweep_Step( RAS_ARG ) + { + /* Nothing, really */ + } + + +/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/******** *********/ +/******** Anti-Aliased Vertical Bitmap Sweep Routines *********/ +/******** *********/ +/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef FT_RASTER_OPTION_ANTI_ALIAS + +#ifdef FT_RASTER_ANTI_ALIAS_5 +/***********************************************************************/ +/* */ +/* 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_Gray5_Sweep_Init( RAS_ARGS int* min, int* max ) + { + long pitch; + + *min = *min & -2; + *max = ( *max+3 ) & -2; + + ras.trace_bit = 0; + + pitch = ras.target.pitch; + ras.trace_incr = -pitch; + ras.trace_pix = - (*min/2)*pitch; + if (pitch > 0) + ras.trace_pix += (ras.target.rows-1)*pitch; + + ras.gray_min_x = 32000; + ras.gray_max_x = -32000; + } + + + static void Vertical_Gray5_Sweep_Span( RAS_ARGS TScan y, + TPos x1, + TPos x2 ) + { + static + const unsigned int LMask[17] = +#ifdef FT_RASTER_LITTLE_ENDIAN + { 0xF0F0F0F0, 0xF0F0F070, 0xF0F0F030, 0xF0F0F010, + 0xF0F0F000, 0xF0F07000, 0xF0F03000, 0xF0F01000, + 0xF0F00000, 0xF0700000, 0xF0300000, 0xF0100000, + 0xF0000000, 0x70000000, 0x30000000, 0x10000000, + 0x00000000 }; +#else + { 0xF0F0F0F0, 0x70F0F0F0, 0x30F0F0F0, 0x10F0F0F0, + 0x00F0F0F0, 0x0070F0F0, 0x0030F0F0, 0x0010F0F0, + 0x0000F0F0, 0x000070F0, 0x000030F0, 0x000010F0, + 0x000000F0, 0x00000070, 0x00000030, 0x00000010, + 0x00000000 }; +#endif + + TPos e1, e2; + int c1, c2; + TByte* target; + + unsigned int f1, f2; + unsigned int shift, fill; + + /* Drop-out control */ + + e1 = TRUNC( CEILING( x1 ) ); + if ( x2-x1 <= ras.precision+ras.precision_jitter ) + e2 = e1; + else + e2 = FLOOR ( x2 ); + + e2 = TRUNC( e2 ); + + if ( e1 <= e2 && e2 >= 0 && e1 < ras.bit_width ) + { + if ( e1 < 0 ) e1 = 0; + if ( e2 >= ras.bit_width ) e2 = ras.bit_width-1; + + shift = (y & 1)*4; + + c1 = e1 >> 4; + c2 = e2 >> 4; + + fill = LMask[0]; + f1 = LMask[ e1 & 15 ]; + f2 = fill ^ LMask[ 1+(e2 & 15) ]; + + f1 >>= shift; + f2 >>= shift; + fill >>= shift; + + if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1; + if ( ras.gray_max_x < c2 ) ras.gray_max_x = c2; + + target = ras.bit_buffer + c1*4; + c2 -= c1; + + if (c2 > 0) + { + int* slice = (int*)target; + + slice[0] |= f1; + c2--; + while (c2 > 0) + { + * ++slice |= fill; + c2--; + } + slice[1] |= f2; + } + else + ((int*)target)[0] |= ( f1 & f2 ); + } + } + + + static + int Vertical_Gray5_Test_Pixel( RAS_ARGS int y, + int x ) + { + int c1 = x >> 2; + int f1 = x & 3; + int mask = (0x80 >> f1) >> ((y & 1)*4); + + return ( x >= 0 && + x < ras.bit_width && + ras.bit_buffer[c1] & mask ); + } + + + static + void Vertical_Gray5_Set_Pixel( RAS_ARGS int y, + int x, + int color ) + { + (void)color; /* unused here */ + + if ( x >= 0 && x < ras.bit_width ) + { + int c1 = x >> 2; + int f1 = x & 3; + int mask = (0x80 >> f1) >> ((y & 1)*4); + + if ( ras.gray_min_x > c1/4 ) ras.gray_min_x = c1/4; + if ( ras.gray_max_x < c1/4 ) ras.gray_max_x = c1/4; + + ras.bit_buffer[c1] |= mask; + } + } + + + static void Vertical_Gray5_Sweep_Step( RAS_ARG ) + { + int c1; + PByte pix, bit; + int* count = ras.count_table; + long* grays; + + + ras.trace_bit++; + + if ( ras.trace_bit > 1 ) + { + pix = ras.pix_buffer + ras.trace_pix + ras.gray_min_x*8; + grays = ras.grays; + + if ( ras.gray_max_x >= 0 ) + { + if ( ras.gray_max_x*8 >= ras.target.width ) + ras.gray_max_x = (ras.target.width+7)/8-1; + + if ( ras.gray_min_x < 0 ) + ras.gray_min_x = 0; + + bit = ras.bit_buffer + ras.gray_min_x*4; + c1 = (ras.gray_max_x - ras.gray_min_x + 1); + + while ( c1 >= 0 ) + { + if ( *(short*)bit ) + { +#if defined( FT_RASTER_LITTLE_ENDIAN ) + /* little endian storage */ + *((long*)pix) = (count[bit[1]] << 16) | count[bit[0]]; +#elif defined( FT_RASTER_BIG_ENDIAN ) + /* big-endian storage */ + *((long*)pix) = (count[bit[0]] << 16) | count[bit[1]]; +#else + /* endian-independent storage */ + int c; + c = count[bit[1]]; + pix[4] = (TByte)(c >> 8); + pix[5] = (TByte)(c & 0xFF); + c = count[bit[0]]; + pix[6] = (TByte)(c >> 8); + pix[7] = (TByte)(c & 0xFF); +#endif + *(short*)bit = 0; + } + + bit += 2; + + if ( *(short*)bit ) + { +#if defined( FT_RASTER_LITTLE_ENDIAN ) + /* little endian storage */ + *((long*)pix + 1)= (count[bit[1]] << 16) | count[bit[0]]; +#elif defined( FT_RASTER_BIG_ENDIAN ) + /* big-endian storage */ + *((long*)pix + 1) = (count[bit[0]] << 16) | count[bit[1]]; +#else + /* endian-independent storage */ + int c; + c = count[bit[1]]; + pix[0] = (TByte)(c >> 8); + pix[1] = (TByte)(c & 0xFF); + c = count[bit[0]]; + pix[2] = (TByte)(c >> 8); + pix[3] = (TByte)(c & 0xFF); +#endif + *(short*)bit = 0; + } + pix += 8; + bit += 2; + c1 --; + } + } + + ras.trace_bit = 0; + ras.trace_pix += ras.trace_incr; + + ras.gray_min_x = 32000; + ras.gray_max_x = -32000; + } + } + + + + static void Horizontal_Gray5_Sweep_Span( RAS_ARGS TScan y, + TPos x1, + TPos x2 ) + { + /* nothing, really */ + (void)y; + (void)x1; + (void)x2; + } + + + static + int Horizontal_Gray5_Test_Pixel( RAS_ARGS TScan y, + int x ) + { + /* don't do anything here */ + (void)x; + (void)y; + + return 0; + } + + + static + void Horizontal_Gray5_Set_Pixel( RAS_ARGS TScan y, + int x, + int color ) + { + char* pixel; + + x = x/2; + + if (x < ras.target.rows) + { + long pitch; + + pixel = (char*)ras.pix_buffer + (y/2); + pitch = ras.target.pitch; + pixel -= x*pitch; + if (pitch > 0) + pixel += (ras.target.rows-1)*pitch; + + if (pixel[0] == ras.grays[0]) + pixel[0] = (char)ras.grays[1+color]; + } + } +#endif /* FT_RASTER_ANTI_ALIAS_5 */ + +#ifdef FT_RASTER_ANTI_ALIAS_17 +/***********************************************************************/ +/* */ +/* 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_Gray17_Sweep_Init( RAS_ARGS int* min, int* max ) + { + long pitch = ras.target.pitch; + + *min = *min & -4; + *max = ( *max+7 ) & -4; + + ras.trace_bit = 0; + ras.trace_incr = -pitch; + ras.trace_bit = - (*min/4)*pitch; + if (pitch > 0) + ras.trace_bit += (ras.target.rows-1)*pitch; + + ras.gray_min_x = 32000; + ras.gray_max_x = -32000; + } + + + static void Vertical_Gray17_Sweep_Span( RAS_ARGS TScan y, + TPos x1, + TPos x2 ) + { + static + const unsigned int LMask[9] = +#ifdef FT_RASTER_LITTLE_ENDIAN + { 0xF000F000, 0xF0007000, 0xF0003000, 0xF0001000, + 0xF0000000, 0x70000000, 0x30000000, 0x10000000, + 0x00000000 }; +#else + { 0xF000F000, 0x7000F000, 0x3000F000, 0x1000F000, + 0x0000F000, 0x00007000, 0x00003000, 0x00001000, + 0x00000000 }; +#endif + + TPos e1, e2; + int c1, c2; + TByte* target; + + unsigned int f1, f2; + unsigned int shift, fill; + + /* Drop-out control */ + + e1 = TRUNC( CEILING( x1 ) ); + if ( x2-x1 <= ras.precision+ras.precision_jitter ) + e2 = e1; + else + e2 = FLOOR ( x2 ); + e2 = TRUNC( e2 ); + + if ( e1 <= e2 && e2 >= 0 && e1 < ras.bit_width ) + { + if ( e1 < 0 ) e1 = 0; + if ( e2 >= ras.bit_width ) e2 = ras.bit_width-1; + + shift = (y & 3)*4; + + c1 = e1 >> 3; + c2 = e2 >> 3; + + fill = LMask[0]; + f1 = LMask[ e1 & 7 ]; + f2 = fill ^ LMask[ 1+(e2 & 7) ]; + + f1 >>= shift; + f2 >>= shift; + fill >>= shift; + + if ( ras.gray_min_x > c1/2 ) ras.gray_min_x = c1/2; + if ( ras.gray_max_x < c2/2 ) ras.gray_max_x = c2/2; + + target = ras.bit_buffer + c1*4; + c2 -= c1; + + if (c2 > 0) + { + int* slice = (int*)target; + + slice[0] |= f1; + c2--; + while (c2 > 0) + { + * ++slice |= fill; + c2--; + } + slice[1] |= f2; + } + else + ((int*)target)[0] |= ( f1 & f2 ); + } + } + + + static + int Vertical_Gray17_Test_Pixel( RAS_ARGS int y, + int x ) + { + int c1 = x >> 2; + int f1 = x & 3; + int mask = (0x8000 >> f1) >> ((y & 3)*4); + + return ( x >= 0 && + x < ras.bit_width && + ((short*)ras.bit_buffer)[c1] & mask ); + } + + + static + void Vertical_Gray17_Set_Pixel( RAS_ARGS int y, + int x, + int color ) + { + (void)color; /* unused here */ + + if ( x >= 0 && x < ras.bit_width ) + { + int c1 = x >> 2; + int f1 = x & 3; + int mask = (0x8000 >> f1) >> ((y & 3)*4); + + if ( ras.gray_min_x > c1/4 ) ras.gray_min_x = c1/4; + if ( ras.gray_max_x < c1/4 ) ras.gray_max_x = c1/4; + + ((short*)ras.bit_buffer)[c1] |= mask; + } + } + + + static void Vertical_Gray17_Sweep_Step( RAS_ARG ) + { + int c1; + PByte pix, bit; + long* grays; + + + ras.trace_bit++; + + if ( ras.trace_bit > 3 ) + { + pix = ras.pix_buffer + ras.trace_pix + ras.gray_min_x*4; + grays = ras.grays; + + if ( ras.gray_max_x >= 0 ) + { + if ( ras.gray_max_x >= ras.target.width/4 ) + ras.gray_max_x = (ras.target.width+3)/4-1; + + if ( ras.gray_min_x < 0 ) + ras.gray_min_x = 0; + + bit = ras.bit_buffer + ras.gray_min_x*8; + c1 = (ras.gray_max_x - ras.gray_min_x + 1); + + while ( c1 >= 0 ) + { + if ( *(long*)bit || *(long*)(bit+4) ) + { + int* table = ras.count_table; + +#if defined( FT_RASTER_LITTLE_ENDIAN ) + /* little-endian specific storage */ + *(long*)pix = grays[ table[ bit[0] ] + + table[ bit[1] ] ] | + + ( grays[ table[ bit[2] ] + + table[ bit[3] ] ] << 8 ) | + + ( grays[ table[ bit[4] ] + + table[ bit[5] ] ] << 16 ) | + + ( grays[ table[ bit[6] ] + + table[ bit[7] ] ] << 24 ); + +#elif defined( FT_RASTER_BIG_ENDIAN ) + /* big-endian specific storage */ + *(long*)pix = ( grays[ table[ bit[0] ] + + table[ bit[1] ] ] << 24 | + + ( grays[ table[ bit[2] ] + + table[ bit[3] ] ] << 16 ) | + + ( grays[ table[ bit[4] ] + + table[ bit[5] ] ] << 8 ) | + + ( grays[ table[ bit[6] ] + + table[ bit[7] ] ] ); +#else + /* endianess-independent storage */ + pix[0] = grays[ table[bit[0]] + table[bit[1]] ]; + pix[1] = grays[ table[bit[2]] + table[bit[3]] ]; + pix[2] = grays[ table[bit[4]] + table[bit[5]] ]; + pix[3] = grays[ table[bit[6]] + table[bit[7]] ]; +#endif + *(long*)( bit ) = 0; + *(long*)(bit+4) = 0; + } + pix += 4; + bit += 8; + c1 --; + } + } + ras.trace_bit = 0; + ras.trace_pix += ras.trace_incr; + + ras.gray_min_x = 32000; + ras.gray_max_x = -32000; + } + } + + + + static void Horizontal_Gray17_Sweep_Span( RAS_ARGS TScan y, + TPos x1, + TPos x2 ) + { + /* nothing, really */ + (void)y; + (void)x1; + (void)x2; + } + + + static + int Horizontal_Gray17_Test_Pixel( RAS_ARGS TScan y, + int x ) + { + /* don't do anything here */ + (void)x; + (void)y; + + return 0; + } + + + static + void Horizontal_Gray17_Set_Pixel( RAS_ARGS TScan y, + int x, + int color ) + { + char* pixel; + + x = x/4; + + if (x < ras.target.rows) + { + long pitch = ras.target.pitch; + + pixel = (char*)ras.pix_buffer + (y/4); + pixel -= x*pitch; + if (pitch > 0) + pixel += (ras.target.rows-1)*pitch; + + if (pixel[0] == ras.grays[0]) + pixel[0] = ras.grays[1+color]; + } + } +#endif /* FT_RASTER_ANTI_ALIAS_17 */ + +#endif /* FT_RASTER_OPTION_ANTI_ALIAS */ + +/********************************************************************/ +/* */ +/* Generic Sweep Drawing routine */ +/* */ +/********************************************************************/ + + static TBool Draw_Sweep( RAS_ARG ) + { + TScan y, y_change, y_height; + + PProfile P, Q, P_Left, P_Right; + + TScan min_Y, max_Y, top, bottom, dropouts; + + TPos x1, x2, e1, e2; + + TProfileList wait; + TProfileList draw; + + /* Init empty linked lists */ + + Init_Linked( &wait ); + Init_Linked( &draw ); + + /* first, compute min and max Y */ + + P = ras.start_prof; + max_Y = TRUNC( ras.minY ); + min_Y = TRUNC( ras.maxY ); + + while ( P ) + { + Q = P->link; + + bottom = P->start; + top = P->start + P->height-1; + + if ( min_Y > bottom ) min_Y = bottom; + if ( max_Y < top ) max_Y = top; + + P->X = 0; + InsNew( &wait, P ); + + P = Q; + } + + /* Check the Y-turns */ + if ( ras.n_turns == 0 ) + { + ras.error = ErrRaster_Invalid_Outline; + 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 = wait; + + while ( P ) + { + P->countL = P->start - min_Y; + P = P->link; + } + + /* Let's go */ + + y = min_Y; + y_height = 0; + + if ( ras.n_turns > 0 && + ras.pool_size[-ras.n_turns] == min_Y ) + ras.n_turns--; + + while (ras.n_turns > 0) + { + PProfile prof = wait; + + /* look in the wait list for new activations */ + while (prof) + { + PProfile next = prof->link; + + prof->countL -= y_height; + if ( prof->countL == 0 ) + { + DelOld( &wait, prof ); + InsNew( &draw, prof ); + } + prof = next; + } + + /* Sort the drawing lists */ + Sort( &draw ); + + y_change = ras.pool_size[-ras.n_turns--]; + y_height = y_change - y; + + while ( y < y_change ) + { + int window; + PProfile left; + + /* Let's trace */ + + dropouts = 0; + + if (!draw) + goto Next_Line; + + left = draw; + window = left->flow; + prof = left->link; + + while (prof) + { + PProfile next = prof->link; + + window += prof->flow; + + if ( window == 0 ) + { + x1 = left->X; + x2 = prof->X; +#if 1 + if ( x1 > x2 ) + { + TPos xs = x1; + x1 = x2; + x2 = xs; + } +#endif + if ( x2-x1 <= ras.precision && ras.dropout_mode ) + { +#if 1 + e1 = CEILING( x1 ); + e2 = FLOOR( x2 ); +#else + e1 = FLOOR(x1); + e2 = CEILING(x2); +#endif + if ( e1 > e2 || e2 == e1+ ras.precision ) + { + /* a drop out was detected */ + + left->X = x1; + prof->X = x2; + + /* mark profiles for drop-out processing */ + left->countL = 1; + prof->countL = 2; + dropouts++; + goto Skip_To_Next; + } + } + + ras.Proc_Sweep_Span( RAS_VARS y, x1, x2 ); + + Skip_To_Next: + left = next; + } + prof = next; + } + + /* now perform the dropouts _after_ the span drawing */ + /* drop-outs processing has been moved out of the loop */ + /* for performance tuning */ + if (dropouts > 0) + goto Scan_DropOuts; + + Next_Line: + + ras.Proc_Sweep_Step( RAS_VAR ); + + y++; + + if ( y < y_change ) + Sort( &draw ); + } + + /* Now finalize the profiles that needs it */ + + { + PProfile prof, next; + prof = draw; + while (prof) + { + next = prof->link; + if (prof->height == 0) + DelOld( &draw, prof ); + prof = next; + } + } + } + + /* 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; + while (dropouts > 0) + { + TPos e1, e2; + PProfile left, right; + + while (P_Left->countL != 1) P_Left = P_Left->link; + P_Right = P_Left->link; + while (P_Right->countL != 2) P_Right = P_Right->link; + + P_Left->countL = 0; + P_Right->countL = 0; + + /* Now perform the dropout control */ + x1 = P_Left->X; + x2 = P_Right->X; + + left = ( ras.flipped ? P_Right : P_Left ); + right = ( ras.flipped ? P_Left : P_Right ); + + e1 = CEILING( x1 ); + e2 = FLOOR ( x2 ); + + if ( e1 > e2 ) + { + if ( e1 == e2 + ras.precision ) + { + switch ( ras.dropout_mode ) + { + case 1: + e1 = e2; + break; + + case 4: + e1 = CEILING( (x1+x2+1) / 2 ); + break; + + case 2: + case 5: + /* Drop-out Control Rule #4 */ + + /* The spec is not very clear regarding rule #4. It */ + /* presents a method that is way too costly to implement */ + /* while the general idea seems to get rid of 'stubs'. */ + /* */ + /* Here, we only get rid of stubs recognized when: */ + /* */ + /* upper stub: */ + /* */ + /* - P_Left and P_Right are in the same contour */ + /* - P_Right is the successor of P_Left in that contour */ + /* - y is the top of P_Left and P_Right */ + /* */ + /* lower stub: */ + /* */ + /* - P_Left and P_Right are in the same contour */ + /* - P_Left is the successor of P_Right in that contour */ + /* - y is the bottom of P_Left */ + /* */ + + /* upper stub test */ + if ( ( left->next == right && left->height <= 0 ) || + + /* lower stub test */ + ( right->next == left && left->start == y ) || + + /* check that the rightmost pixel isn't set */ + ras.Proc_Test_Pixel( RAS_VARS y, TRUNC(e1)) ) + goto Next_Dropout; + + if ( ras.dropout_mode == 2 ) + e1 = e2; + else + e1 = CEILING( (x1+x2+1)/2 ); + + break; + + default: + goto Next_Dropout; /* Unsupported mode */ + } + } + else + goto Next_Dropout; + } + + ras.Proc_Set_Pixel( RAS_VARS y, + TRUNC(e1), + (x2-x1 >= ras.precision_half) & 1 ); + Next_Dropout: + + dropouts--; + } + goto Next_Line; + } + + + +/****************************************************************************/ +/* */ +/* Function: Render_Single_Pass */ +/* */ +/* Description: Performs one sweep with sub-banding. */ +/* */ +/* Input: _XCoord, _YCoord : x and y coordinates arrays */ +/* */ +/* Returns: SUCCESS on success */ +/* FAILURE if any error was encountered during render. */ +/* */ +/****************************************************************************/ + + static + int Render_Single_Pass( RAS_ARGS int flipped ) + { + int i, j, k; + + ras.flipped = flipped; + + while ( ras.band_top >= 0 ) + { + ras.maxY = ((long)ras.band_stack[ras.band_top].y_max << + (ras.scale_shift+6))-1; + + ras.minY = (long)ras.band_stack[ras.band_top].y_min << + (ras.scale_shift+6); + + ras.cursor = ras.pool; + ras.error = 0; + + if ( Convert_Glyph( RAS_VARS ras.outline ) ) + { + if ( ras.error != ErrRaster_Overflow ) return FAILURE; + ras.error = ErrRaster_Ok; + + /* 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 = ( j-i ) >> 1; + + if ( ras.band_top >= 7 || k == 0 ) + { + ras.band_top = 0; + ras.error = ErrRaster_Invalid_Outline; + return ras.error; + } + + ras.band_stack[ras.band_top+1].y_min = i + k; + ras.band_stack[ras.band_top+1].y_max = j; + + ras.band_stack[ras.band_top].y_max = i + k; + + ras.band_top++; + } + else + { + if ( ras.start_prof ) + if ( Draw_Sweep( RAS_VAR ) ) return ras.error; + ras.band_top--; + } + } + + return 0; /* success */ + } + + +#if 0 + void Compute_BBox( FT_Outline* outline, + FT_BBox* bbox ) + { + int n; + FT_Vector* vec; + + vec = outline->points; + bbox->xMin = bbox->xMax = vec->x; + bbox->yMin = bbox->yMax = vec->y; + + n = outline->n_points-1; + while ( n > 0 ) + { + FT_Pos x, y; + + vec++; + + x = vec->x; + if ( bbox->xMin > x ) bbox->xMin = x; + if ( bbox->xMax < x ) bbox->xMax = x; + + y = vec->y; + if ( bbox->yMin > y ) bbox->yMin = y; + if ( bbox->yMax < y ) bbox->yMax = y; + + n--; + } + } +#endif + + static + int Raster_Render1( FT_Raster raster ) + { + int error; + long byte_len; + + byte_len = ras.target.pitch; + if (byte_len < 0) byte_len = -byte_len; + + if ( ras.target.width > byte_len*8 ) + return ErrRaster_Invalid_Map; + + ras.scale_shift = (ras.precision_bits-6); + + /* Vertical Sweep */ + ras.band_top = 0; + ras.band_stack[0].y_min = 0; + ras.band_stack[0].y_max = ras.target.rows; + + ras.Proc_Sweep_Init = Vertical_Sweep_Init; + ras.Proc_Sweep_Span = Vertical_Sweep_Span; + ras.Proc_Sweep_Step = Vertical_Sweep_Step; + ras.Proc_Test_Pixel = Vertical_Test_Pixel; + ras.Proc_Set_Pixel = Vertical_Set_Pixel; + + ras.bit_width = ras.target.width; + ras.bit_buffer = (unsigned char*)ras.target.buffer; + + if ( (error = Render_Single_Pass( RAS_VARS 0 )) != 0 ) + return error; + + /* Horizontal Sweep */ + + if ( ras.second_pass && ras.dropout_mode != 0 ) + { + ras.Proc_Sweep_Init = Horizontal_Sweep_Init; + ras.Proc_Sweep_Span = Horizontal_Sweep_Span; + ras.Proc_Sweep_Step = Horizontal_Sweep_Step; + ras.Proc_Test_Pixel = Horizontal_Test_Pixel; + ras.Proc_Set_Pixel = Horizontal_Set_Pixel; + + ras.band_top = 0; + ras.band_stack[0].y_min = 0; + ras.band_stack[0].y_max = ras.target.width - 1; + + if ( (error = Render_Single_Pass( RAS_VARS 1 )) != 0 ) + return error; + } + + return ErrRaster_Ok; + } + + + +#ifdef FT_RASTER_OPTION_ANTI_ALIAS + static + int Raster_Render8( FT_Raster raster ) + { + int error; + long byte_len; + + byte_len = ras.target.pitch; + if (byte_len < 0) byte_len = -byte_len; + + if ( ras.target.width > byte_len ) + return ErrRaster_Invalid_Map; + + /* Vertical Sweep */ + ras.band_top = 0; + ras.band_stack[0].y_min = 0; + ras.band_stack[0].y_max = ras.target.rows; + +#if !defined(FT_RASTER_ANTI_ALIAS_5) && !defined(FT_RASTER_ANTI_ALIAS_17) + return ErrRaster_Unimplemented; +#endif + +#ifdef FT_RASTER_ANTI_ALIAS_5 + if ( ras.grays_count == 5 ) + { + ras.scale_shift = (ras.precision_bits-5); + ras.bit_width = ras.gray_width/2; + if ( ras.bit_width > (ras.target.width+3)/4 ) + ras.bit_width = (ras.target.width+3)/4; + + ras.bit_width = ras.bit_width * 8; + ras.bit_buffer = (unsigned char*)ras.gray_lines; + ras.pix_buffer = (unsigned char*)ras.target.buffer; + + ras.Proc_Sweep_Init = Vertical_Gray5_Sweep_Init; + ras.Proc_Sweep_Span = Vertical_Gray5_Sweep_Span; + ras.Proc_Sweep_Step = Vertical_Gray5_Sweep_Step; + ras.Proc_Test_Pixel = Vertical_Gray5_Test_Pixel; + ras.Proc_Set_Pixel = Vertical_Gray5_Set_Pixel; + } +#endif + +#ifdef FT_RASTER_ANTI_ALIAS_17 + if ( ras.grays_count == 17 ) + { + ras.scale_shift = (ras.precision_bits-4); + ras.bit_width = ras.gray_width/4; + if ( ras.bit_width > (ras.target.width+1)/2 ) + ras.bit_width = (ras.target.width+1)/2; + + ras.bit_width = ras.bit_width * 8; + ras.bit_buffer = (unsigned char*)ras.gray_lines; + ras.pix_buffer = (unsigned char*)ras.target.buffer; + + ras.Proc_Sweep_Init = Vertical_Gray17_Sweep_Init; + ras.Proc_Sweep_Span = Vertical_Gray17_Sweep_Span; + ras.Proc_Sweep_Step = Vertical_Gray17_Sweep_Step; + ras.Proc_Test_Pixel = Vertical_Gray17_Test_Pixel; + ras.Proc_Set_Pixel = Vertical_Gray17_Set_Pixel; + } +#endif + + error = Render_Single_Pass( RAS_VARS 0 ); + if (error) + return error; + + /* Horizontal Sweep */ + + if ( ras.second_pass && ras.dropout_mode != 0 ) + { +#ifdef FT_RASTER_ANTI_ALIAS_5 + if ( ras.grays_count == 5 ) + { + ras.Proc_Sweep_Init = Horizontal_Sweep_Init; + ras.Proc_Sweep_Span = Horizontal_Gray5_Sweep_Span; + ras.Proc_Sweep_Step = Horizontal_Sweep_Step; + ras.Proc_Test_Pixel = Horizontal_Gray5_Test_Pixel; + ras.Proc_Set_Pixel = Horizontal_Gray5_Set_Pixel; + } +#endif + +#ifdef FT_RASTER_ANTI_ALIAS_17 + if ( ras.grays_count == 17 ) + { + ras.Proc_Sweep_Init = Horizontal_Sweep_Init; + ras.Proc_Sweep_Span = Horizontal_Gray17_Sweep_Span; + ras.Proc_Sweep_Step = Horizontal_Sweep_Step; + ras.Proc_Test_Pixel = Horizontal_Gray17_Test_Pixel; + ras.Proc_Set_Pixel = Horizontal_Gray17_Set_Pixel; + } +#endif + ras.band_top = 0; + ras.band_stack[0].y_min = 0; + ras.band_stack[0].y_max = ras.target.width-1; + + error = Render_Single_Pass( RAS_VARS 1 ); + if (error) + return error; + } + + return ErrRaster_Ok; + } + +#else /* ANTI_ALIAS */ + + static + int Raster_Render8( FT_Raster raster ) + { + return ErrRaster_Unimplemented; + } + +#endif /* FT_RASTER_OPTION_ANTI_ALIAS */ + + + +#ifdef FT_RASTER_OPTION_ANTI_ALIAS +/****************************************************************************/ +/* */ +/* Reset_Palette_5 */ +/* */ +/* Resets lookup table when the 5-gray-levels palette changes */ +/* */ +/****************************************************************************/ + +static +void Reset_Palette_5( RAS_ARG ) +{ + int i; + + for ( i = 0; i < 256; i++ ) + { + int cnt1, cnt2; + + cnt1 = ((i & 128) >> 7) + + ((i & 64) >> 6) + + ((i & 8) >> 3) + + ((i & 4) >> 2); + + cnt2 = ((i & 32) >> 5) + + ((i & 16) >> 4) + + ((i & 2) >> 1) + + (i & 1); + + /* */ + /* Note that when the endianess isn't specified through one of the */ + /* configuration, we use the big-endian storage in 'count_table' */ + /* */ + +#if defined( FT_RASTER_LITTLE_ENDIAN ) + ras.count_table[i] = (ras.grays[cnt2] << 8) | ras.grays[cnt1]; +#else + ras.count_table[i] = (ras.grays[cnt1] << 8) | ras.grays[cnt2]; +#endif + } +} + +/****************************************************************************/ +/* */ +/* Reset_Palette_17 */ +/* */ +/* Resets lookup table when 17-gray-levels palette changes */ +/* */ +/****************************************************************************/ + +#ifdef FT_RASTER_ANTI_ALIAS_17 +static +void Reset_Palette_17( RAS_ARG ) +{ + int i; + + for ( i = 0; i < 256; i++ ) + ras.count_table[i] = + ((i & 128) >> 7) + + ((i & 64) >> 6) + + ((i & 8) >> 3) + + ((i & 4) >> 2) + + ((i & 32) >> 5) + + ((i & 16) >> 4) + + ((i & 2) >> 1) + + (i & 1); +} +#endif /* ANTI_ALIAS_17 */ + +#endif /* TT_RASTER_OPTION_ANTI_ALIAS */ + + + + /**********************************************************************/ + /* */ + /* FT_Raster_SetPalette */ + /* */ + /* */ + /* Set the pixmap rendering palette. anti-aliasing modes are */ + /* implemented/possible, they differ from the number of */ + /* entries in the palette. */ + /* */ + /* */ + /* count :: the number of palette entries. Valid values are */ + /* 2, 5 and 17, which are the number of intermediate */ + /* gray levels supported */ + /* */ + /* palette :: an array of 'count' chars giving the 8-bit palette */ + /* of intermediate "gray" levels for anti-aliased */ + /* rendering. */ + /* */ + /* In all modes, palette[0] corresponds to the background, */ + /* while palette[count-1] to the foreground. Hence, a count */ + /* of 2 corresponds to no anti-aliasing; a count of 5 uses */ + /* 3 intermediate levels between the background and */ + /* foreground, while a count of 17 uses 15 of them.. */ + /* */ + /* */ + /* An error code, used as a FT_Error by the FreeType library. */ + /* */ + /* */ + /* By default, a new object uses mode 5, with a palette of */ + /* 0,1,2,3 and 4. You don't need to set the palette if you */ + /* don't need to render pixmaps.. */ + /* */ + /**********************************************************************/ + + EXPORT_FUNC + int FT_Raster_SetPalette( FT_Raster raster, + int count, + const char* palette ) + { + switch (count) + { +#ifdef FT_RASTER_OPTION_ANTI_ALIAS + + /******************************/ + /* The case of 17 gray levels */ + /******************************/ + + case 17: +#ifdef FT_RASTER_ANTI_ALIAS_17 + { + int n; + + raster->grays_count = count; + for ( n = 0; n < count; n++ ) + raster->grays[n] = (unsigned char)palette[n]; + Reset_Palette_17( RAS_VAR ); + break; + } +#else + return ErrRaster_Unimplemented; +#endif + + /*****************************/ + /* The case of 5 gray levels */ + /*****************************/ + + case 5: +#ifdef FT_RASTER_ANTI_ALIAS_5 + { + int n; + + raster->grays_count = count; + for ( n = 0; n < count; n++ ) + raster->grays[n] = (unsigned char)palette[n]; + Reset_Palette_5( RAS_VAR ); + break; + } +#else + return ErrRaster_Unimplemented; +#endif + +#endif /* FT_RASTER_OPTION_ANTI_ALIAS */ + default: + return ErrRaster_Bad_Palette_Count; + } + + return ErrRaster_Ok; + } + + + /**** RASTER OBJECT CREATION : in standalone mode, we simply use *****/ + /**** a static object .. *****/ +#ifdef _STANDALONE_ + + static + int ft_black2_new( void* memory, FT_Raster *araster ) + { + static FT_RasterRec_ the_raster; + *araster = &the_raster; + memset( &the_raster, sizeof(the_raster), 0 ); + return 0; + } + + static + void ft_black2_done( FT_Raster raster ) + { + /* nothing */ + raster->init = 0; + } + +#else + +#include + + static + int ft_black2_new( FT_Memory memory, FT_Raster *araster ) + { + FT_Error error; + FT_Raster raster; + + *araster = 0; + if ( !ALLOC( raster, sizeof(*raster) )) + { + raster->memory = memory; + *araster = raster; + } + + return error; + } + + static + void ft_black2_done( FT_Raster raster ) + { + FT_Memory memory = (FT_Memory)raster->memory; + FREE( raster ); + } + +#endif + + + static void ft_black2_reset( FT_Raster raster, + const char* pool_base, + long pool_size ) + { + static const char default_palette[5] = { 0, 1, 2, 3, 4 }; + + /* check the object address */ + if ( !raster ) + return; + + /* check the render pool - we won't go under 4 Kb */ + if ( !pool_base || pool_size < 4096 ) + return; + + /* save the pool */ + raster->pool = (PPos)pool_base; + raster->pool_size = (PPos)(pool_base + (pool_size & -8)); + +#ifdef FT_RASTER_OPTION_ANTI_ALIAS + raster->gray_width = ANTI_ALIAS_BUFFER_SIZE/2; + /* clear anti-alias intermediate lines */ + { + char* p = raster->gray_lines; + int size = ANTI_ALIAS_BUFFER_SIZE; + do + { + *p++ = 0; + size--; + + } while (size > 0); + } +#endif + + /* set the default palette : 5 levels = 0, 1, 2, 3 and 4 */ + FT_Raster_SetPalette( raster, 5, default_palette ); + } + + + static + int ft_black2_render( FT_Raster raster, + FT_Raster_Params* params ) + { + FT_Outline* outline = (FT_Outline*)params->source; + FT_Bitmap* target_map = params->target; + + if ( !raster || !raster->pool || !raster->pool_size ) + return ErrRaster_Uninitialised_Object; + + if ( !outline || !outline->contours || !outline->points ) + return ErrRaster_Invalid_Outline; + + /* return immediately if the outline is empty */ + if ( outline->n_points == 0 || outline->n_contours <= 0 ) + return ErrRaster_Invalid_Outline; + + if ( outline->n_points != outline->contours[outline->n_contours - 1] + 1 ) + return ErrRaster_Invalid_Outline; + + if ( !target_map || !target_map->buffer ) + return ErrRaster_Invalid_Map; + + /* this version of the raster does not support direct rendering, sorry */ + if ( params->flags & ft_raster_flag_direct ) + return ErrRaster_Unimplemented; + + ras.outline = outline; + ras.target = *target_map; + + ras.dropout_mode = 2; + ras.second_pass = !(outline->flags & ft_outline_single_pass); + SET_High_Precision( (outline->flags & ft_outline_high_precision) ); + + return ( params->flags & ft_raster_flag_aa + ? Raster_Render8(raster) + : Raster_Render1(raster) ); + } + + + FT_Raster_Funcs ft_black2_raster = + { + ft_glyph_format_outline, + (FT_Raster_New_Func) ft_black2_new, + (FT_Raster_Reset_Func) ft_black2_reset, + (FT_Raster_Set_Mode_Func) 0, + (FT_Raster_Render_Func) ft_black2_render, + (FT_Raster_Done_Func) ft_black2_done + }; + + diff --git a/demos/src/ftrast2.h b/demos/src/ftrast2.h new file mode 100644 index 000000000..8cd9adb45 --- /dev/null +++ b/demos/src/ftrast2.h @@ -0,0 +1,42 @@ +/******************************************************************* + * + * ftraster.h v 2.0 + * + * The FreeType glyph scan-line converter (interface) + * + * Copyright 1996-2000 by + * David Turner, Robert Wilhelm, and Werner Lemberg + * + * This file is part of the FreeType project, and may only be used + * modified and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + * + ******************************************************************/ + +#ifndef FTRAST2_H +#define FTRAST2_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef EXPORT_DEF +#define EXPORT_DEF /* nothing */ +#endif + + EXPORT_DEF + FT_Raster_Funcs ft_black2_raster; + +#ifdef __cplusplus +} +#endif + +#endif /* FTRAST2_H */ + + +/* END */ diff --git a/demos/src/ftview.c b/demos/src/ftview.c index b0c092192..e0367b466 100644 --- a/demos/src/ftview.c +++ b/demos/src/ftview.c @@ -28,7 +28,7 @@ #include "grfont.h" #include "ftgrays.h" -#include "ftrast.h" +#include "ftrast2.h" #define DIM_X 500 #define DIM_Y 400 @@ -425,7 +425,7 @@ $\243^\250*\265\371%!\247:/;.,?<>"; error = 1; if ( !antialias) - error = FT_Set_Raster( library, &ft_black_raster ); + error = FT_Set_Raster( library, &ft_black2_raster ); else if ( use_grays && antialias ) error = FT_Set_Raster( library, &ft_grays_raster );