diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /modules/freetype2/src/raster/ftraster.c | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules/freetype2/src/raster/ftraster.c')
-rw-r--r-- | modules/freetype2/src/raster/ftraster.c | 3292 |
1 files changed, 3292 insertions, 0 deletions
diff --git a/modules/freetype2/src/raster/ftraster.c b/modules/freetype2/src/raster/ftraster.c new file mode 100644 index 0000000000..67cbfd5d9b --- /dev/null +++ b/modules/freetype2/src/raster/ftraster.c @@ -0,0 +1,3292 @@ +/**************************************************************************** + * + * ftraster.c + * + * The FreeType glyph rasterizer (body). + * + * Copyright (C) 1996-2023 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 -DSTANDALONE_ 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_ + + /* The size in bytes of the render pool used by the scan-line converter */ + /* to do all of its work. */ +#define FT_RENDER_POOL_SIZE 16384L + +#define FT_CONFIG_STANDARD_LIBRARY_H <stdlib.h> + +#include <string.h> /* for memset */ + +#include "ftmisc.h" +#include "ftimage.h" + +#else /* !STANDALONE_ */ + +#include "ftraster.h" +#include <freetype/internal/ftcalc.h> /* for FT_MulDiv and FT_MulDiv_No_Round */ +#include <freetype/ftoutln.h> /* for FT_Outline_Get_CBox */ + +#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 + * + * o its drop-out mode + * + * 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 **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** 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 raster + + +#ifdef STANDALONE_ + + /* Auxiliary macros for token concatenation. */ +#define FT_ERR_XCAT( x, y ) x ## y +#define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y ) + + /* 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 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 */ +#define FT_TRACE7( x ) do { } while ( 0 ) /* nothing */ +#endif + +#ifndef FT_THROW +#define FT_THROW( e ) FT_ERR_CAT( Raster_Err_, e ) +#endif + +#define Raster_Err_Ok 0 +#define Raster_Err_Invalid_Outline -1 +#define Raster_Err_Cannot_Render_Glyph -2 +#define Raster_Err_Invalid_Argument -3 +#define Raster_Err_Raster_Overflow -4 +#define Raster_Err_Raster_Uninitialized -5 +#define Raster_Err_Raster_Negative_Height -6 + +#define ft_memset memset + +#define FT_DEFINE_RASTER_FUNCS( class_, glyph_format_, raster_new_, \ + raster_reset_, raster_set_mode_, \ + raster_render_, raster_done_ ) \ + const FT_Raster_Funcs class_ = \ + { \ + glyph_format_, \ + raster_new_, \ + raster_reset_, \ + raster_set_mode_, \ + raster_render_, \ + raster_done_ \ + }; + +#else /* !STANDALONE_ */ + + +#include <freetype/internal/ftobjs.h> +#include <freetype/internal/ftdebug.h> /* for FT_TRACE, FT_ERROR, and FT_THROW */ + +#include "rasterrs.h" + + +#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 + +#ifndef FT_ZERO +#define FT_ZERO( p ) FT_MEM_ZERO( p, sizeof ( *(p) ) ) +#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 +#define SMulDiv_No_Round FT_MulDiv_No_Round + + /* 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; + + + /* values for the `flags' bit field */ +#define Flow_Up 0x08U +#define Overshoot_Top 0x10U +#define Overshoot_Bottom 0x20U + + + /* 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 purposes) */ + PLong offset; /* start of profile's data in render pool */ + UShort flags; /* Bit 0-2: drop-out mode */ + /* Bit 3: profile orientation (up/down) */ + /* Bit 4: is top profile? */ + /* Bit 5: is bottom profile? */ + Long height; /* profile's height in scanlines */ + Long start; /* profile's starting scanline */ + + Int 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; + + +#define AlignProfileSize \ + ( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( Long ) ) + + +#undef RAS_ARG +#undef RAS_ARGS +#undef RAS_VAR +#undef RAS_VARS + +#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 black_PWorker worker, +#define RAS_ARG black_PWorker worker + +#define RAS_VARS worker, +#define RAS_VAR worker + +#define FT_UNUSED_RASTER FT_UNUSED( worker ) + + +#endif /* !FT_STATIC_RASTER */ + + + typedef struct black_TWorker_ black_TWorker, *black_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 */ +#undef FLOOR +#undef CEILING +#undef TRUNC +#undef SCALED + +#define FLOOR( x ) ( (x) & -ras.precision ) +#define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision ) +#define TRUNC( x ) ( (Long)(x) >> ras.precision_bits ) +#define FRAC( x ) ( (x) & ( ras.precision - 1 ) ) + + /* scale and shift grid to pixel centers */ +#define SCALED( x ) ( (x) * ras.precision_scale - ras.precision_half ) + +#define IS_BOTTOM_OVERSHOOT( x ) \ + (Bool)( CEILING( x ) - x >= ras.precision_half ) +#define IS_TOP_OVERSHOOT( x ) \ + (Bool)( x - FLOOR( x ) >= ras.precision_half ) + + /* Smart dropout rounding to find which pixel is closer to span ends. */ + /* To mimick Windows, symmetric cases break down indepenently of the */ + /* precision. */ +#define SMART( p, q ) FLOOR( ( (p) + (q) + ras.precision * 63 / 64 ) >> 1 ) + +#if FT_RENDER_POOL_SIZE > 2048 +#define FT_MAX_BLACK_POOL ( FT_RENDER_POOL_SIZE / sizeof ( Long ) ) +#else +#define FT_MAX_BLACK_POOL ( 2048 / sizeof ( Long ) ) +#endif + + /* The most used variables are positioned at the top of the structure. */ + /* Thus, their offset can be coded with less opcodes, resulting in a */ + /* smaller executable. */ + + struct black_TWorker_ + { + Int precision_bits; /* precision related variables */ + Int precision; + Int precision_half; + Int precision_scale; + Int precision_step; + Int precision_jitter; + + 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 */ + + Byte dropOutControl; /* current drop_out control method */ + + UShort bWidth; /* target bitmap width */ + PByte bOrigin; /* target bitmap bottom-left origin */ + PByte bLine; /* target bitmap current line */ + + Long lastX, lastY; + Long 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; + + /* 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; + + }; + + + typedef struct black_TRaster_ + { + void* memory; + + } black_TRaster, *black_PRaster; + +#ifdef FT_STATIC_RASTER + + static black_TWorker ras; + +#else /* !FT_STATIC_RASTER */ + +#define ras (*worker) + +#endif /* !FT_STATIC_RASTER */ + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** PROFILES COMPUTATION **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + + /************************************************************************** + * + * @Function: + * Set_High_Precision + * + * @Description: + * Set precision variables according to param flag. + * + * @Input: + * High :: + * Set to True for high precision (typically for ppem < 24), + * false otherwise. + */ + static void + Set_High_Precision( RAS_ARGS Int High ) + { + /* + * `precision_step' is used in `Bezier_Up' to decide when to split a + * given y-monotonous Bezier arc that crosses a scanline before + * approximating it as a straight segment. The default value of 32 (for + * low accuracy) corresponds to + * + * 32 / 64 == 0.5 pixels, + * + * while for the high accuracy case we have + * + * 256 / (1 << 12) = 0.0625 pixels. + * + * `precision_jitter' is an epsilon threshold used in + * `Vertical_Sweep_Span' to deal with small imperfections in the Bezier + * decomposition (after all, we are working with approximations only); + * it avoids switching on additional pixels which would cause artifacts + * otherwise. + * + * The value of `precision_jitter' has been determined heuristically. + * + */ + + if ( High ) + { + ras.precision_bits = 12; + ras.precision_step = 256; + ras.precision_jitter = 30; + } + 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 >> 1; + ras.precision_scale = ras.precision >> Pixel_Bits; + } + + + /************************************************************************** + * + * @Function: + * New_Profile + * + * @Description: + * Create a new profile in the render pool. + * + * @Input: + * aState :: + * The state/orientation of the new profile. + * + * overshoot :: + * Whether the profile's unrounded start position + * differs by at least a half pixel. + * + * @Return: + * SUCCESS on success. FAILURE in case of overflow or of incoherent + * profile. + */ + static Bool + New_Profile( RAS_ARGS TStates aState, + Bool overshoot ) + { + if ( !ras.fProfile ) + { + ras.cProfile = (PProfile)ras.top; + ras.fProfile = ras.cProfile; + ras.top += AlignProfileSize; + } + + if ( ras.top >= ras.maxBuff ) + { + ras.error = FT_THROW( Raster_Overflow ); + 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; + ras.cProfile->flags = ras.dropOutControl; + + switch ( aState ) + { + case Ascending_State: + ras.cProfile->flags |= Flow_Up; + if ( overshoot ) + ras.cProfile->flags |= Overshoot_Bottom; + + FT_TRACE6(( " new ascending profile = %p\n", (void *)ras.cProfile )); + break; + + case Descending_State: + if ( overshoot ) + ras.cProfile->flags |= Overshoot_Top; + FT_TRACE6(( " new descending profile = %p\n", (void *)ras.cProfile )); + break; + + default: + FT_ERROR(( "New_Profile: invalid profile direction\n" )); + ras.error = FT_THROW( Invalid_Outline ); + return FAILURE; + } + + if ( !ras.gProfile ) + ras.gProfile = ras.cProfile; + + ras.state = aState; + ras.fresh = TRUE; + ras.joint = FALSE; + + return SUCCESS; + } + + + /************************************************************************** + * + * @Function: + * End_Profile + * + * @Description: + * Finalize the current profile. + * + * @Input: + * overshoot :: + * Whether the profile's unrounded end position differs + * by at least a half pixel. + * + * @Return: + * SUCCESS on success. FAILURE in case of overflow or incoherency. + */ + static Bool + End_Profile( RAS_ARGS Bool overshoot ) + { + Long h; + + + h = (Long)( ras.top - ras.cProfile->offset ); + + if ( h < 0 ) + { + FT_ERROR(( "End_Profile: negative height encountered\n" )); + ras.error = FT_THROW( Raster_Negative_Height ); + return FAILURE; + } + + if ( h > 0 ) + { + PProfile oldProfile; + + + FT_TRACE6(( " ending profile %p, start = %ld, height = %ld\n", + (void *)ras.cProfile, ras.cProfile->start, h )); + + ras.cProfile->height = h; + if ( overshoot ) + { + if ( ras.cProfile->flags & Flow_Up ) + ras.cProfile->flags |= Overshoot_Top; + else + ras.cProfile->flags |= Overshoot_Bottom; + } + + oldProfile = ras.cProfile; + 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 = FT_THROW( Raster_Overflow ); + return FAILURE; + } + + ras.joint = FALSE; + + return SUCCESS; + } + + + /************************************************************************** + * + * @Function: + * Insert_Y_Turn + * + * @Description: + * Insert 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 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] ) + do + { + Int y2 = (Int)y_turns[n]; + + + y_turns[n] = y; + y = y2; + } while ( --n >= 0 ); + + if ( n < 0 ) + { + ras.maxBuff--; + if ( ras.maxBuff <= ras.top ) + { + ras.error = FT_THROW( Raster_Overflow ); + return FAILURE; + } + ras.numTurns++; + ras.sizeBuff[-ras.numTurns] = y; + } + + return SUCCESS; + } + + + /************************************************************************** + * + * @Function: + * Finalize_Profile_Table + * + * @Description: + * Adjust all links in the profiles list. + * + * @Return: + * SUCCESS on success. FAILURE in case of overflow. + */ + static Bool + Finalize_Profile_Table( RAS_ARG ) + { + UShort n; + PProfile p; + + + n = ras.num_Profs; + p = ras.fProfile; + + if ( n > 1 && p ) + { + do + { + Int bottom, top; + + + if ( n > 1 ) + p->link = (PProfile)( p->offset + p->height ); + else + p->link = NULL; + + if ( p->flags & Flow_Up ) + { + bottom = (Int)p->start; + top = (Int)( p->start + p->height - 1 ); + } + else + { + bottom = (Int)( p->start - p->height + 1 ); + top = (Int)p->start; + p->start = bottom; + p->offset += p->height - 1; + } + + if ( Insert_Y_Turn( RAS_VARS bottom ) || + Insert_Y_Turn( RAS_VARS top + 1 ) ) + return FAILURE; + + p = p->link; + } while ( --n ); + } + else + ras.fProfile = NULL; + + return SUCCESS; + } + + + /************************************************************************** + * + * @Function: + * Split_Conic + * + * @Description: + * Subdivide 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; + a = base[0].x + base[1].x; + b = base[1].x + base[2].x; + base[3].x = b >> 1; + base[2].x = ( a + b ) >> 2; + base[1].x = a >> 1; + + base[4].y = base[2].y; + a = base[0].y + base[1].y; + b = base[1].y + base[2].y; + base[3].y = b >> 1; + base[2].y = ( a + b ) >> 2; + base[1].y = a >> 1; + + /* hand optimized. gcc doesn't seem to be too good at common */ + /* expression substitution and instruction scheduling ;-) */ + } + + + /************************************************************************** + * + * @Function: + * Split_Cubic + * + * @Description: + * Subdivide 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; + + + base[6].x = base[3].x; + a = base[0].x + base[1].x; + b = base[1].x + base[2].x; + c = base[2].x + base[3].x; + base[5].x = c >> 1; + c += b; + base[4].x = c >> 2; + base[1].x = a >> 1; + a += b; + base[2].x = a >> 2; + base[3].x = ( a + c ) >> 3; + + base[6].y = base[3].y; + a = base[0].y + base[1].y; + b = base[1].y + base[2].y; + c = base[2].y + base[3].y; + base[5].y = c >> 1; + c += b; + base[4].y = c >> 2; + base[1].y = a >> 1; + a += b; + base[2].y = a >> 2; + base[3].y = ( a + c ) >> 3; + } + + + /************************************************************************** + * + * @Function: + * Line_Up + * + * @Description: + * Compute the x-coordinates of an ascending line segment and store + * 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 += SMulDiv( 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 = FT_THROW( Raster_Overflow ); + return FAILURE; + } + + if ( Dx > 0 ) + { + Ix = SMulDiv_No_Round( ras.precision, Dx, Dy ); + Rx = ( ras.precision * Dx ) % Dy; + Dx = 1; + } + else + { + Ix = -SMulDiv_No_Round( 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: + * Compute the x-coordinates of an descending line segment and store + * 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: + * Compute the x-coordinates of an ascending Bezier arc and store + * 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, + TPoint* arc, + TSplitter splitter, + Long miny, + Long maxy ) + { + Long y1, y2, e, e2, e0; + Short f1; + + TPoint* start_arc; + + PLong top; + + + 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 = FT_THROW( Raster_Overflow ); + return FAILURE; + } + + start_arc = arc; + + do + { + 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; + } + } while ( arc >= start_arc && e <= e2 ); + + Fin: + ras.top = top; + return SUCCESS; + } + + + /************************************************************************** + * + * @Function: + * Bezier_Down + * + * @Description: + * Compute the x-coordinates of an descending Bezier arc and store + * 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, + TPoint* arc, + TSplitter splitter, + Long miny, + Long maxy ) + { + 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, arc, 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: + * Inject a new line segment and adjust the 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, + IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ) + return FAILURE; + } + else + { + if ( y < ras.lastY ) + if ( New_Profile( RAS_VARS Descending_State, + IS_TOP_OVERSHOOT( ras.lastY ) ) ) + return FAILURE; + } + break; + + case Ascending_State: + if ( y < ras.lastY ) + { + if ( End_Profile( RAS_VARS IS_TOP_OVERSHOOT( ras.lastY ) ) || + New_Profile( RAS_VARS Descending_State, + IS_TOP_OVERSHOOT( ras.lastY ) ) ) + return FAILURE; + } + break; + + case Descending_State: + if ( y > ras.lastY ) + { + if ( End_Profile( RAS_VARS IS_BOTTOM_OVERSHOOT( ras.lastY ) ) || + New_Profile( RAS_VARS Ascending_State, + IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ) + 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: + * Inject a new conic arc and adjust 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; + TPoint arcs[2 * MaxBezier + 1]; /* The Bezier stack */ + TPoint* arc; /* current Bezier arc pointer */ + + + arc = arcs; + arc[2].x = ras.lastX; + arc[2].y = ras.lastY; + arc[1].x = cx; + arc[1].y = cy; + arc[0].x = x; + arc[0].y = y; + + do + { + y1 = arc[2].y; + y2 = arc[1].y; + y3 = arc[0].y; + x3 = 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( arc ); + arc += 2; + } + else if ( y1 == y3 ) + { + /* this arc is flat, ignore it and pop it from the Bezier stack */ + 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 ) + { + Bool o = ( state_bez == Ascending_State ) + ? IS_BOTTOM_OVERSHOOT( y1 ) + : IS_TOP_OVERSHOOT( y1 ); + + + /* finalize current profile if any */ + if ( ras.state != Unknown_State && + End_Profile( RAS_VARS o ) ) + goto Fail; + + /* create a new profile */ + if ( New_Profile( RAS_VARS state_bez, o ) ) + goto Fail; + } + + /* now call the appropriate routine */ + if ( state_bez == Ascending_State ) + { + if ( Bezier_Up( RAS_VARS 2, arc, Split_Conic, + ras.minY, ras.maxY ) ) + goto Fail; + } + else + if ( Bezier_Down( RAS_VARS 2, arc, Split_Conic, + ras.minY, ras.maxY ) ) + goto Fail; + arc -= 2; + } + + } while ( arc >= arcs ); + + ras.lastX = x3; + ras.lastY = y3; + + return SUCCESS; + + Fail: + return FAILURE; + } + + + /************************************************************************** + * + * @Function: + * Cubic_To + * + * @Description: + * Inject a new cubic arc and adjust 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; + TPoint arcs[3 * MaxBezier + 1]; /* The Bezier stack */ + TPoint* arc; /* current Bezier arc pointer */ + + + arc = arcs; + arc[3].x = ras.lastX; + arc[3].y = ras.lastY; + arc[2].x = cx1; + arc[2].y = cy1; + arc[1].x = cx2; + arc[1].y = cy2; + arc[0].x = x; + arc[0].y = y; + + do + { + y1 = arc[3].y; + y2 = arc[2].y; + y3 = arc[1].y; + y4 = arc[0].y; + x4 = 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( arc ); + arc += 3; + } + else if ( y1 == y4 ) + { + /* this arc is flat, ignore it and pop it from the Bezier stack */ + arc -= 3; + } + else + { + state_bez = ( y1 <= y4 ) ? Ascending_State : Descending_State; + + /* detect a change of direction */ + if ( ras.state != state_bez ) + { + Bool o = ( state_bez == Ascending_State ) + ? IS_BOTTOM_OVERSHOOT( y1 ) + : IS_TOP_OVERSHOOT( y1 ); + + + /* finalize current profile if any */ + if ( ras.state != Unknown_State && + End_Profile( RAS_VARS o ) ) + goto Fail; + + if ( New_Profile( RAS_VARS state_bez, o ) ) + goto Fail; + } + + /* compute intersections */ + if ( state_bez == Ascending_State ) + { + if ( Bezier_Up( RAS_VARS 3, arc, Split_Cubic, + ras.minY, ras.maxY ) ) + goto Fail; + } + else + if ( Bezier_Down( RAS_VARS 3, arc, Split_Cubic, + ras.minY, ras.maxY ) ) + goto Fail; + arc -= 3; + } + + } while ( arc >= 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: + * Scan 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; + + UInt 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; + + /* set scan mode if necessary */ + if ( tags[0] & FT_CURVE_TAG_HAS_SCANMODE ) + ras.dropOutControl = (Byte)tags[0] >> 5; + + 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 ); + + if ( flipped ) + { + SWAP_( x1, y1 ); + SWAP_( x2, y2 ); + } + + if ( point <= limit ) + { + x3 = SCALED( point[0].x ); + y3 = SCALED( point[0].y ); + + if ( flipped ) + SWAP_( x3, y3 ); + + 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 = FT_THROW( Invalid_Outline ); + + Fail: + return FAILURE; + } + + + /************************************************************************** + * + * @Function: + * Convert_Glyph + * + * @Description: + * Convert a glyph into a series of segments and arcs and make 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; + UInt start; + + + 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++ ) + { + PProfile lastProfile; + Bool o; + + + ras.state = Unknown_State; + ras.gProfile = NULL; + + if ( Decompose_Curve( RAS_VARS (UShort)start, + (UShort)ras.outline.contours[i], + flipped ) ) + return FAILURE; + + start = (UShort)ras.outline.contours[i] + 1; + + /* we must now check 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->flags & Flow_Up ) == + ( ras.cProfile->flags & Flow_Up ) ) + ras.top--; + /* Note that ras.gProfile can be nil if the contour was too small */ + /* to be drawn. */ + + lastProfile = ras.cProfile; + if ( ras.top != ras.cProfile->offset && + ( ras.cProfile->flags & Flow_Up ) ) + o = IS_TOP_OVERSHOOT( ras.lastY ); + else + o = IS_BOTTOM_OVERSHOOT( ras.lastY ); + if ( End_Profile( RAS_VARS o ) ) + 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, + const 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->flags & Flow_Up ) ? 1 : -1; + 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 ) + { + FT_UNUSED( max ); + + + ras.bLine = ras.bOrigin - min * ras.target.pitch; + } + + + static void + Vertical_Sweep_Span( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + Long e1, e2; + + Int dropOutControl = left->flags & 7; + + FT_UNUSED( y ); + FT_UNUSED( left ); + FT_UNUSED( right ); + + + /* in high-precision mode, we need 12 digits after the comma to */ + /* represent multiples of 1/(1<<12) = 1/4096 */ + FT_TRACE7(( " y=%d x=[% .12f;% .12f]", + y, + (double)x1 / (double)ras.precision, + (double)x2 / (double)ras.precision )); + + /* Drop-out control */ + + e1 = CEILING( x1 ); + e2 = FLOOR( x2 ); + + /* take care of the special case where both the left */ + /* and right contour lie exactly on pixel centers */ + if ( dropOutControl != 2 && + x2 - x1 - ras.precision <= ras.precision_jitter && + e1 != x1 && e2 != x2 ) + e2 = e1; + + e1 = TRUNC( e1 ); + e2 = TRUNC( e2 ); + + if ( e2 >= 0 && e1 < ras.bWidth ) + { + Byte* target; + + Int c1, c2; + Byte f1, f2; + + + if ( e1 < 0 ) + e1 = 0; + if ( e2 >= ras.bWidth ) + e2 = ras.bWidth - 1; + + FT_TRACE7(( " -> x=[%ld;%ld]", e1, e2 )); + + c1 = (Short)( e1 >> 3 ); + c2 = (Short)( e2 >> 3 ); + + f1 = (Byte) ( 0xFF >> ( e1 & 7 ) ); + f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) ); + + target = ras.bLine + 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. */ + while ( --c2 > 0 ) + *( ++target ) = 0xFF; + + target[1] |= f2; + } + else + *target |= ( f1 & f2 ); + } + + FT_TRACE7(( "\n" )); + } + + + static void + Vertical_Sweep_Drop( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + Long e1, e2, pxl; + Short c1, f1; + + + FT_TRACE7(( " y=%d x=[% .12f;% .12f]", + y, + (double)x1 / (double)ras.precision, + (double)x2 / (double)ras.precision )); + + /* Drop-out control */ + + /* e2 x2 x1 e1 */ + /* */ + /* ^ | */ + /* | | */ + /* +-------------+---------------------+------------+ */ + /* | | */ + /* | v */ + /* */ + /* pixel contour contour pixel */ + /* center center */ + + /* drop-out mode scan conversion rules (as defined in OpenType) */ + /* --------------------------------------------------------------- */ + /* 0 1, 2, 3 */ + /* 1 1, 2, 4 */ + /* 2 1, 2 */ + /* 3 same as mode 2 */ + /* 4 1, 2, 5 */ + /* 5 1, 2, 6 */ + /* 6, 7 same as mode 2 */ + + e1 = CEILING( x1 ); + e2 = FLOOR ( x2 ); + pxl = e1; + + if ( e1 > e2 ) + { + Int dropOutControl = left->flags & 7; + + + if ( e1 == e2 + ras.precision ) + { + switch ( dropOutControl ) + { + case 0: /* simple drop-outs including stubs */ + pxl = e2; + break; + + case 4: /* smart drop-outs including stubs */ + pxl = SMART( x1, x2 ); + break; + + case 1: /* simple drop-outs excluding stubs */ + case 5: /* smart drop-outs excluding stubs */ + + /* Drop-out Control Rules #4 and #6 */ + + /* The specification neither provides an exact definition */ + /* of a `stub' nor gives exact rules to exclude them. */ + /* */ + /* Here the constraints we use to recognize a stub. */ + /* */ + /* 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 */ + /* */ + /* We draw a stub if the following constraints are met. */ + /* */ + /* - for an upper or lower stub, there is top or bottom */ + /* overshoot, respectively */ + /* - the covered interval is greater or equal to a half */ + /* pixel */ + + /* upper stub test */ + if ( left->next == right && + left->height <= 0 && + !( left->flags & Overshoot_Top && + x2 - x1 >= ras.precision_half ) ) + goto Exit; + + /* lower stub test */ + if ( right->next == left && + left->start == y && + !( left->flags & Overshoot_Bottom && + x2 - x1 >= ras.precision_half ) ) + goto Exit; + + if ( dropOutControl == 1 ) + pxl = e2; + else + pxl = SMART( x1, x2 ); + break; + + default: /* modes 2, 3, 6, 7 */ + goto Exit; /* no drop-out control */ + } + + /* undocumented but confirmed: If the drop-out would result in a */ + /* pixel outside of the bounding box, use the pixel inside of the */ + /* bounding box instead */ + if ( pxl < 0 ) + pxl = e1; + else if ( TRUNC( pxl ) >= ras.bWidth ) + pxl = e2; + + /* check that the other pixel isn't set */ + e1 = ( pxl == e1 ) ? e2 : e1; + + e1 = TRUNC( e1 ); + + c1 = (Short)( e1 >> 3 ); + f1 = (Short)( e1 & 7 ); + + if ( e1 >= 0 && e1 < ras.bWidth && + ras.bLine[c1] & ( 0x80 >> f1 ) ) + goto Exit; + } + else + goto Exit; + } + + e1 = TRUNC( pxl ); + + if ( e1 >= 0 && e1 < ras.bWidth ) + { + FT_TRACE7(( " -> x=%ld", e1 )); + + c1 = (Short)( e1 >> 3 ); + f1 = (Short)( e1 & 7 ); + + ras.bLine[c1] |= (char)( 0x80 >> f1 ); + } + + Exit: + FT_TRACE7(( " dropout=%d\n", left->flags & 7 )); + } + + + static void + Vertical_Sweep_Step( RAS_ARG ) + { + ras.bLine -= ras.target.pitch; + } + + + /************************************************************************ + * + * 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; + + FT_UNUSED( left ); + FT_UNUSED( right ); + + + FT_TRACE7(( " x=%d y=[% .12f;% .12f]", + y, + (double)x1 / (double)ras.precision, + (double)x2 / (double)ras.precision )); + + /* We should not need this procedure but the vertical sweep */ + /* mishandles horizontal lines through pixel centers. So we */ + /* have to check perfectly aligned span edges here. */ + /* */ + /* XXX: Can we handle horizontal lines better and drop this? */ + + e1 = CEILING( x1 ); + + if ( x1 == e1 ) + { + e1 = TRUNC( e1 ); + + if ( e1 >= 0 && (ULong)e1 < ras.target.rows ) + { + Byte f1; + PByte bits; + + + bits = ras.bOrigin + ( y >> 3 ) - e1 * ras.target.pitch; + f1 = (Byte)( 0x80 >> ( y & 7 ) ); + + FT_TRACE7(( bits[0] & f1 ? " redundant" + : " -> y=%ld edge", e1 )); + + bits[0] |= f1; + } + } + + e2 = FLOOR ( x2 ); + + if ( x2 == e2 ) + { + e2 = TRUNC( e2 ); + + if ( e2 >= 0 && (ULong)e2 < ras.target.rows ) + { + Byte f1; + PByte bits; + + + bits = ras.bOrigin + ( y >> 3 ) - e2 * ras.target.pitch; + f1 = (Byte)( 0x80 >> ( y & 7 ) ); + + FT_TRACE7(( bits[0] & f1 ? " redundant" + : " -> y=%ld edge", e2 )); + + bits[0] |= f1; + } + } + + FT_TRACE7(( "\n" )); + } + + + static void + Horizontal_Sweep_Drop( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + Long e1, e2, pxl; + PByte bits; + Byte f1; + + + FT_TRACE7(( " x=%d y=[% .12f;% .12f]", + y, + (double)x1 / (double)ras.precision, + (double)x2 / (double)ras.precision )); + + /* During the horizontal sweep, we only take care of drop-outs */ + + /* e1 + <-- pixel center */ + /* | */ + /* x1 ---+--> <-- contour */ + /* | */ + /* | */ + /* x2 <--+--- <-- contour */ + /* | */ + /* | */ + /* e2 + <-- pixel center */ + + e1 = CEILING( x1 ); + e2 = FLOOR ( x2 ); + pxl = e1; + + if ( e1 > e2 ) + { + Int dropOutControl = left->flags & 7; + + + if ( e1 == e2 + ras.precision ) + { + switch ( dropOutControl ) + { + case 0: /* simple drop-outs including stubs */ + pxl = e2; + break; + + case 4: /* smart drop-outs including stubs */ + pxl = SMART( x1, x2 ); + break; + + case 1: /* simple drop-outs excluding stubs */ + case 5: /* smart drop-outs excluding stubs */ + /* see Vertical_Sweep_Drop for details */ + + /* rightmost stub test */ + if ( left->next == right && + left->height <= 0 && + !( left->flags & Overshoot_Top && + x2 - x1 >= ras.precision_half ) ) + goto Exit; + + /* leftmost stub test */ + if ( right->next == left && + left->start == y && + !( left->flags & Overshoot_Bottom && + x2 - x1 >= ras.precision_half ) ) + goto Exit; + + if ( dropOutControl == 1 ) + pxl = e2; + else + pxl = SMART( x1, x2 ); + break; + + default: /* modes 2, 3, 6, 7 */ + goto Exit; /* no drop-out control */ + } + + /* undocumented but confirmed: If the drop-out would result in a */ + /* pixel outside of the bounding box, use the pixel inside of the */ + /* bounding box instead */ + if ( pxl < 0 ) + pxl = e1; + else if ( (ULong)( TRUNC( pxl ) ) >= ras.target.rows ) + pxl = e2; + + /* check that the other pixel isn't set */ + e1 = ( pxl == e1 ) ? e2 : e1; + + e1 = TRUNC( e1 ); + + bits = ras.bOrigin + ( y >> 3 ) - e1 * ras.target.pitch; + f1 = (Byte)( 0x80 >> ( y & 7 ) ); + + if ( e1 >= 0 && + (ULong)e1 < ras.target.rows && + *bits & f1 ) + goto Exit; + } + else + goto Exit; + } + + e1 = TRUNC( pxl ); + + if ( e1 >= 0 && (ULong)e1 < ras.target.rows ) + { + FT_TRACE7(( " -> y=%ld", e1 )); + + bits = ras.bOrigin + ( y >> 3 ) - e1 * ras.target.pitch; + f1 = (Byte)( 0x80 >> ( y & 7 ) ); + + bits[0] |= f1; + } + + Exit: + FT_TRACE7(( " dropout=%d\n", left->flags & 7 )); + } + + + static void + Horizontal_Sweep_Step( RAS_ARG ) + { + /* Nothing, really */ + FT_UNUSED_RASTER; + } + + + /************************************************************************** + * + * 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; + + + /* initialize 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 = FT_THROW( Invalid_Outline ); + return FAILURE; + } + + /* now initialize 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 = 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 ) + { + /* check waiting list for new activations */ + + P = waiting; + + while ( P ) + { + Q = P->link; + P->countL -= y_height; + if ( P->countL == 0 ) + { + DelOld( &waiting, P ); + + if ( P->flags & Flow_Up ) + InsNew( &draw_left, P ); + else + InsNew( &draw_right, P ); + } + + 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 && P_Right ) + { + x1 = P_Left ->X; + x2 = P_Right->X; + + if ( x1 > x2 ) + { + xs = x1; + x1 = x2; + x2 = xs; + } + + e1 = FLOOR( x1 ); + e2 = CEILING( x2 ); + + if ( x2 - x1 <= ras.precision && + e1 != x1 && e2 != x2 ) + { + if ( e1 > e2 || e2 == e1 + ras.precision ) + { + Int dropOutControl = P_Left->flags & 7; + + + if ( dropOutControl != 2 ) + { + /* 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; + } + + /* handle drop-outs _after_ the span drawing -- */ + /* drop-out 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 need 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, flush 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 && P_Right ) + { + 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; + } + + +#ifdef STANDALONE_ + + /************************************************************************** + * + * The following functions should only compile in stand-alone mode, + * i.e., when building this component without the rest of FreeType. + * + */ + + /************************************************************************** + * + * @Function: + * FT_Outline_Get_CBox + * + * @Description: + * Return an outline's `control box'. The control box encloses all + * the outline's points, including Bézier control points. Though it + * coincides with the exact bounding box for most glyphs, it can be + * slightly larger in some situations (like when rotating an outline + * that contains Bézier outside arcs). + * + * Computing the control box is very fast, while getting the bounding + * box can take much more time as it needs to walk over all segments + * and arcs in the outline. To get the latter, you can use the + * `ftbbox' component, which is dedicated to this single task. + * + * @Input: + * outline :: + * A pointer to the source outline descriptor. + * + * @Output: + * acbox :: + * The outline's control box. + * + * @Note: + * See @FT_Glyph_Get_CBox for a discussion of tricky fonts. + */ + + static void + FT_Outline_Get_CBox( const FT_Outline* outline, + FT_BBox *acbox ) + { + if ( outline && acbox ) + { + Long xMin, yMin, xMax, yMax; + + + if ( outline->n_points == 0 ) + { + xMin = 0; + yMin = 0; + xMax = 0; + yMax = 0; + } + else + { + FT_Vector* vec = outline->points; + FT_Vector* limit = vec + outline->n_points; + + + xMin = xMax = vec->x; + yMin = yMax = vec->y; + vec++; + + for ( ; vec < limit; vec++ ) + { + Long x, y; + + + x = vec->x; + if ( x < xMin ) xMin = x; + if ( x > xMax ) xMax = x; + + y = vec->y; + if ( y < yMin ) yMin = y; + if ( y > yMax ) yMax = y; + } + } + acbox->xMin = xMin; + acbox->xMax = xMax; + acbox->yMin = yMin; + acbox->yMax = yMax; + } + } + +#endif /* STANDALONE_ */ + + + /************************************************************************** + * + * @Function: + * Render_Single_Pass + * + * @Description: + * Perform 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, + Int y_min, + Int y_max ) + { + Int y_mid; + Int band_top = 0; + Int band_stack[32]; /* enough to bisect 32-bit int bands */ + + + while ( 1 ) + { + ras.minY = (Long)y_min * ras.precision; + ras.maxY = (Long)y_max * ras.precision; + + ras.top = ras.buff; + + ras.error = Raster_Err_Ok; + + if ( Convert_Glyph( RAS_VARS flipped ) ) + { + if ( ras.error != Raster_Err_Raster_Overflow ) + return ras.error; + + /* sub-banding */ + + if ( y_min == y_max ) + return ras.error; /* still Raster_Overflow */ + + y_mid = ( y_min + y_max ) >> 1; + + band_stack[band_top++] = y_min; + y_min = y_mid + 1; + } + else + { + if ( ras.fProfile ) + if ( Draw_Sweep( RAS_VAR ) ) + return ras.error; + + if ( --band_top < 0 ) + break; + + y_max = y_min - 1; + y_min = band_stack[band_top]; + } + } + + return Raster_Err_Ok; + } + + + /************************************************************************** + * + * @Function: + * Render_Glyph + * + * @Description: + * Render a glyph in a bitmap. Sub-banding if needed. + * + * @Return: + * FreeType error code. 0 means success. + */ + static FT_Error + Render_Glyph( RAS_ARG ) + { + FT_Error error; + + + Set_High_Precision( RAS_VARS ras.outline.flags & + FT_OUTLINE_HIGH_PRECISION ); + + if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS ) + ras.dropOutControl = 2; + else + { + if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS ) + ras.dropOutControl = 4; + else + ras.dropOutControl = 0; + + if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) ) + ras.dropOutControl += 1; + } + + /* Vertical Sweep */ + FT_TRACE7(( "Vertical pass (ftraster)\n" )); + + 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.bWidth = (UShort)ras.target.width; + ras.bOrigin = (Byte*)ras.target.buffer; + + if ( ras.target.pitch > 0 ) + ras.bOrigin += (Long)( ras.target.rows - 1 ) * ras.target.pitch; + + error = Render_Single_Pass( RAS_VARS 0, 0, (Int)ras.target.rows - 1 ); + if ( error ) + return error; + + /* Horizontal Sweep */ + if ( !( ras.outline.flags & FT_OUTLINE_SINGLE_PASS ) ) + { + FT_TRACE7(( "Horizontal pass (ftraster)\n" )); + + 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; + + error = Render_Single_Pass( RAS_VARS 1, 0, (Int)ras.target.width - 1 ); + if ( error ) + return error; + } + + return Raster_Err_Ok; + } + + + /**** 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 black_TRaster the_raster; + FT_UNUSED( memory ); + + + *araster = (FT_Raster)&the_raster; + FT_ZERO( &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, + black_PRaster *araster ) + { + FT_Error error; + black_PRaster raster = NULL; + + + if ( !FT_NEW( raster ) ) + raster->memory = memory; + + *araster = raster; + + return error; + } + + + static void + ft_black_done( black_PRaster raster ) + { + FT_Memory memory = (FT_Memory)raster->memory; + + + FT_FREE( raster ); + } + + +#endif /* !STANDALONE_ */ + + + static void + ft_black_reset( FT_Raster raster, + PByte pool_base, + ULong pool_size ) + { + FT_UNUSED( raster ); + FT_UNUSED( pool_base ); + FT_UNUSED( pool_size ); + } + + + static int + ft_black_set_mode( FT_Raster raster, + ULong mode, + void* args ) + { + FT_UNUSED( raster ); + FT_UNUSED( mode ); + FT_UNUSED( args ); + + return 0; + } + + + static int + ft_black_render( FT_Raster raster, + const FT_Raster_Params* params ) + { + const FT_Outline* outline = (const FT_Outline*)params->source; + const FT_Bitmap* target_map = params->target; + +#ifndef FT_STATIC_RASTER + black_TWorker worker[1]; +#endif + + Long buffer[FT_MAX_BLACK_POOL]; + + + if ( !raster ) + return FT_THROW( Raster_Uninitialized ); + + if ( !outline ) + return FT_THROW( Invalid_Outline ); + + /* return immediately if the outline is empty */ + if ( outline->n_points == 0 || outline->n_contours <= 0 ) + return Raster_Err_Ok; + + if ( !outline->contours || !outline->points ) + return FT_THROW( Invalid_Outline ); + + if ( outline->n_points != + outline->contours[outline->n_contours - 1] + 1 ) + return FT_THROW( Invalid_Outline ); + + /* this version of the raster does not support direct rendering, sorry */ + if ( params->flags & FT_RASTER_FLAG_DIRECT || + params->flags & FT_RASTER_FLAG_AA ) + return FT_THROW( Cannot_Render_Glyph ); + + if ( !target_map ) + return FT_THROW( Invalid_Argument ); + + /* nothing to do */ + if ( !target_map->width || !target_map->rows ) + return Raster_Err_Ok; + + if ( !target_map->buffer ) + return FT_THROW( Invalid_Argument ); + + ras.outline = *outline; + ras.target = *target_map; + + ras.buff = buffer; + ras.sizeBuff = (&buffer)[1]; /* Points to right after buffer. */ + + return Render_Glyph( RAS_VAR ); + } + + + FT_DEFINE_RASTER_FUNCS( + ft_standard_raster, + + FT_GLYPH_FORMAT_OUTLINE, + + (FT_Raster_New_Func) ft_black_new, /* raster_new */ + (FT_Raster_Reset_Func) ft_black_reset, /* raster_reset */ + (FT_Raster_Set_Mode_Func)ft_black_set_mode, /* raster_set_mode */ + (FT_Raster_Render_Func) ft_black_render, /* raster_render */ + (FT_Raster_Done_Func) ft_black_done /* raster_done */ + ) + + +/* END */ |