From 5ae1bad3f6f1c842a6f87d8c6e1e5c0237eb236b Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 27 Jun 2000 23:18:39 +0000 Subject: [PATCH] added new renderer module --- src/raster1/ftraster.c | 3270 ++++++++++++++++++++++++++++++++++++++++ src/raster1/ftraster.h | 48 + src/raster1/ftrend1.c | 231 +++ src/raster1/ftrend1.h | 14 + src/raster1/module.mk | 7 + src/raster1/raster1.c | 4 + src/raster1/raster1.h | 14 + src/raster1/rules.mk | 74 + src/smooth/ftgrays.c | 1954 ++++++++++++++++++++++++ src/smooth/ftgrays.h | 49 + src/smooth/ftsmooth.c | 176 +++ src/smooth/ftsmooth.h | 8 + src/smooth/module.mk | 7 + src/smooth/rules.mk | 74 + src/smooth/smooth.c | 4 + src/smooth/smooth.h | 14 + 16 files changed, 5948 insertions(+) create mode 100644 src/raster1/ftraster.c create mode 100644 src/raster1/ftraster.h create mode 100644 src/raster1/ftrend1.c create mode 100644 src/raster1/ftrend1.h create mode 100644 src/raster1/module.mk create mode 100644 src/raster1/raster1.c create mode 100644 src/raster1/raster1.h create mode 100644 src/raster1/rules.mk create mode 100644 src/smooth/ftgrays.c create mode 100644 src/smooth/ftgrays.h create mode 100644 src/smooth/ftsmooth.c create mode 100644 src/smooth/ftsmooth.h create mode 100644 src/smooth/module.mk create mode 100644 src/smooth/rules.mk create mode 100644 src/smooth/smooth.c create mode 100644 src/smooth/smooth.h diff --git a/src/raster1/ftraster.c b/src/raster1/ftraster.c new file mode 100644 index 000000000..6417bdca6 --- /dev/null +++ b/src/raster1/ftraster.c @@ -0,0 +1,3270 @@ +/***************************************************************************/ +/* */ +/* ftraster.c */ +/* */ +/* The FreeType glyph rasterizer (body). */ +/* */ +/* Copyright 1996-2000 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* This is a rewrite of the FreeType 1.x scan-line converter */ + /* */ + /*************************************************************************/ + + +#include "ftraster.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: */ + /* */ + /* 1 - Decomposing the outline into successive `profiles'. Each */ + /* profile is simply an array of scanline intersections on a given */ + /* dimension. A profile's main attributes are */ + /* */ + /* o its scanline position boundaries, i.e. `Ymin' and `Ymax'. */ + /* */ + /* o an array of intersection coordinates for each scanline */ + /* between `Ymin' and `Ymax'. */ + /* */ + /* o a direction, indicating whether it was built going `up' or */ + /* `down', as this is very important for filling rules. */ + /* */ + /* 2 - Sweeping the target map's scanlines in order to compute segment */ + /* `spans' which are then filled. Additionally, this pass */ + /* performs drop-out control. */ + /* */ + /* The outline data is parsed during step 1 only. The profiles are */ + /* built from the bottom of the render pool, used as a stack. The */ + /* following graphics shows the profile list under construction: */ + /* */ + /* ____________________________________________________________ _ _ */ + /* | | | | | */ + /* | profile | coordinates for | profile | coordinates for |--> */ + /* | 1 | profile 1 | 2 | profile 2 |--> */ + /* |_________|___________________|_________|_________________|__ _ _ */ + /* */ + /* ^ ^ */ + /* | | */ + /* start of render pool top */ + /* */ + /* The top of the profile stack is kept in the `top' variable. */ + /* */ + /* As you can see, a profile record is pushed on top of the render */ + /* pool, which is then followed by its coordinates/intersections. If */ + /* a change of direction is detected in the outline, a new profile is */ + /* generated until the end of the outline. */ + /* */ + /* Note that when all profiles have been generated, the function */ + /* Finalize_Profile_Table() is used to record, for each profile, its */ + /* bottom-most scanline as well as the scanline above its upmost */ + /* boundary. These positions are called `y-turns' because they (sort */ + /* of) correspond to local extrema. They are stored in a sorted list */ + /* built from the top of the render pool as a downwards stack: */ + /* */ + /* _ _ _______________________________________ */ + /* | | */ + /* <--| sorted list of | */ + /* <--| extrema scanlines | */ + /* _ _ __________________|____________________| */ + /* */ + /* ^ ^ */ + /* | | */ + /* maxBuff sizeBuff = end of pool */ + /* */ + /* This list is later used during the sweep phase in order to */ + /* optimize performance (see technical note on the sweep below). */ + /* */ + /* Of course, the raster detects whether the two stacks collide and */ + /* handles the situation propertly. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** CONFIGURATION MACROS **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + /* define DEBUG_RASTER if you want to compile a debugging version */ +#define xxxDEBUG_RASTER + + /* The default render pool size in bytes */ +#define RASTER_RENDER_POOL 8192 + + /* undefine FT_RASTER_OPTION_ANTI_ALIASING if you do not want to support */ + /* 5-levels anti-aliasing */ +#ifdef xxxFT_CONFIG_OPTION_5_GRAY_LEVELS +#define FT_RASTER_OPTION_ANTI_ALIASING +#endif + + /* The size of the two-lines intermediate bitmap used */ + /* for anti-aliasing, in bytes. */ +#define RASTER_GRAY_LINES 2048 + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** OTHER MACROS (do not change) **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_raster + + +#ifdef _STANDALONE_ + + + /* This macro is used to indicate that a function parameter is unused. */ + /* Its purpose is simply to reduce compiler warnings. Note also that */ + /* simply defining it as `(void)x' doesn't avoid warnings with certain */ + /* ANSI compilers (e.g. LCC). */ +#define UNUSED( x ) (x) = (x) + + /* Disable the tracing mechanism for simplicity -- developers can */ + /* activate it easily by redefining these two macros. */ +#ifndef FT_ERROR +#define FT_ERROR( x ) do ; while ( 0 ) /* nothing */ +#endif + +#ifndef FT_TRACE +#define FT_TRACE( x ) do ; while ( 0 ) /* nothing */ +#endif + + +#else /* _STANDALONE_ */ + + +#include +#include /* for FT_TRACE() and FT_ERROR() */ + + +#endif /* _STANDALONE_ */ + + +#define Raster_Err_None 0 +#define Raster_Err_Not_Ini -1 +#define Raster_Err_Overflow -2 +#define Raster_Err_Neg_Height -3 +#define Raster_Err_Invalid -4 +#define Raster_Err_Gray_Unsupported -5 +#define Raster_Err_Unsupported -6 + + /* FMulDiv means `Fast MulDiv', it is used in case where `b' is */ + /* typically a small value and the result of a*b is known to fit into */ + /* 32 bits. */ +#define FMulDiv( a, b, c ) ( (a) * (b) / (c) ) + + /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */ + /* for clipping computations. It simply uses the FT_MulDiv() function */ + /* defined in `ftcalc.h'. */ +#define SMulDiv FT_MulDiv + + /* The rasterizer is a very general purpose component; please leave */ + /* the following redefinitions there (you never know your target */ + /* environment). */ + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL (void*)0 +#endif + +#ifndef SUCCESS +#define SUCCESS 0 +#endif + +#ifndef FAILURE +#define FAILURE 1 +#endif + + +#define MaxBezier 32 /* The maximum number of stacked Bezier curves. */ + /* Setting this constant to more than 32 is a */ + /* pure waste of space. */ + +#define Pixel_Bits 6 /* fractional bits of *input* coordinates */ + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** SIMPLE TYPE DECLARATIONS **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + typedef int Int; + typedef unsigned int UInt; + typedef short Short; + typedef unsigned short UShort, *PUShort; + typedef long Long, *PLong; + typedef unsigned long ULong; + + typedef unsigned char Byte, *PByte; + typedef char Bool; + + typedef struct TPoint_ + { + Long x; + Long y; + + } TPoint; + + + typedef enum TFlow_ + { + Flow_None = 0, + Flow_Up = 1, + Flow_Down = -1 + + } TFlow; + + + /* States of each line, arc, and profile */ + typedef enum TStates_ + { + Unknown, + Ascending, + Descending, + Flat + + } TStates; + + + typedef struct TProfile_ TProfile; + typedef TProfile* PProfile; + + struct TProfile_ + { + FT_F26Dot6 X; /* current coordinate during sweep */ + PProfile link; /* link to next profile - various purpose */ + PLong offset; /* start of profile's data in render pool */ + Int flow; /* Profile orientation: Asc/Descending */ + Long height; /* profile's height in scanlines */ + Long start; /* profile's starting scanline */ + + UShort countL; /* number of lines to step before this */ + /* profile becomes drawable */ + + PProfile next; /* next profile in same contour, used */ + /* during drop-out control */ + }; + + typedef PProfile TProfileList; + typedef PProfile* PProfileList; + + + /* Simple record used to implement a stack of bands, required */ + /* by the sub-banding mechanism */ + typedef struct TBand_ + { + Short y_min; /* band's minimum */ + Short y_max; /* band's maximum */ + + } TBand; + + +#define AlignProfileSize \ + ( ( sizeof ( TProfile ) + sizeof ( long ) - 1 ) / sizeof ( long ) ) + + +#ifdef TT_STATIC_RASTER + +#define RAS_ARGS /* void */ +#define RAS_ARG /* void */ + +#define RAS_VARS /* void */ +#define RAS_VAR /* void */ + +#define UNUSED_RASTER do ; while ( 0 ) + +#else /* TT_STATIC_RASTER */ + +#define RAS_ARGS TRaster_Instance* raster, +#define RAS_ARG TRaster_Instance* raster + +#define RAS_VARS raster, +#define RAS_VAR raster + +#define UNUSED_RASTER UNUSED( raster ) + +#endif /* TT_STATIC_RASTER */ + + + typedef struct TRaster_Instance_ TRaster_Instance; + + + /* prototypes used for sweep function dispatch */ + typedef void Function_Sweep_Init( RAS_ARGS Short* min, + Short* max ); + + typedef void Function_Sweep_Span( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ); + + typedef void Function_Sweep_Step( RAS_ARG ); + + + /* NOTE: These operations are only valid on 2's complement processors */ + +#define FLOOR( x ) ( (x) & -ras.precision ) +#define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision ) +#define TRUNC( x ) ( (signed long)(x) >> ras.precision_bits ) +#define FRAC( x ) ( (x) & ( ras.precision - 1 ) ) +#define SCALED( x ) ( ( (x) << ras.scale_shift ) - ras.precision_half ) + + /* Note that I have moved the location of some fields in the */ + /* structure to ensure that the most used variables are used */ + /* at the top. Thus, their offset can be coded with less */ + /* opcodes, and it results in a smaller executable. */ + + struct TRaster_Instance_ + { + Int precision_bits; /* precision related variables */ + Int precision; + Int precision_half; + Long precision_mask; + Int precision_shift; + Int precision_step; + Int precision_jitter; + + Int scale_shift; /* == precision_shift for bitmaps */ + /* == precision_shift+1 for pixmaps */ + + PLong buff; /* The profiles buffer */ + PLong sizeBuff; /* Render pool size */ + PLong maxBuff; /* Profiles buffer size */ + PLong top; /* Current cursor in buffer */ + + FT_Error error; + + Int numTurns; /* number of Y-turns in outline */ + + TPoint* arc; /* current Bezier arc pointer */ + + UShort bWidth; /* target bitmap width */ + PByte bTarget; /* target bitmap buffer */ + PByte gTarget; /* target pixmap buffer */ + + Long lastX, lastY, minY, maxY; + + UShort num_Profs; /* current number of profiles */ + + Bool fresh; /* signals a fresh new profile which */ + /* 'start' field must be completed */ + Bool joint; /* signals that the last arc ended */ + /* exactly on a scanline. Allows */ + /* removal of doublets */ + PProfile cProfile; /* current profile */ + PProfile fProfile; /* head of linked list of profiles */ + PProfile gProfile; /* contour's first profile in case */ + /* of impact */ + TStates state; /* rendering state */ + + FT_Bitmap target; /* description of target bit/pixmap */ + FT_Outline outline; + + Long traceOfs; /* current offset in target bitmap */ + Long traceG; /* current offset in target pixmap */ + + Short traceIncr; /* sweep's increment in target bitmap */ + + Short gray_min_x; /* current min x during gray rendering */ + Short gray_max_x; /* current max x during gray rendering */ + + /* dispatch variables */ + + Function_Sweep_Init* Proc_Sweep_Init; + Function_Sweep_Span* Proc_Sweep_Span; + Function_Sweep_Span* Proc_Sweep_Drop; + Function_Sweep_Step* Proc_Sweep_Step; + + Byte dropOutControl; /* current drop_out control method */ + + Bool second_pass; /* indicates 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. */ + TPoint arcs[2 * MaxBezier + 1]; /* The Bezier stack */ + + TBand band_stack[16]; /* band stack used for sub-banding */ + Int band_top; /* band stack top */ + + Int count_table[256]; /* Look-up table used to quickly count */ + /* set bits in a gray 2x2 cell */ + + void* memory; + +#ifdef FT_RASTER_OPTION_ANTI_ALIASING + + Byte grays[5]; /* Palette of gray levels used for */ + /* render. */ + + Byte gray_lines[RASTER_GRAY_LINES]; + /* Intermediate table used to render the */ + /* graylevels pixmaps. */ + /* gray_lines is a buffer holding two */ + /* monochrome scanlines */ + Short gray_width; /* width in bytes of one monochrome */ + /* intermediate scanline of gray_lines. */ + /* Each gray pixel takes 2 bits long there */ + + /* The gray_lines must hold 2 lines, thus with size */ + /* in bytes of at least `gray_width*2'. */ + +#endif /* FT_RASTER_ANTI_ALIASING */ + +#if 0 + PByte flags; /* current flags table */ + PUShort outs; /* current outlines table */ + FT_Vector* coords; + + UShort nPoints; /* number of points in current glyph */ + Short nContours; /* number of contours in current glyph */ +#endif + + }; + + +#ifdef FT_CONFIG_OPTION_STATIC_RASTER + + static TRaster_Instance cur_ras; +#define ras cur_ras + +#else + +#define ras (*raster) + +#endif /* FT_CONFIG_OPTION_STATIC_RASTER */ + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** PROFILES COMPUTATION **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* */ + /* 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; + } + + FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" )); + + ras.precision = 1L << 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 :: The state/orientation of the new profile. */ + /* */ + /* */ + /* SUCCESS on success. FAILURE in case of overflow or of incoherent */ + /* profile. */ + /* */ + static + Bool New_Profile( RAS_ARGS TStates aState ) + { + if ( !ras.fProfile ) + { + ras.cProfile = (PProfile)ras.top; + ras.fProfile = ras.cProfile; + ras.top += AlignProfileSize; + } + + if ( ras.top >= ras.maxBuff ) + { + ras.error = Raster_Err_Overflow; + return FAILURE; + } + + switch ( aState ) + { + case Ascending: + ras.cProfile->flow = Flow_Up; + FT_TRACE6(( "New ascending profile = %lx\n", (long)ras.cProfile )); + break; + + case Descending: + ras.cProfile->flow = Flow_Down; + FT_TRACE6(( "New descending profile = %lx\n", (long)ras.cProfile )); + break; + + default: + FT_ERROR(( "New_Profile: invalid profile direction!\n" )); + ras.error = Raster_Err_Invalid; + return FAILURE; + } + + ras.cProfile->start = 0; + ras.cProfile->height = 0; + ras.cProfile->offset = ras.top; + ras.cProfile->link = (PProfile)0; + ras.cProfile->next = (PProfile)0; + + if ( !ras.gProfile ) + ras.gProfile = ras.cProfile; + + ras.state = aState; + ras.fresh = TRUE; + ras.joint = FALSE; + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* */ + /* End_Profile */ + /* */ + /* */ + /* Finalizes the current profile. */ + /* */ + /* */ + /* SUCCESS on success. FAILURE in case of overflow or incoherency. */ + /* */ + static + Bool End_Profile( RAS_ARG ) + { + Long h; + PProfile oldProfile; + + + h = ras.top - ras.cProfile->offset; + + if ( h < 0 ) + { + FT_ERROR(( "End_Profile: negative height encountered!\n" )); + ras.error = Raster_Err_Neg_Height; + return FAILURE; + } + + if ( h > 0 ) + { + FT_TRACE6(( "Ending profile %lx, start = %ld, height = %ld\n", + (long)ras.cProfile, ras.cProfile->start, h )); + + oldProfile = ras.cProfile; + ras.cProfile->height = h; + ras.cProfile = (PProfile)ras.top; + + ras.top += AlignProfileSize; + + ras.cProfile->height = 0; + ras.cProfile->offset = ras.top; + oldProfile->next = ras.cProfile; + ras.num_Profs++; + } + + if ( ras.top >= ras.maxBuff ) + { + FT_TRACE1(( "overflow in End_Profile\n" )); + ras.error = Raster_Err_Overflow; + return FAILURE; + } + + ras.joint = FALSE; + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* */ + /* Insert_Y_Turn */ + /* */ + /* */ + /* Inserts a salient into the sorted list placed on top of the render */ + /* pool. */ + /* */ + /* */ + /* New y scanline position. */ + /* */ + /* */ + /* SUCCESS on success. FAILURE in case of overflow. */ + /* */ + static + Bool Insert_Y_Turn( RAS_ARGS Int y ) + { + PLong y_turns; + Int y2, n; + + + n = ras.numTurns - 1; + y_turns = ras.sizeBuff - ras.numTurns; + + /* look for first y value that is <= */ + while ( n >= 0 && y < y_turns[n] ) + n--; + + /* if it is <, simply insert it, ignore if == */ + if ( n >= 0 && y > y_turns[n] ) + while ( n >= 0 ) + { + y2 = y_turns[n]; + y_turns[n] = y; + y = y2; + n--; + } + + if ( n < 0 ) + { + if ( ras.maxBuff <= ras.top ) + { + ras.error = Raster_Err_Overflow; + return FAILURE; + } + ras.maxBuff--; + ras.numTurns++; + ras.sizeBuff[-ras.numTurns] = y; + } + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* */ + /* Finalize_Profile_Table */ + /* */ + /* */ + /* Adjusts all links in the profiles list. */ + /* */ + /* */ + /* SUCCESS on success. FAILURE in case of overflow. */ + /* */ + static + Bool Finalize_Profile_Table( RAS_ARG ) + { + Int bottom, top; + UShort n; + PProfile p; + + + n = ras.num_Profs; + + if ( n > 1 ) + { + p = ras.fProfile; + while ( n > 0 ) + { + if ( n > 1 ) + p->link = (PProfile)( p->offset + p->height ); + else + p->link = NULL; + + switch ( p->flow ) + { + case Flow_Down: + bottom = p->start - p->height+1; + top = p->start; + p->start = bottom; + p->offset += p->height - 1; + break; + + case 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.fProfile = NULL; + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* */ + /* Split_Conic */ + /* */ + /* */ + /* Subdivides one conic Bezier into two joint sub-arcs in the Bezier */ + /* stack. */ + /* */ + /* */ + /* None (subdivided Bezier is taken from the top of the stack). */ + /* */ + /* */ + /* This routine is the `beef' of this component. It is _the_ inner */ + /* loop that should be optimized to hell to get the best performance. */ + /* */ + static + void Split_Conic( TPoint* base ) + { + Long a, b; + + + base[4].x = base[2].x; + b = base[1].x; + a = base[3].x = ( base[2].x + b ) / 2; + b = base[1].x = ( base[0].x + b ) / 2; + base[2].x = ( a + b ) / 2; + + base[4].y = base[2].y; + b = base[1].y; + a = base[3].y = ( base[2].y + b ) / 2; + b = base[1].y = ( base[0].y + b ) / 2; + base[2].y = ( a + b ) / 2; + + /* hand optimized. gcc doesn't seem too good at common expression */ + /* substitution and instruction scheduling ;-) */ + } + + + /*************************************************************************/ + /* */ + /* */ + /* 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 ) + { + Long a, b, c, d; + + + base[6].x = base[3].x; + c = base[1].x; + d = base[2].x; + base[1].x = a = ( base[0].x + c + 1 ) >> 1; + base[5].x = b = ( base[3].x + d + 1 ) >> 1; + c = ( c + d + 1 ) >> 1; + base[2].x = a = ( a + c + 1 ) >> 1; + base[4].x = b = ( b + c + 1 ) >> 1; + base[3].x = ( a + b + 1 ) >> 1; + + base[6].y = base[3].y; + c = base[1].y; + d = base[2].y; + base[1].y = a = ( base[0].y + c + 1 ) >> 1; + base[5].y = b = ( base[3].y + d + 1 ) >> 1; + c = ( c + d + 1 ) >> 1; + base[2].y = a = ( a + c + 1 ) >> 1; + base[4].y = b = ( b + c + 1 ) >> 1; + base[3].y = ( a + b + 1 ) >> 1; + } + + + /*************************************************************************/ + /* */ + /* */ + /* Line_Up */ + /* */ + /* */ + /* Computes the x-coordinates of an ascending line segment and stores */ + /* them in the render pool. */ + /* */ + /* */ + /* 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. */ + /* */ + /* */ + /* 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 = 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.top--; + ras.joint = FALSE; + } + + ras.joint = ( f2 == 0 ); + + if ( ras.fresh ) + { + ras.cProfile->start = e1; + ras.fresh = FALSE; + } + + size = e2 - e1 + 1; + if ( ras.top + size >= ras.maxBuff ) + { + ras.error = Raster_Err_Overflow; + return FAILURE; + } + + if ( Dx > 0 ) + { + Ix = ( ras.precision * Dx ) / Dy; + Rx = ( ras.precision * Dx ) % Dy; + Dx = 1; + } + else + { + Ix = -( ( ras.precision * -Dx ) / Dy ); + Rx = ( ras.precision * -Dx ) % Dy; + Dx = -1; + } + + Ax = -Dy; + top = ras.top; + + while ( size > 0 ) + { + *top++ = x1; + + x1 += Ix; + Ax += Rx; + if ( Ax >= 0 ) + { + Ax -= Dy; + x1 += Dx; + } + size--; + } + + ras.top = top; + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* */ + /* Line_Down */ + /* */ + /* */ + /* Computes the x-coordinates of an descending line segment and */ + /* stores them in the render pool. */ + /* */ + /* */ + /* 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. */ + /* */ + /* */ + /* 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 ); + + + /*************************************************************************/ + /* */ + /* */ + /* Bezier_Up */ + /* */ + /* */ + /* Computes the x-coordinates of an ascending Bezier arc and stores */ + /* them in the render pool. */ + /* */ + /* */ + /* 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. */ + /* */ + /* */ + /* SUCCESS on success, FAILURE on render pool overflow. */ + /* */ + static + Bool Bezier_Up( RAS_ARGS Int degree, + TSplitter splitter, + Long miny, + Long maxy ) + { + Long y1, y2, e, e2, e0; + Short f1; + + TPoint* arc; + TPoint* start_arc; + + PLong top; + + + arc = ras.arc; + y1 = arc[degree].y; + y2 = arc[0].y; + top = ras.top; + + if ( y2 < miny || y1 > maxy ) + goto Fin; + + e2 = FLOOR( y2 ); + + if ( e2 > maxy ) + e2 = maxy; + + e0 = miny; + + if ( y1 < miny ) + e = miny; + else + { + e = CEILING( y1 ); + f1 = FRAC( y1 ); + e0 = e; + + if ( f1 == 0 ) + { + if ( ras.joint ) + { + top--; + ras.joint = FALSE; + } + + *top++ = arc[degree].x; + + e += ras.precision; + } + } + + if ( ras.fresh ) + { + ras.cProfile->start = TRUNC( e0 ); + ras.fresh = FALSE; + } + + if ( e2 < e ) + goto Fin; + + if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff ) + { + ras.top = top; + ras.error = Raster_Err_Overflow; + return FAILURE; + } + + start_arc = arc; + + while ( arc >= start_arc && e <= e2 ) + { + ras.joint = FALSE; + + y2 = arc[0].y; + + if ( y2 > e ) + { + y1 = arc[degree].y; + if ( y2 - y1 >= ras.precision_step ) + { + splitter( arc ); + arc += degree; + } + else + { + *top++ = arc[degree].x + FMulDiv( arc[0].x-arc[degree].x, + e - y1, y2 - y1 ); + arc -= degree; + e += ras.precision; + } + } + else + { + if ( y2 == e ) + { + ras.joint = TRUE; + *top++ = arc[0].x; + + e += ras.precision; + } + arc -= degree; + } + } + + Fin: + ras.top = top; + ras.arc -= degree; + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* */ + /* Bezier_Down */ + /* */ + /* */ + /* Computes the x-coordinates of an descending Bezier arc and stores */ + /* them in the render pool. */ + /* */ + /* */ + /* 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. */ + /* */ + /* */ + /* SUCCESS on success, FAILURE on render pool overflow. */ + /* */ + static + Bool Bezier_Down( RAS_ARGS Int degree, + TSplitter splitter, + Long miny, + Long maxy ) + { + TPoint* arc = ras.arc; + Bool result, fresh; + + + arc[0].y = -arc[0].y; + arc[1].y = -arc[1].y; + arc[2].y = -arc[2].y; + if ( degree > 2 ) + arc[3].y = -arc[3].y; + + fresh = ras.fresh; + + result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny ); + + if ( fresh && !ras.fresh ) + ras.cProfile->start = -ras.cProfile->start; + + arc[0].y = -arc[0].y; + return result; + } + + + /*************************************************************************/ + /* */ + /* */ + /* Line_To */ + /* */ + /* */ + /* Injects a new line segment and adjusts Profiles list. */ + /* */ + /* */ + /* 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'). */ + /* */ + /* */ + /* 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: + if ( y > ras.lastY ) + { + if ( New_Profile( RAS_VARS Ascending ) ) + return FAILURE; + } + else + { + if ( y < ras.lastY ) + if ( New_Profile( RAS_VARS Descending ) ) + return FAILURE; + } + break; + + case Ascending: + if ( y < ras.lastY ) + { + if ( End_Profile( RAS_VAR ) || + New_Profile( RAS_VARS Descending ) ) + return FAILURE; + } + break; + + case Descending: + if ( y > ras.lastY ) + { + 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.lastX, ras.lastY, + x, y, ras.minY, ras.maxY ) ) + return FAILURE; + break; + + case Descending: + 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; + } + + + /*************************************************************************/ + /* */ + /* */ + /* Conic_To */ + /* */ + /* */ + /* Injects a new conic arc and adjusts the profile list. */ + /* */ + /* */ + /* 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'). */ + /* */ + /* */ + /* SUCCESS on success, FAILURE on render pool overflow or incorrect */ + /* profile. */ + /* */ + static + Bool Conic_To( RAS_ARGS Long cx, + Long cy, + Long x, + Long y ) + { + Long y1, y2, y3, x3, ymin, ymax; + TStates state_bez; + + + ras.arc = ras.arcs; + ras.arc[2].x = ras.lastX; + ras.arc[2].y = ras.lastY; + ras.arc[1].x = cx; ras.arc[1].y = cy; + ras.arc[0].x = x; ras.arc[0].y = y; + + do + { + y1 = ras.arc[2].y; + y2 = ras.arc[1].y; + y3 = ras.arc[0].y; + x3 = ras.arc[0].x; + + /* first, categorize the Bezier arc */ + + if ( y1 <= y3 ) + { + ymin = y1; + ymax = y3; + } + else + { + ymin = y3; + ymax = y1; + } + + if ( y2 < ymin || y2 > ymax ) + { + /* this arc has no given direction, split it! */ + Split_Conic( ras.arc ); + ras.arc += 2; + } + else if ( y1 == y3 ) + { + /* this arc is flat, ignore it and pop it from the Bezier stack */ + ras.arc -= 2; + } + else + { + /* the arc is y-monotonous, either ascending or descending */ + /* detect a change of direction */ + state_bez = y1 < y3 ? Ascending : Descending; + if ( ras.state != state_bez ) + { + /* finalize current profile if any */ + if ( ras.state != Unknown && + End_Profile( RAS_VAR ) ) + goto Fail; + + /* create a new profile */ + if ( New_Profile( RAS_VARS state_bez ) ) + goto Fail; + } + + /* now call the appropriate routine */ + if ( state_bez == Ascending ) + { + if ( Bezier_Up( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) ) + goto Fail; + } + else + if ( Bezier_Down( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) ) + goto Fail; + } + + } while ( ras.arc >= ras.arcs ); + + ras.lastX = x3; + ras.lastY = y3; + + return SUCCESS; + + Fail: + return FAILURE; + } + + + /*************************************************************************/ + /* */ + /* */ + /* Cubic_To */ + /* */ + /* */ + /* Injects a new cubic arc and adjusts the profile list. */ + /* */ + /* */ + /* 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'). */ + /* */ + /* */ + /* SUCCESS on success, FAILURE on render pool overflow or incorrect */ + /* profile. */ + /* */ + static + Bool Cubic_To( RAS_ARGS Long cx1, + Long cy1, + Long cx2, + Long cy2, + Long x, + Long y ) + { + Long y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2; + TStates state_bez; + + + ras.arc = ras.arcs; + ras.arc[3].x = ras.lastX; + ras.arc[3].y = ras.lastY; + ras.arc[2].x = cx1; ras.arc[2].y = cy1; + ras.arc[1].x = cx2; ras.arc[1].y = cy2; + ras.arc[0].x = x; ras.arc[0].y = y; + + do + { + y1 = ras.arc[3].y; + y2 = ras.arc[2].y; + y3 = ras.arc[1].y; + y4 = ras.arc[0].y; + x4 = ras.arc[0].x; + + /* first, categorize the Bezier arc */ + + if ( y1 <= y4 ) + { + ymin1 = y1; + ymax1 = y4; + } + else + { + ymin1 = y4; + ymax1 = y1; + } + + if ( y2 <= y3 ) + { + ymin2 = y2; + ymax2 = y3; + } + else + { + ymin2 = y3; + ymax2 = y2; + } + + if ( ymin2 < ymin1 || ymax2 > ymax1 ) + { + /* this arc has no given direction, split it! */ + Split_Cubic( ras.arc ); + ras.arc += 3; + } + else if ( y1 == y4 ) + { + /* this arc is flat, ignore it and pop it from the Bezier stack */ + ras.arc -= 3; + } + else + { + state_bez = y1 <= y4 ? Ascending : Descending; + + /* detect a change of direction */ + if ( ras.state != state_bez ) + { + if ( ras.state != Unknown && + End_Profile( RAS_VAR ) ) + goto Fail; + + if ( New_Profile( RAS_VARS state_bez ) ) + goto Fail; + } + + /* compute intersections */ + if ( state_bez == Ascending ) + { + if ( Bezier_Up( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) ) + goto Fail; + } + else + if ( Bezier_Down( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) ) + goto Fail; + } + + } while ( ras.arc >= ras.arcs ); + + ras.lastX = x4; + ras.lastY = y4; + + return SUCCESS; + + Fail: + return FAILURE; + } + + +#undef SWAP_ +#define SWAP_( x, y ) do \ + { \ + Long swap = x; \ + \ + \ + x = y; \ + y = swap; \ + } while ( 0 ) + + + /*************************************************************************/ + /* */ + /* */ + /* Decompose_Curve */ + /* */ + /* */ + /* Scans the outline arays 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! */ + /* */ + /* */ + /* 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. */ + /* */ + /* */ + /* 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; + + char tag; /* current point's state */ + + + points = ras.outline.points; + limit = points + last; + + v_start.x = SCALED( points[first].x ); + v_start.y = SCALED( points[first].y ); + v_last.x = SCALED( points[last].x ); + v_last.y = SCALED( points[last].y ); + + if ( flipped ) + { + SWAP_( v_start.x, v_start.y ); + SWAP_( v_last.x, v_last.y ); + } + + v_control = v_start; + + point = points + first; + tags = ras.outline.tags + first; + tag = FT_CURVE_TAG( tags[0] ); + + /* A contour cannot start with a cubic control point! */ + if ( tag == FT_Curve_Tag_Cubic ) + goto Invalid_Outline; + + /* check first point to determine origin */ + if ( tag == FT_Curve_Tag_Conic ) + { + /* first point is conic control. Yes, this happens. */ + if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_Curve_Tag_On ) + { + /* start at last point if it is on the curve */ + v_start = v_last; + limit--; + } + else + { + /* if both first and last points are conic, */ + /* start at their middle and record its position */ + /* for closure */ + v_start.x = ( v_start.x + v_last.x ) / 2; + v_start.y = ( v_start.y + v_last.y ) / 2; + + v_last = v_start; + } + point--; + tags--; + } + + ras.lastX = v_start.x; + ras.lastY = v_start.y; + + while ( point < limit ) + { + point++; + tags++; + + tag = FT_CURVE_TAG( tags[0] ); + + switch ( tag ) + { + case FT_Curve_Tag_On: /* emit a single line_to */ + { + Long x, y; + + + x = SCALED( point->x ); + y = SCALED( point->y ); + if ( flipped ) + SWAP_( x, y ); + + if ( Line_To( RAS_VARS x, y ) ) + goto Fail; + continue; + } + + case FT_Curve_Tag_Conic: /* consume conic arcs */ + { + v_control.x = SCALED( point[0].x ); + v_control.y = SCALED( point[0].y ); + + if ( flipped ) + SWAP_( v_control.x, v_control.y ); + + Do_Conic: + if ( point < limit ) + { + FT_Vector v_middle; + Long x, y; + + + point++; + tags++; + tag = FT_CURVE_TAG( tags[0] ); + + x = SCALED( point[0].x ); + y = SCALED( point[0].y ); + + if ( flipped ) + SWAP_( x, y ); + + if ( tag == FT_Curve_Tag_On ) + { + if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) ) + goto Fail; + continue; + } + + if ( tag != FT_Curve_Tag_Conic ) + goto Invalid_Outline; + + v_middle.x = ( v_control.x + x ) / 2; + v_middle.y = ( v_control.y + y ) / 2; + + if ( Conic_To( RAS_VARS v_control.x, v_control.y, + v_middle.x, v_middle.y ) ) + goto Fail; + + v_control.x = x; + v_control.y = y; + + goto Do_Conic; + } + + if ( Conic_To( RAS_VARS v_control.x, v_control.y, + v_start.x, v_start.y ) ) + goto Fail; + + goto Close; + } + + default: /* FT_Curve_Tag_Cubic */ + { + Long x1, y1, x2, y2, x3, y3; + + + if ( point + 1 > limit || + FT_CURVE_TAG( tags[1] ) != FT_Curve_Tag_Cubic ) + goto Invalid_Outline; + + point += 2; + tags += 2; + + x1 = SCALED( point[-2].x ); + y1 = SCALED( point[-2].y ); + x2 = SCALED( point[-1].x ); + y2 = SCALED( point[-1].y ); + x3 = SCALED( point[ 0].x ); + y3 = SCALED( point[ 0].y ); + + if ( flipped ) + { + SWAP_( x1, y1 ); + SWAP_( x2, y2 ); + SWAP_( x3, y3 ); + } + + if ( point <= limit ) + { + if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) ) + goto Fail; + continue; + } + + if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) ) + goto Fail; + goto Close; + } + } + } + + /* close the contour with a line segment */ + if ( Line_To( RAS_VARS v_start.x, v_start.y ) ) + goto Fail; + + Close: + return SUCCESS; + + Invalid_Outline: + ras.error = Raster_Err_Invalid; + + Fail: + return FAILURE; + } + + + /*************************************************************************/ + /* */ + /* */ + /* Convert_Glyph */ + /* */ + /* */ + /* Converts a glyph into a series of segments and arcs and makes a */ + /* profiles list with them. */ + /* */ + /* */ + /* flipped :: If set, flip the direction of curve. */ + /* */ + /* */ + /* SUCCESS on success, FAILURE if any error was encountered during */ + /* rendering. */ + /* */ + static + Bool Convert_Glyph( RAS_ARGS int flipped ) + { + Short i; + UShort start; + + PProfile lastProfile; + + + ras.fProfile = NULL; + ras.joint = FALSE; + ras.fresh = FALSE; + + ras.maxBuff = ras.sizeBuff - AlignProfileSize; + + ras.numTurns = 0; + + ras.cProfile = (PProfile)ras.top; + ras.cProfile->offset = ras.top; + ras.num_Profs = 0; + + start = 0; + + for ( i = 0; i < ras.outline.n_contours; i++ ) + { + ras.state = Unknown; + ras.gProfile = NULL; + + if ( Decompose_Curve( RAS_VARS start, ras.outline.contours[i], flipped ) ) + return FAILURE; + + start = ras.outline.contours[i] + 1; + + /* We must now see whether the extreme arcs join or not */ + if ( FRAC( ras.lastY ) == 0 && + ras.lastY >= ras.minY && + ras.lastY <= ras.maxY ) + if ( ras.gProfile && ras.gProfile->flow == ras.cProfile->flow ) + ras.top--; + /* Note that ras.gProfile can be nil if the contour was too small */ + /* to be drawn. */ + + lastProfile = ras.cProfile; + if ( End_Profile( RAS_VAR ) ) + return FAILURE; + + /* close the `next profile in contour' linked list */ + if ( ras.gProfile ) + lastProfile->next = ras.gProfile; + } + + if ( Finalize_Profile_Table( RAS_VAR ) ) + return FAILURE; + + return ( ras.top < ras.maxBuff ? SUCCESS : FAILURE ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** SCAN-LINE SWEEPS AND DRAWING **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Init_Linked */ + /* */ + /* Initializes an empty linked list. */ + /* */ + static + void Init_Linked( TProfileList* l ) + { + *l = NULL; + } + + + /*************************************************************************/ + /* */ + /* InsNew */ + /* */ + /* Inserts a new profile in a linked list. */ + /* */ + static + void InsNew( PProfileList list, + PProfile profile ) + { + PProfile *old, current; + Long x; + + + old = list; + current = *old; + x = profile->X; + + while ( current ) + { + if ( x < current->X ) + break; + old = ¤t->link; + current = *old; + } + + profile->link = current; + *old = profile; + } + + + /*************************************************************************/ + /* */ + /* DelOld */ + /* */ + /* Removes an old profile from a linked list. */ + /* */ + static + void DelOld( PProfileList list, + PProfile profile ) + { + PProfile *old, current; + + + old = list; + current = *old; + + while ( current ) + { + if ( current == profile ) + { + *old = current->link; + return; + } + + old = ¤t->link; + current = *old; + } + + /* we should never get there, unless the profile was not part of */ + /* the list. */ + } + + + /*************************************************************************/ + /* */ + /* 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 Sweep Procedure Set */ + /* */ + /* These four routines are used during the vertical black/white */ + /* sweep phase by the generic Draw_Sweep() function. */ + /* */ + /*************************************************************************/ + + static + void Vertical_Sweep_Init( RAS_ARGS Short* min, + Short* max ) + { + Long pitch = ras.target.pitch; + + UNUSED( max ); + + + ras.traceIncr = (Short)-pitch; + ras.traceOfs = -*min * pitch; + if ( pitch > 0 ) + ras.traceOfs += ( ras.target.rows - 1 ) * pitch; + + ras.gray_min_x = 0; + ras.gray_max_x = 0; + } + + + static + void Vertical_Sweep_Span( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + Long e1, e2; + Short c1, c2; + Byte f1, f2; + Byte* target; + + UNUSED( y ); + UNUSED( left ); + UNUSED( right ); + + + /* Drop-out control */ + + e1 = TRUNC( CEILING( x1 ) ); + + if ( x2 - x1 - ras.precision <= ras.precision_jitter ) + e2 = e1; + else + e2 = TRUNC( FLOOR( x2 ) ); + + if ( e2 >= 0 && e1 < ras.bWidth ) + { + if ( e1 < 0 ) e1 = 0; + if ( e2 >= ras.bWidth ) e2 = ras.bWidth - 1; + + c1 = (Short)( e1 >> 3 ); + c2 = (Short)( e2 >> 3 ); + + f1 = (unsigned char)0xFF >> ( e1 & 7 ); + f2 = ~((unsigned char)0x7F >> ( e2 & 7 )); + + if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1; + if ( ras.gray_max_x < c2 ) ras.gray_max_x = c2; + + target = ras.bTarget + ras.traceOfs + c1; + c2 -= c1; + + if ( c2 > 0 ) + { + target[0] |= f1; + + /* memset() is slower than the following code on many platforms. */ + /* This is due to the fact that, in the vast majority of cases, */ + /* the span length in bytes is relatively small. */ + c2--; + while ( c2 > 0 ) + { + *(++target) = 0xFF; + c2--; + } + target[1] |= f2; + } + else + *target |= ( f1 & f2 ); + } + } + + + static + void Vertical_Sweep_Drop( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + Long e1, e2; + Short c1, f1; + + + /* Drop-out control */ + + e1 = CEILING( x1 ); + e2 = FLOOR ( x2 ); + + if ( e1 > e2 ) + { + if ( e1 == e2 + ras.precision ) + { + switch ( ras.dropOutControl ) + { + case 1: + e1 = e2; + break; + + case 4: + e1 = CEILING( (x1 + x2 + 1) / 2 ); + break; + + case 2: + case 5: + /* Drop-out Control Rule #4 */ + + /* The spec is not very clear regarding rule #4. It */ + /* presents a method that is way too costly to implement */ + /* while the general idea seems to get rid of `stubs'. */ + /* */ + /* Here, we only get rid of stubs recognized if: */ + /* */ + /* upper stub: */ + /* */ + /* - P_Left and P_Right are in the same contour */ + /* - P_Right is the successor of P_Left in that contour */ + /* - y is the top of P_Left and P_Right */ + /* */ + /* lower stub: */ + /* */ + /* - P_Left and P_Right are in the same contour */ + /* - P_Left is the successor of P_Right in that contour */ + /* - y is the bottom of P_Left */ + /* */ + + /* FIXXXME: uncommenting this line solves the disappearing */ + /* bit problem in the `7' of verdana 10pts, but */ + /* makes a new one in the `C' of arial 14pts */ + +#if 0 + if ( x2 - x1 < ras.precision_half ) +#endif + { + /* upper stub test */ + if ( left->next == right && left->height <= 0 ) + return; + + /* lower stub test */ + if ( right->next == left && left->start == y ) + return; + } + + /* check that the rightmost pixel isn't set */ + + e1 = TRUNC( e1 ); + + c1 = (Short)( e1 >> 3 ); + f1 = e1 & 7; + + if ( e1 >= 0 && e1 < ras.bWidth && + ras.bTarget[ras.traceOfs + c1] & ( 0x80 >> f1 ) ) + return; + + if ( ras.dropOutControl == 2 ) + e1 = e2; + else + e1 = CEILING( ( x1 + x2 + 1 ) / 2 ); + + break; + + default: + return; /* unsupported mode */ + } + } + else + return; + } + + e1 = TRUNC( e1 ); + + if ( e1 >= 0 && e1 < ras.bWidth ) + { + c1 = (Short)( e1 >> 3 ); + f1 = e1 & 7; + + if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1; + if ( ras.gray_max_x < c1 ) ras.gray_max_x = c1; + + ras.bTarget[ras.traceOfs + c1] |= (char)( 0x80 >> f1 ); + } + } + + + static + void Vertical_Sweep_Step( RAS_ARG ) + { + ras.traceOfs += ras.traceIncr; + } + + + /***********************************************************************/ + /* */ + /* Horizontal Sweep Procedure Set */ + /* */ + /* These four routines are used during the horizontal black/white */ + /* sweep phase by the generic Draw_Sweep() function. */ + /* */ + /***********************************************************************/ + + static + void Horizontal_Sweep_Init( RAS_ARGS Short* min, + Short* max ) + { + /* nothing, really */ + UNUSED( raster ); + UNUSED( min ); + UNUSED( max ); + } + + + static + void Horizontal_Sweep_Span( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + Long e1, e2; + PByte bits; + Byte f1; + + UNUSED( left ); + UNUSED( right ); + + + if ( x2 - x1 < ras.precision ) + { + e1 = CEILING( x1 ); + e2 = FLOOR ( x2 ); + + if ( e1 == e2 ) + { + bits = ras.bTarget + ( y >> 3 ); + f1 = (Byte)( 0x80 >> ( y & 7 ) ); + + e1 = TRUNC( e1 ); + + if ( e1 >= 0 && e1 < ras.target.rows ) + { + PByte p; + + + p = bits - e1*ras.target.pitch; + if ( ras.target.pitch > 0 ) + p += ( ras.target.rows - 1 ) * ras.target.pitch; + + p[0] |= f1; + } + } + } + } + + + static + void Horizontal_Sweep_Drop( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + Long e1, e2; + PByte bits; + Byte f1; + + + /* During the horizontal sweep, we only take care of drop-outs */ + + e1 = CEILING( x1 ); + e2 = FLOOR ( x2 ); + + if ( e1 > e2 ) + { + if ( e1 == e2 + ras.precision ) + { + switch ( ras.dropOutControl ) + { + case 1: + e1 = e2; + break; + + case 4: + e1 = CEILING( ( x1 + x2 + 1 ) / 2 ); + break; + + case 2: + case 5: + + /* Drop-out Control Rule #4 */ + + /* The spec is not very clear regarding rule #4. It */ + /* presents a method that is way too costly to implement */ + /* while the general idea seems to get rid of `stubs'. */ + /* */ + + /* rightmost stub test */ + if ( left->next == right && left->height <= 0 ) + return; + + /* leftmost stub test */ + if ( right->next == left && left->start == y ) + return; + + /* check that the rightmost pixel isn't set */ + + e1 = TRUNC( e1 ); + + bits = ras.bTarget + ( y >> 3 ); + f1 = (Byte)( 0x80 >> ( y & 7 ) ); + + bits -= e1 * ras.target.pitch; + if ( ras.target.pitch > 0 ) + bits += ( ras.target.rows - 1 ) * ras.target.pitch; + + if ( e1 >= 0 && + e1 < ras.target.rows && + *bits & f1 ) + return; + + if ( ras.dropOutControl == 2 ) + e1 = e2; + else + e1 = CEILING( ( x1 + x2 + 1 ) / 2 ); + + break; + + default: + return; /* unsupported mode */ + } + } + else + return; + } + + bits = ras.bTarget + ( y >> 3 ); + f1 = (Byte)( 0x80 >> ( y & 7 ) ); + + e1 = TRUNC( e1 ); + + if ( e1 >= 0 && e1 < ras.target.rows ) + { + bits -= e1 * ras.target.pitch; + if ( ras.target.pitch > 0 ) + bits += ( ras.target.rows - 1 ) * ras.target.pitch; + + bits[0] |= f1; + } + } + + + static + void Horizontal_Sweep_Step( RAS_ARG ) + { + /* Nothing, really */ + UNUSED( raster ); + } + + +#ifdef FT_RASTER_OPTION_ANTI_ALIASING + + + /*************************************************************************/ + /* */ + /* Vertical Gray Sweep Procedure Set */ + /* */ + /* These two routines are used during the vertical gray-levels sweep */ + /* phase by the generic Draw_Sweep() function. */ + /* */ + /* NOTES */ + /* */ + /* - The target pixmap's width *must* be a multiple of 4. */ + /* */ + /* - You have to use the function Vertical_Sweep_Span() for the gray */ + /* span call. */ + /* */ + /*************************************************************************/ + + static + void Vertical_Gray_Sweep_Init( RAS_ARGS Short* min, + Short* max ) + { + Long pitch, byte_len; + + + *min = *min & -2; + *max = ( *max + 3 ) & -2; + + ras.traceOfs = 0; + pitch = ras.target.pitch; + byte_len = -pitch; + ras.traceIncr = (Short)byte_len; + ras.traceG = ( *min / 2 ) * byte_len; + + if ( pitch > 0 ) + { + ras.traceG += ( ras.target.rows - 1 ) * pitch; + byte_len = -byte_len; + } + + ras.gray_min_x = (Short)byte_len; + ras.gray_max_x = -(Short)byte_len; + } + + + static + void Vertical_Gray_Sweep_Step( RAS_ARG ) + { + Int c1, c2; + PByte pix, bit, bit2; + Int* count = ras.count_table; + Byte* grays; + + + ras.traceOfs += ras.gray_width; + + if ( ras.traceOfs > ras.gray_width ) + { + pix = ras.gTarget + ras.traceG + ras.gray_min_x * 4; + grays = ras.grays; + + if ( ras.gray_max_x >= 0 ) + { + Long last_pixel = ras.target.width - 1; + Int last_cell = last_pixel >> 2; + Int last_bit = last_pixel & 3; + Bool over = 0; + + if ( ras.gray_max_x >= last_cell && last_bit != 3 ) + { + ras.gray_max_x = last_cell - 1; + over = 1; + } + + if ( ras.gray_min_x < 0 ) + ras.gray_min_x = 0; + + bit = ras.bTarget + ras.gray_min_x; + bit2 = bit + ras.gray_width; + + c1 = ras.gray_max_x - ras.gray_min_x; + + while ( c1 >= 0 ) + { + c2 = count[*bit] + count[*bit2]; + + if ( c2 ) + { + pix[0] = grays[(c2 >> 12) & 0x000F]; + pix[1] = grays[(c2 >> 8 ) & 0x000F]; + pix[2] = grays[(c2 >> 4 ) & 0x000F]; + pix[3] = grays[ c2 & 0x000F]; + + *bit = 0; + *bit2 = 0; + } + + bit++; + bit2++; + pix += 4; + c1--; + } + + if ( over ) + { + c2 = count[*bit] + count[*bit2]; + if ( c2 ) + { + switch ( last_bit ) + { + case 2: + pix[2] = grays[(c2 >> 4 ) & 0x000F]; + case 1: + pix[1] = grays[(c2 >> 8 ) & 0x000F]; + default: + pix[0] = grays[(c2 >> 12) & 0x000F]; + } + + *bit = 0; + *bit2 = 0; + } + } + } + + ras.traceOfs = 0; + ras.traceG += ras.traceIncr; + + ras.gray_min_x = 32000; + ras.gray_max_x = -32000; + } + } + + + static + void Horizontal_Gray_Sweep_Span( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + /* nothing, really */ + UNUSED( raster ); + UNUSED( y ); + UNUSED( x1 ); + UNUSED( x2 ); + UNUSED( left ); + UNUSED( right ); + } + + + static + void Horizontal_Gray_Sweep_Drop( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + Long e1, e2; + PByte pixel; + Byte color; + + + /* During the horizontal sweep, we only take care of drop-outs */ + e1 = CEILING( x1 ); + e2 = FLOOR ( x2 ); + + if ( e1 > e2 ) + { + if ( e1 == e2 + ras.precision ) + { + switch ( ras.dropOutControl ) + { + case 1: + e1 = e2; + break; + + case 4: + e1 = CEILING( ( x1 + x2 + 1 ) / 2 ); + break; + + case 2: + case 5: + + /* Drop-out Control Rule #4 */ + + /* The spec is not very clear regarding rule #4. It */ + /* presents a method that is way too costly to implement */ + /* while the general idea seems to get rid of `stubs'. */ + /* */ + + /* rightmost stub test */ + if ( left->next == right && left->height <= 0 ) + return; + + /* leftmost stub test */ + if ( right->next == left && left->start == y ) + return; + + if ( ras.dropOutControl == 2 ) + e1 = e2; + else + e1 = CEILING( ( x1 + x2 + 1 ) / 2 ); + + break; + + default: + return; /* unsupported mode */ + } + } + else + return; + } + + if ( e1 >= 0 ) + { + if ( x2 - x1 >= ras.precision_half ) + color = ras.grays[2]; + else + color = ras.grays[1]; + + e1 = TRUNC( e1 ) / 2; + if ( e1 < ras.target.rows ) + { + pixel = ras.gTarget - e1 * ras.target.pitch + y / 2; + if ( ras.target.pitch > 0 ) + pixel += ( ras.target.rows - 1 ) * ras.target.pitch; + + if ( pixel[0] == ras.grays[0] ) + pixel[0] = color; + } + } + } + + +#endif /* FT_RASTER_OPTION_ANTI_ALIASING */ + + + /*************************************************************************/ + /* */ + /* Generic Sweep Drawing routine */ + /* */ + /*************************************************************************/ + + static + Bool Draw_Sweep( RAS_ARG ) + { + Short y, y_change, y_height; + + PProfile P, Q, P_Left, P_Right; + + Short min_Y, max_Y, top, bottom, dropouts; + + Long x1, x2, xs, e1, e2; + + TProfileList wait; + TProfileList draw_left, draw_right; + + + /* Init empty linked lists */ + + Init_Linked( &wait ); + + 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( &wait, P ); + + P = Q; + } + + /* Check the Y-turns */ + if ( ras.numTurns == 0 ) + { + ras.error = Raster_Err_Invalid; + return FAILURE; + } + + /* Now inits the sweep */ + + ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y ); + + /* Then compute the distance of each profile from min_Y */ + + P = wait; + + 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 ) + { + /* look in the wait list for new activations */ + + P = wait; + + while ( P ) + { + Q = P->link; + P->countL -= y_height; + if ( P->countL == 0 ) + { + DelOld( &wait, P ); + + switch ( P->flow ) + { + case Flow_Up: + InsNew( &draw_left, P ); + break; + + case Flow_Down: + InsNew( &draw_right, P ); + break; + } + } + + P = Q; + } + + /* Sort the drawing lists */ + + Sort( &draw_left ); + Sort( &draw_right ); + + y_change = (Short)ras.sizeBuff[-ras.numTurns--]; + y_height = y_change - y; + + while ( y < y_change ) + { + /* Let's trace */ + + dropouts = 0; + + P_Left = draw_left; + P_Right = draw_right; + + while ( P_Left ) + { + x1 = P_Left ->X; + x2 = P_Right->X; + + if ( x1 > x2 ) + { + xs = x1; + x1 = x2; + x2 = xs; + } + + if ( x2 - x1 <= ras.precision ) + { + e1 = FLOOR( x1 ); + e2 = CEILING( x2 ); + + if ( ras.dropOutControl != 0 && + ( e1 > e2 || e2 == e1 + ras.precision ) ) + { + /* a drop out was detected */ + + P_Left ->X = x1; + P_Right->X = x2; + + /* mark profile for drop-out processing */ + P_Left->countL = 1; + dropouts++; + + goto Skip_To_Next; + } + } + + ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right ); + + Skip_To_Next: + + P_Left = P_Left->link; + P_Right = P_Right->link; + } + + /* now perform the dropouts _after_ the span drawing */ + /* drop-outs processing has been moved out of the loop */ + /* for performance tuning */ + if ( dropouts > 0 ) + goto Scan_DropOuts; + + Next_Line: + + ras.Proc_Sweep_Step( RAS_VAR ); + + y++; + + if ( y < y_change ) + { + Sort( &draw_left ); + Sort( &draw_right ); + } + } + + /* Now finalize the profiles that needs it */ + + { + PProfile Q, P; + + + P = draw_left; + while ( P ) + { + Q = P->link; + if ( P->height == 0 ) + DelOld( &draw_left, P ); + P = Q; + } + } + + { + PProfile Q, P = draw_right; + + + while ( P ) + { + Q = P->link; + if ( P->height == 0 ) + DelOld( &draw_right, P ); + P = Q; + } + } + } + + /* for gray-scaling, flushes the bitmap scanline cache */ + while ( y <= max_Y ) + { + ras.Proc_Sweep_Step( RAS_VAR ); + y++; + } + + return SUCCESS; + + Scan_DropOuts: + + P_Left = draw_left; + P_Right = draw_right; + + while ( P_Left ) + { + if ( P_Left->countL ) + { + P_Left->countL = 0; +#if 0 + dropouts--; /* -- this is useful when debugging only */ +#endif + ras.Proc_Sweep_Drop( RAS_VARS y, + P_Left->X, + P_Right->X, + P_Left, + P_Right ); + } + + P_Left = P_Left->link; + P_Right = P_Right->link; + } + + goto Next_Line; + } + + + /*************************************************************************/ + /* */ + /* */ + /* Render_Single_Pass */ + /* */ + /* */ + /* Performs one sweep with sub-banding. */ + /* */ + /* */ + /* flipped :: If set, flip the direction of the outline. */ + /* */ + /* */ + /* Renderer error code. */ + /* */ + static + int Render_Single_Pass( RAS_ARGS Bool flipped ) + { + Short i, j, k; + + + while ( ras.band_top >= 0 ) + { + ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision; + ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision; + + ras.top = ras.buff; + + ras.error = Raster_Err_None; + + if ( Convert_Glyph( RAS_VARS flipped ) ) + { + if ( ras.error != Raster_Err_Overflow ) + return FAILURE; + + ras.error = Raster_Err_None; + + /* sub-banding */ + +#ifdef DEBUG_RASTER + ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) ); +#endif + + i = ras.band_stack[ras.band_top].y_min; + j = ras.band_stack[ras.band_top].y_max; + + k = ( i + j ) / 2; + + if ( ras.band_top >= 7 || k < i ) + { + ras.band_top = 0; + ras.error = Raster_Err_Invalid; + + return ras.error; + } + + ras.band_stack[ras.band_top + 1].y_min = k; + ras.band_stack[ras.band_top + 1].y_max = j; + + ras.band_stack[ras.band_top].y_max = k - 1; + + ras.band_top++; + } + else + { + if ( ras.fProfile ) + if ( Draw_Sweep( RAS_VAR ) ) + return ras.error; + ras.band_top--; + } + } + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* */ + /* Render_Glyph */ + /* */ + /* */ + /* Renders a glyph in a bitmap. Sub-banding if needed. */ + /* */ + /* */ + /* FreeType error code. 0 means success. */ + /* */ + /* XXX Fixme: ftraster's error codes don't harmonize with FT2's ones! */ + /* */ + LOCAL_FUNC + FT_Error Render_Glyph( RAS_ARG ) + { + FT_Error error; + + + Set_High_Precision( RAS_VARS ras.outline.flags & + ft_outline_high_precision ); + ras.scale_shift = ras.precision_shift; + ras.dropOutControl = 2; + ras.second_pass = !( ras.outline.flags & ft_outline_single_pass ); + + /* Vertical Sweep */ + ras.Proc_Sweep_Init = Vertical_Sweep_Init; + ras.Proc_Sweep_Span = Vertical_Sweep_Span; + ras.Proc_Sweep_Drop = Vertical_Sweep_Drop; + ras.Proc_Sweep_Step = Vertical_Sweep_Step; + + ras.band_top = 0; + ras.band_stack[0].y_min = 0; + ras.band_stack[0].y_max = ras.target.rows - 1; + + ras.bWidth = ras.target.width; + ras.bTarget = (Byte*)ras.target.buffer; + + if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 ) + return error; + + /* Horizontal Sweep */ + if ( ras.second_pass && ras.dropOutControl != 0 ) + { + ras.Proc_Sweep_Init = Horizontal_Sweep_Init; + ras.Proc_Sweep_Span = Horizontal_Sweep_Span; + ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop; + ras.Proc_Sweep_Step = Horizontal_Sweep_Step; + + ras.band_top = 0; + ras.band_stack[0].y_min = 0; + ras.band_stack[0].y_max = ras.target.width - 1; + + if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 ) + return error; + } + + return FT_Err_Ok; + } + + +#ifdef FT_RASTER_OPTION_ANTI_ALIASING + + + /*************************************************************************/ + /* */ + /* */ + /* Render_Gray_Glyph */ + /* */ + /* */ + /* Renders a glyph with grayscaling. Sub-banding if needed. */ + /* */ + /* */ + /* FreeType error code. 0 means success. */ + /* */ + LOCAL_FUNC + FT_Error Render_Gray_Glyph( RAS_ARG ) + { + Long pixel_width; + FT_Error error; + + + Set_High_Precision( RAS_VARS ras.outline.flags & + ft_outline_high_precision ); + ras.scale_shift = ras.precision_shift + 1; + ras.dropOutControl = 2; + ras.second_pass = !( ras.outline.flags & ft_outline_single_pass ); + + + /* Vertical Sweep */ + + ras.band_top = 0; + ras.band_stack[0].y_min = 0; + ras.band_stack[0].y_max = 2 * ras.target.rows - 1; + + ras.bWidth = ras.gray_width; + pixel_width = 2 * ( ( ras.target.width + 3 ) >> 2 ); + + if ( ras.bWidth > pixel_width ) + ras.bWidth = pixel_width; + + ras.bWidth = ras.bWidth * 8; + ras.bTarget = (Byte*)ras.gray_lines; + ras.gTarget = (Byte*)ras.target.buffer; + + ras.Proc_Sweep_Init = Vertical_Gray_Sweep_Init; + ras.Proc_Sweep_Span = Vertical_Sweep_Span; + ras.Proc_Sweep_Drop = Vertical_Sweep_Drop; + ras.Proc_Sweep_Step = Vertical_Gray_Sweep_Step; + + error = Render_Single_Pass( RAS_VARS 0 ); + if (error) + return error; + + /* Horizontal Sweep */ + if ( ras.second_pass && ras.dropOutControl != 0 ) + { + ras.Proc_Sweep_Init = Horizontal_Sweep_Init; + ras.Proc_Sweep_Span = Horizontal_Gray_Sweep_Span; + ras.Proc_Sweep_Drop = Horizontal_Gray_Sweep_Drop; + ras.Proc_Sweep_Step = Horizontal_Sweep_Step; + + ras.band_top = 0; + ras.band_stack[0].y_min = 0; + ras.band_stack[0].y_max = ras.target.width * 2 - 1; + + error = Render_Single_Pass( RAS_VARS 1 ); + if (error) + return error; + } + + return FT_Err_Ok; + } + +#else /* FT_RASTER_OPTION_ANTI_ALIASING */ + + LOCAL_FUNC + FT_Error Render_Gray_Glyph( RAS_ARG ) + { + UNUSED_RASTER; + return FT_Err_Cannot_Render_Glyph; + } + +#endif + + + static + void ft_black_init( TRaster_Instance* raster ) + { + FT_UInt n; + FT_ULong c; + + /* setup count table */ + for ( n = 0; n < 256; n++ ) + { + c = ( n & 0x55 ) + ( ( n & 0xAA ) >> 1 ); + + c = ( ( c << 6 ) & 0x3000 ) | + ( ( c << 4 ) & 0x0300 ) | + ( ( c << 2 ) & 0x0030 ) | + (c & 0x0003 ); + + raster->count_table[n] = c; + } + +#ifdef FT_RASTER_OPTION_ANTI_ALIASING + /* set default 5-levels gray palette */ + for ( n = 0; n < 5; n++ ) + raster->grays[n] = n * 255 / 4; + + raster->gray_width = RASTER_GRAY_LINES / 2; +#endif + } + + /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/ + /**** a static object. *****/ + +#ifdef _STANDALONE_ + + + static + int ft_black_new( void* memory, + FT_Raster *araster ) + { + static FT_RasterRec_ the_raster; + + + *araster = &the_raster; + memset( &the_raster, sizeof ( the_raster ), 0 ); + ft_black_init( &the_raster ); + + return 0; + } + + + static + void ft_black_done( FT_Raster raster ) + { + /* nothing */ + raster->init = 0; + } + + +#else /* _STANDALONE_ */ + + + static + int ft_black_new( FT_Memory memory, + TRaster_Instance** araster ) + { + FT_Error error; + TRaster_Instance* raster; + + + *araster = 0; + if ( !ALLOC( raster, sizeof ( *raster ) ) ) + { + raster->memory = memory; + ft_black_init( raster ); + + *araster = raster; + } + + return error; + } + + + static + void ft_black_done( TRaster_Instance* raster ) + { + FT_Memory memory = (FT_Memory)raster->memory; + FREE( raster ); + } + + +#endif /* _STANDALONE_ */ + + + static + void ft_black_reset( TRaster_Instance* raster, + const char* pool_base, + long pool_size ) + { + if ( raster && pool_base && pool_size >= 4096 ) + { + /* save the pool */ + raster->buff = (PLong)pool_base; + raster->sizeBuff = raster->buff + pool_size / sizeof ( Long ); + } + } + + + static + void ft_black_set_mode( TRaster_Instance* raster, + unsigned long mode, + const char* palette ) + { +#ifdef FT_RASTER_OPTION_ANTI_ALIASING + if ( mode == FT_MAKE_TAG( 'p', 'a', 'l', '5' ) ) + { + /* set 5-levels gray palette */ + raster->grays[0] = palette[0]; + raster->grays[1] = palette[1]; + raster->grays[2] = palette[2]; + raster->grays[3] = palette[3]; + raster->grays[4] = palette[4]; + } +#else + UNUSED(raster); + UNUSED(mode); + UNUSED(palette); +#endif + } + + + static + int ft_black_render( TRaster_Instance* raster, + FT_Raster_Params* params ) + { + FT_Outline* outline = (FT_Outline*)params->source; + FT_Bitmap* target_map = params->target; + + + if ( !raster || !raster->buff || !raster->sizeBuff ) + return Raster_Err_Not_Ini; + + if ( !outline || !outline->contours || !outline->points ) + return Raster_Err_Invalid; + + /* return immediately if the outline is empty */ + if ( outline->n_points == 0 || outline->n_contours <= 0 ) + return Raster_Err_None; + + if ( outline->n_points != outline->contours[outline->n_contours - 1] + 1 ) + return Raster_Err_Invalid; + + if ( !target_map || !target_map->buffer ) + return Raster_Err_Invalid; + + /* this version of the raster does not support direct rendering, sorry */ + if ( params->flags & ft_raster_flag_direct ) + return Raster_Err_Unsupported; + + ras.outline = *outline; + ras.target = *target_map; + + return ( params->flags & ft_raster_flag_aa + ? Render_Gray_Glyph( raster ) + : Render_Glyph( raster ) ); + } + + + FT_Raster_Funcs ft_standard_raster = + { + ft_glyph_format_outline, + (FT_Raster_New_Func) ft_black_new, + (FT_Raster_Reset_Func) ft_black_reset, + (FT_Raster_Set_Mode_Func)ft_black_set_mode, + (FT_Raster_Render_Func) ft_black_render, + (FT_Raster_Done_Func) ft_black_done + }; + + +/* END */ diff --git a/src/raster1/ftraster.h b/src/raster1/ftraster.h new file mode 100644 index 000000000..371ed2783 --- /dev/null +++ b/src/raster1/ftraster.h @@ -0,0 +1,48 @@ +/***************************************************************************/ +/* */ +/* ftraster.h */ +/* */ +/* The FreeType glyph rasterizer (specification). */ +/* */ +/* 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 FTRASTER_H +#define FTRASTER_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include + + /*************************************************************************/ + /* */ + /* Uncomment the following line if you are using ftraster.c as a */ + /* standalone module, fully independent of FreeType. */ + /* */ +/* #define _STANDALONE_ */ + +#ifndef FT_EXPORT_VAR +#define FT_EXPORT_VAR(x) extern x +#endif + + FT_EXPORT_VAR(FT_Raster_Funcs) ft_standard_raster; + +#ifdef __cplusplus + } +#endif + +#endif /* FTRASTER_H */ + + +/* END */ diff --git a/src/raster1/ftrend1.c b/src/raster1/ftrend1.c new file mode 100644 index 000000000..84fe38a12 --- /dev/null +++ b/src/raster1/ftrend1.c @@ -0,0 +1,231 @@ +#include +#include +#include + + /* initialise renderer - init its raster */ + static FT_Error ft_raster1_init( FT_Renderer render ) + { + FT_Library library = FT_MODULE_LIBRARY(render); + + render->clazz->raster_class->raster_reset( render->raster, + library->raster_pool, library->raster_pool_size ); + + return 0; + } + + + + /* sets render-specific mode */ + static FT_Error ft_raster1_set_mode( FT_Renderer render, + FT_ULong mode_tag, + FT_Pointer data ) + { + /* we simply pass it to the raster */ + return render->clazz->raster_class->raster_set_mode( + render->raster, mode_tag, data ); + } + + /* transform a given glyph image */ + static FT_Error ft_raster1_transform( FT_Renderer render, + FT_GlyphSlot slot, + FT_Matrix* matrix, + FT_Vector* delta ) + { + FT_Error error = FT_Err_Ok; + + if (slot->format != render->glyph_format) + { + error = FT_Err_Invalid_Argument; + goto Exit; + } + + if (matrix) + FT_Outline_Transform( &slot->outline, matrix ); + + if (delta) + FT_Outline_Translate( &slot->outline, delta->x, delta->y ); + + Exit: + return error; + } + + /* return the glyph's control box */ + static void ft_raster1_get_cbox( FT_Renderer render, + FT_GlyphSlot slot, + FT_BBox *cbox ) + { + MEM_Set( cbox, 0, sizeof(*cbox) ); + + if (slot->format == render->glyph_format) + FT_Outline_Get_CBox( &slot->outline, cbox ); + } + + + /* convert a slot's glyph image into a bitmap */ + static FT_Error ft_raster1_render( FT_Renderer render, + FT_GlyphSlot slot, + FT_UInt mode, + FT_Vector* origin ) + { + FT_Error error; + FT_Outline* outline; + FT_BBox cbox; + FT_UInt width, height, pitch; + FT_Bitmap* bitmap; + FT_Memory memory; + + FT_Raster_Params params; + + /* check glyph image format */ + if (slot->format != render->glyph_format) + { + error = FT_Err_Invalid_Argument; + goto Exit; + } + + /* check rendering mode */ + if ( mode & ft_render_mode_antialias ) + { + /* raster1 is only capable of producing monochrome bitmaps */ + if (render->clazz == &ft_raster1_renderer_class) + return FT_Err_Cannot_Render_Glyph; + } + else + { + /* raster5 is only capable of producing 5-gray-levels bitmaps */ + if (render->clazz == &ft_raster5_renderer_class) + return FT_Err_Cannot_Render_Glyph; + } + + outline = &slot->outline; + + /* translate the outline to the new origin if needed */ + if (origin) + FT_Outline_Translate( outline, origin->x, origin->y ); + + /* compute the control box, and grid fit it */ + FT_Outline_Get_CBox( outline, &cbox ); + + cbox.xMin &= -64; + cbox.yMin &= -64; + cbox.xMax = (cbox.xMax+63) & -64; + cbox.yMax = (cbox.yMax+63) & -64; + + width = (cbox.xMax - cbox.xMin) >> 6; + height = (cbox.yMax - cbox.yMin) >> 6; + bitmap = &slot->bitmap; + memory = render->root.memory; + + /* release old bitmap buffer */ + if ((slot->flags & ft_glyph_own_bitmap)) + { + FREE(bitmap->buffer); + slot->flags &= ~ft_glyph_own_bitmap; + } + + /* allocate new one, depends on pixel format */ + if ( mode & ft_render_mode_antialias ) + { + /* we pad to 32 bits, only for backwards compatibility with FT 1.x */ + pitch = (width+3) & -4; + bitmap->pixel_mode = ft_pixel_mode_grays; + bitmap->num_grays = 256; + } + else + { + pitch = (width+7) >> 3; + bitmap->pixel_mode = ft_pixel_mode_mono; + } + + bitmap->width = width; + bitmap->rows = height; + bitmap->pitch = pitch; + + if (ALLOC( bitmap->buffer, (FT_ULong)pitch * height )) + goto Exit; + + slot->flags |= ft_glyph_own_bitmap; + + /* translate outline to render it into the bitmap */ + FT_Outline_Translate( outline, -cbox.xMin, -cbox.yMin ); + + /* set up parameters */ + params.target = bitmap; + params.source = outline; + params.flags = 0; + + if ( bitmap->pixel_mode == ft_pixel_mode_grays ) + params.flags |= ft_raster_flag_aa; + + /* render outline into the bitmap */ + error = render->raster_render( render->raster, ¶ms ); + if (error) goto Exit; + + slot->format = ft_glyph_format_bitmap; + slot->bitmap_left = cbox.xMin >> 6; + slot->bitmap_top = cbox.yMax >> 6; + + Exit: + return error; + } + + + const FT_Renderer_Class ft_raster1_renderer_class = + { + { + ft_module_renderer, + sizeof( FT_RendererRec ), + + "raster1", + 0x10000, + 0x20000, + + 0, /* module specific interface */ + + (FT_Module_Constructor) ft_raster1_init, + (FT_Module_Destructor) 0, + (FT_Module_Requester) 0 + }, + + ft_glyph_format_outline, + + (FTRenderer_render) ft_raster1_render, + (FTRenderer_transform) ft_raster1_transform, + (FTRenderer_getCBox) ft_raster1_get_cbox, + (FTRenderer_setMode) ft_raster1_set_mode, + + (FT_Raster_Funcs*) &ft_standard_raster + }; + + + /* this renderer is _NOT_ part of the default modules, you'll need */ + /* to register it by hand in your application. It should only be */ + /* used for backwards-compatibility with FT 1.x anyway.. */ + const FT_Renderer_Class ft_raster5_renderer_class = + { + { + ft_module_renderer, + sizeof( FT_RendererRec ), + + "raster5", + 0x10000, + 0x20000, + + 0, /* module specific interface */ + + (FT_Module_Constructor) ft_raster1_init, + (FT_Module_Destructor) 0, + (FT_Module_Requester) 0 + }, + + ft_glyph_format_outline, + + (FTRenderer_render) ft_raster1_render, + (FTRenderer_transform) ft_raster1_transform, + (FTRenderer_getCBox) ft_raster1_get_cbox, + (FTRenderer_setMode) ft_raster1_set_mode, + + (FT_Raster_Funcs*) &ft_standard_raster + }; + + diff --git a/src/raster1/ftrend1.h b/src/raster1/ftrend1.h new file mode 100644 index 000000000..78b971e7e --- /dev/null +++ b/src/raster1/ftrend1.h @@ -0,0 +1,14 @@ +#ifndef FTREND1_H +#define FTREND1_H + +#include + + FT_EXPORT_VAR(const FT_Renderer_Class) ft_raster1_renderer_class; + + /* this renderer is _NOT_ part of the default modules, you'll need */ + /* to register it by hand in your application. It should only be */ + /* used for backwards-compatibility with FT 1.x anyway.. */ + /* */ + FT_EXPORT_VAR(const FT_Renderer_Class) ft_raster5_renderer_class; + +#endif /* FTREND1_H */ diff --git a/src/raster1/module.mk b/src/raster1/module.mk new file mode 100644 index 000000000..c1ceb2129 --- /dev/null +++ b/src/raster1/module.mk @@ -0,0 +1,7 @@ +make_module_list: add_raster1_module + +add_raster1_module: + $(OPEN_DRIVER)ft_raster1_renderer_class$(CLOSE_DRIVER) + $(ECHO_DRIVER)raster1 $(ECHO_DRIVER_DESC)monochrome bitmap renderer$(ECHO_DRIVER_DONE) + +# EOF diff --git a/src/raster1/raster1.c b/src/raster1/raster1.c new file mode 100644 index 000000000..c6dd58e35 --- /dev/null +++ b/src/raster1/raster1.c @@ -0,0 +1,4 @@ +#define FT_MAKE_OPTION_SINGLE_OBJECT +#include +#include + diff --git a/src/raster1/raster1.h b/src/raster1/raster1.h new file mode 100644 index 000000000..88bc94cd5 --- /dev/null +++ b/src/raster1/raster1.h @@ -0,0 +1,14 @@ +#ifndef RASTER1_H +#define RASTER1_H + +#include + + FT_EXPORT_VAR(const FT_Renderer_Class) ft_raster1_renderer_class; + + /* this renderer is _NOT_ part of the default modules, you'll need */ + /* to register it by hand in your application. It should only be */ + /* used for backwards-compatibility with FT 1.x anyway.. */ + /* */ + FT_EXPORT_VAR(const FT_Renderer_Class) ft_raster5_renderer_class; + +#endif /* RASTER1_H */ diff --git a/src/raster1/rules.mk b/src/raster1/rules.mk new file mode 100644 index 000000000..0725897de --- /dev/null +++ b/src/raster1/rules.mk @@ -0,0 +1,74 @@ +# +# FreeType 2 renderer module build rules +# + + +# 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. + + +# renderer driver directory +# +RAS1_DIR := $(SRC_)raster1 +RAS1_DIR_ := $(RAS1_DIR)$(SEP) + + +# additional include flags used when compiling the driver +# +RAS1_INCLUDE := $(RAS1_DIR) + +# compilation flags for the driver +# +RAS1_CFLAGS := $(RAS1_INCLUDE:%=$I%) +RAS1_COMPILE := $(FT_COMPILE) $(RAS1_CFLAGS) + + +# RASTER1 driver sources (i.e., C files) +# +RAS1_DRV_SRC := $(RAS1_DIR_)ftraster.c \ + $(RAS1_DIR_)ftrend1.c + +# RASTER1 driver headers +# +RAS1_DRV_H := $(RAS1_DRV_SRC:%c=%h) + + +# RASTER1 driver object(s) +# +# RAS1_DRV_OBJ_M is used during `multi' builds. +# RAS1_DRV_OBJ_S is used during `single' builds. +# +RAS1_DRV_OBJ_M := $(RAS1_DRV_SRC:$(RAS1_DIR_)%.c=$(OBJ_)%.$O) +RAS1_DRV_OBJ_S := $(OBJ_)raster1.$O + +# RASTER1 driver source file for single build +# +RAS1_DRV_SRC_S := $(RAS1_DIR_)raster1.c + + +# RASTER1 driver - single object +# +$(RAS1_DRV_OBJ_S): $(RAS1_DRV_SRC_S) $(RAS1_DRV_SRC) \ + $(FREETYPE_H) $(RAS1_DRV_H) + $(RAS1_COMPILE) $T$@ $(RAS1_DRV_SRC_S) + + +# RASTER1 driver - multiple objects +# +$(OBJ_)%.$O: $(RAS1_DIR_)%.c $(FREETYPE_H) $(RAS1_DRV_H) + $(RAS1_COMPILE) $T$@ $< + + +# update main driver object lists +# +DRV_OBJS_S += $(RAS1_DRV_OBJ_S) +DRV_OBJS_M += $(RAS1_DRV_OBJ_M) + + +# EOF diff --git a/src/smooth/ftgrays.c b/src/smooth/ftgrays.c new file mode 100644 index 000000000..06cdab975 --- /dev/null +++ b/src/smooth/ftgrays.c @@ -0,0 +1,1954 @@ +/***************************************************************************/ +/* */ +/* ftgrays.c */ +/* */ +/* A new `perfect' anti-aliasing renderer (body). */ +/* */ +/* Copyright 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. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* 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 `ftgrays.h' and `ftimage.h' into the current */ + /* compilation directory. Typically, you could do something like */ + /* */ + /* - copy `src/base/ftgrays.c' to your current directory */ + /* */ + /* - copy `include/freetype/ftimage.h' and */ + /* `include/freetype/ftgrays.h' to the same directory */ + /* */ + /* - compile `ftgrays' with the _STANDALONE_ macro defined, as in */ + /* */ + /* cc -c -D_STANDALONE_ ftgrays.c */ + /* */ + /* The renderer can be initialized with a call to */ + /* `ft_grays_raster.grays_raster_new'; an anti-aliased bitmap can be */ + /* generated with a call to `ft_grays_raster.grays_raster_render'. */ + /* */ + /* See the comments and documentation in the file `ftimage.h' for */ + /* more details on how the raster works. */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* This is a new anti-aliasing scan-converter for FreeType 2. The */ + /* algorithm used here is _very_ different from the one in the standard */ + /* `ftraster' module. Actually, `ftgrays' computes the _exact_ */ + /* coverage of the outline on each pixel cell. */ + /* */ + /* It is based on ideas that I initially found in Raph Levien's */ + /* excellent LibArt graphics library (see http://www.levien.com/libart */ + /* for more information, though the web pages do not tell anything */ + /* about the renderer; you'll have to dive into the source code to */ + /* understand how it works). */ + /* */ + /* Note, however, that this is a _very_ different implementation */ + /* compared Raph's. Coverage information is stored in a very different */ + /* way, and I don't use sorted vector paths. Also, it doesn't use */ + /* floating point values. */ + /* */ + /* This renderer has the following advantages: */ + /* */ + /* - It doesn't need an intermediate bitmap. Instead, one can supply */ + /* a callback function that will be called by the renderer to draw */ + /* gray spans on any target surface. You can thus do direct */ + /* composition on any kind of bitmap, provided that you give the */ + /* renderer the right callback. */ + /* */ + /* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */ + /* each pixel cell */ + /* */ + /* - It performs a single pass on the outline (the `standard' FT2 */ + /* renderer makes two passes). */ + /* */ + /* - It can easily be modified to render to _any_ number of gray levels */ + /* cheaply. */ + /* */ + /* - For small (< 20) pixel sizes, it is faster than the standard */ + /* renderer. */ + /* */ + /*************************************************************************/ + + +#include /* for memcpy() */ + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_aaraster + + +#define ErrRaster_Invalid_Outline -1 + +#ifdef _STANDALONE_ + +#define ErrRaster_Invalid_Mode -2 + +#include "ftimage.h" +#include "ftgrays.h" + + /* 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 UNUSED( x ) (x) = (x) + + /* Disable the tracing mechanism for simplicity -- developers can */ + /* activate it easily by redefining these two macros. */ +#ifndef FT_ERROR +#define FT_ERROR( x ) do ; while ( 0 ) /* nothing */ +#endif + +#ifndef FT_TRACE +#define FT_TRACE( x ) do ; while ( 0 ) /* nothing */ +#endif + + +#else /* _STANDALONE_ */ + + +#include "ftgrays.h" +#include /* for UNUSED() */ +#include /* for FT_TRACE() and FT_ERROR() */ +#include /* for FT_Outline_Decompose() */ + +#define ErrRaster_Invalid_Mode FT_Err_Cannot_Render_Glyph + + +#endif /* _STANDALONE_ */ + + + /* define this to dump debugging information */ +#define xxxDEBUG_GRAYS + + /* as usual, for the speed hungry :-) */ + +#ifndef FT_STATIC_RASTER + + +#define RAS_ARG PRaster raster +#define RAS_ARG_ PRaster raster, + +#define RAS_VAR raster +#define RAS_VAR_ raster, + +#define ras (*raster) + + +#else /* FT_STATIC_RASTER */ + + +#define RAS_ARG /* empty */ +#define RAS_ARG_ /* empty */ +#define RAS_VAR /* empty */ +#define RAS_VAR_ /* empty */ + + static TRaster ras; + + +#endif /* FT_STATIC_RASTER */ + + + /* must be at least 6 bits! */ +#define PIXEL_BITS 8 + +#define ONE_PIXEL ( 1L << PIXEL_BITS ) +#define PIXEL_MASK ( -1L << PIXEL_BITS ) +#define TRUNC( x ) ( (x) >> PIXEL_BITS ) +#define SUBPIXELS( x ) ( (x) << PIXEL_BITS ) +#define FLOOR( x ) ( (x) & -ONE_PIXEL ) +#define CEILING( x ) ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL ) +#define ROUND( x ) ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL ) + +#if PIXEL_BITS >= 6 +#define UPSCALE( x ) ( (x) << ( PIXEL_BITS - 6 ) ) +#define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) ) +#else +#define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) ) +#define DOWNSCALE( x ) ( (x) << ( 6 - PIXEL_BITS ) ) +#endif + + /* Define this if you want to use a more compact storage scheme. This */ + /* increases the number of cells available in the render pool but slows */ + /* down the rendering a bit. It is useful if you have a really tiny */ + /* render pool. */ +#define xxxGRAYS_COMPACT + + + /*************************************************************************/ + /* */ + /* TYPE DEFINITIONS */ + /* */ + typedef int TScan; /* integer scanline/pixel coordinate */ + typedef long TPos; /* sub-pixel coordinate */ + + /* maximal number of gray spans in a call to the span callback */ +#define FT_MAX_GRAY_SPANS 32 + + +#ifdef GRAYS_COMPACT + + typedef struct TCell_ + { + short x : 14; + short y : 14; + int cover : PIXEL_BITS + 2; + int area : PIXEL_BITS * 2 + 2; + + } TCell, *PCell; + +#else /* GRAYS_COMPACT */ + + typedef struct TCell_ + { + TScan x; + TScan y; + int cover; + int area; + + } TCell, *PCell; + +#endif /* GRAYS_COMPACT */ + + + typedef struct TRaster_ + { + PCell cells; + int max_cells; + int num_cells; + + TScan min_ex, max_ex; + TScan min_ey, max_ey; + + int area; + int cover; + int invalid; + + TScan ex, ey; + TScan cx, cy; + TPos x, y; + + TScan last_ey; + + FT_Vector bez_stack[32 * 3]; + int lev_stack[32]; + + FT_Outline outline; + FT_Bitmap target; + + FT_Span gray_spans[FT_MAX_GRAY_SPANS]; + int num_gray_spans; + + FT_Raster_Span_Func render_span; + void* render_span_data; + int span_y; + + int band_size; + int band_shoot; + int conic_level; + int cubic_level; + + void* memory; + + } TRaster, *PRaster; + + + /*************************************************************************/ + /* */ + /* Initialize the cells table. */ + /* */ + static + void init_cells( RAS_ARG_ void* buffer, + long byte_size ) + { + ras.cells = (PCell)buffer; + ras.max_cells = byte_size / sizeof ( TCell ); + ras.num_cells = 0; + ras.area = 0; + ras.cover = 0; + ras.invalid = 1; + } + + + /*************************************************************************/ + /* */ + /* Compute the outline bounding box. */ + /* */ + static + void compute_cbox( RAS_ARG_ FT_Outline* outline ) + { + FT_Vector* vec = outline->points; + FT_Vector* limit = vec + outline->n_points; + + + if ( outline->n_points <= 0 ) + { + ras.min_ex = ras.max_ex = 0; + ras.min_ey = ras.max_ey = 0; + return; + } + + ras.min_ex = ras.max_ex = vec->x; + ras.min_ey = ras.max_ey = vec->y; + + vec++; + + for ( ; vec < limit; vec++ ) + { + TPos x = vec->x; + TPos y = vec->y; + + + if ( x < ras.min_ex ) ras.min_ex = x; + if ( x > ras.max_ex ) ras.max_ex = x; + if ( y < ras.min_ey ) ras.min_ey = y; + if ( y > ras.max_ey ) ras.max_ey = y; + } + + /* truncate the bounding box to integer pixels */ + ras.min_ex = ras.min_ex >> 6; + ras.min_ey = ras.min_ey >> 6; + ras.max_ex = ( ras.max_ex + 63 ) >> 6; + ras.max_ey = ( ras.max_ey + 63 ) >> 6; + } + + + /*************************************************************************/ + /* */ + /* Record the current cell in the table. */ + /* */ + static + int record_cell( RAS_ARG ) + { + PCell cell; + + + if ( !ras.invalid && ( ras.area | ras.cover ) ) + { + if ( ras.num_cells >= ras.max_cells ) + return 1; + + cell = ras.cells + ras.num_cells++; + cell->x = ras.ex - ras.min_ex; + cell->y = ras.ey - ras.min_ey; + cell->area = ras.area; + cell->cover = ras.cover; + } + + return 0; + } + + + /*************************************************************************/ + /* */ + /* Set the current cell to a new position. */ + /* */ + static + int set_cell( RAS_ARG_ TScan ex, + TScan ey ) + { + int invalid, record, clean; + + + /* Move the cell pointer to a new position. We set the `invalid' */ + /* flag to indicate that the cell isn't part of those we're interested */ + /* in during the render phase. This means that: */ + /* */ + /* . the new vertical position must be within min_ey..max_ey - 1. */ + /* . the new horizontal position must be strictly less than max_ex */ + /* */ + /* Note that if a cell is to the left of the clipping region, it is */ + /* actually set to the (min_ex-1) horizontal position. */ + + record = 0; + clean = 1; + + invalid = ( ey < ras.min_ey || ey >= ras.max_ey || ex >= ras.max_ex ); + if ( !invalid ) + { + /* All cells that are on the left of the clipping region go to the */ + /* min_ex - 1 horizontal position. */ + if ( ex < ras.min_ex ) + ex = ras.min_ex - 1; + + /* if our position is new, then record the previous cell */ + if ( ex != ras.ex || ey != ras.ey ) + record = 1; + else + clean = ras.invalid; /* do not clean if we didn't move from */ + /* a valid cell */ + } + + /* record the previous cell if needed (i.e., if we changed the cell */ + /* position, of changed the `invalid' flag) */ + if ( ( ras.invalid != invalid || record ) && record_cell( RAS_VAR ) ) + return 1; + + if ( clean ) + { + ras.area = 0; + ras.cover = 0; + } + + ras.invalid = invalid; + ras.ex = ex; + ras.ey = ey; + return 0; + } + + + /*************************************************************************/ + /* */ + /* Start a new contour at a given cell. */ + /* */ + static + void start_cell( RAS_ARG_ TScan ex, + TScan ey ) + { + if ( ex < ras.min_ex ) + ex = ras.min_ex - 1; + + ras.area = 0; + ras.cover = 0; + ras.ex = ex; + ras.ey = ey; + ras.last_ey = SUBPIXELS( ey ); + ras.invalid = 0; + + (void)set_cell( RAS_VAR_ ex, ey ); + } + + + /*************************************************************************/ + /* */ + /* Render a scanline as one or more cells. */ + /* */ + static + int render_scanline( RAS_ARG_ TScan ey, + TPos x1, + TScan y1, + TPos x2, + TScan y2 ) + { + TScan ex1, ex2, fx1, fx2, delta; + long p, first, dx; + int incr, lift, mod, rem; + + + dx = x2 - x1; + + ex1 = TRUNC( x1 ); /* if (ex1 >= ras.max_ex) ex1 = ras.max_ex-1; */ + ex2 = TRUNC( x2 ); /* if (ex2 >= ras.max_ex) ex2 = ras.max_ex-1; */ + fx1 = x1 - SUBPIXELS( ex1 ); + fx2 = x2 - SUBPIXELS( ex2 ); + + /* trivial case. Happens often */ + if ( y1 == y2 ) + return set_cell( RAS_VAR_ ex2, ey ); + + /* everything is located in a single cell. That is easy! */ + /* */ + if ( ex1 == ex2 ) + { + delta = y2 - y1; + ras.area += ( fx1 + fx2 ) * delta; + ras.cover += delta; + return 0; + } + + /* ok, we'll have to render a run of adjacent cells on the same */ + /* scanline... */ + /* */ + p = ( ONE_PIXEL - fx1 ) * ( y2 - y1 ); + first = ONE_PIXEL; + incr = 1; + + if ( dx < 0 ) + { + p = fx1 * ( y2 - y1 ); + first = 0; + incr = -1; + dx = -dx; + } + + delta = p / dx; + mod = p % dx; + if ( mod < 0 ) + { + delta--; + mod += dx; + } + + ras.area += ( fx1 + first ) * delta; + ras.cover += delta; + + ex1 += incr; + if ( set_cell( RAS_VAR_ ex1, ey ) ) + goto Error; + y1 += delta; + + if ( ex1 != ex2 ) + { + p = ONE_PIXEL * ( y2 - y1 ); + lift = p / dx; + rem = p % dx; + if ( rem < 0 ) + { + lift--; + rem += dx; + } + + mod -= dx; + + while ( ex1 != ex2 ) + { + delta = lift; + mod += rem; + if ( mod >= 0 ) + { + mod -= dx; + delta++; + } + + ras.area += ONE_PIXEL * delta; + ras.cover += delta; + y1 += delta; + ex1 += incr; + if ( set_cell( RAS_VAR_ ex1, ey ) ) + goto Error; + } + } + + delta = y2 - y1; + ras.area += ( fx2 + ONE_PIXEL - first ) * delta; + ras.cover += delta; + + return 0; + + Error: + return 1; + } + + + /*************************************************************************/ + /* */ + /* Render a given line as a series of scanlines. */ + /* */ + static + int render_line( RAS_ARG_ TPos to_x, + TPos to_y ) + { + TScan ey1, ey2, fy1, fy2; + TPos dx, dy, x, x2; + int p, rem, mod, lift, delta, first, incr; + + + ey1 = TRUNC( ras.last_ey ); + ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */ + fy1 = ras.y - ras.last_ey; + fy2 = to_y - SUBPIXELS( ey2 ); + + dx = to_x - ras.x; + dy = to_y - ras.y; + + /* we should do something about the trivial case where dx == 0, */ + /* as it happens very often! XXXXX */ + + /* perform vertical clipping */ + { + TScan min, max; + + + min = ey1; + max = ey2; + if ( ey1 > ey2 ) + { + min = ey2; + max = ey1; + } + if ( min >= ras.max_ey || max < ras.min_ey ) + goto End; + } + + /* everything is on a single scanline */ + if ( ey1 == ey2 ) + { + if ( render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 ) ) + goto Error; + goto End; + } + + /* ok, we'll have to render several scanlines */ + p = ( ONE_PIXEL - fy1 ) * dx; + first = ONE_PIXEL; + incr = 1; + + if ( dy < 0 ) + { + p = fy1 * dx; + first = 0; + incr = -1; + dy = -dy; + } + + delta = p / dy; + mod = p % dy; + if ( mod < 0 ) + { + delta--; + mod += dy; + } + + x = ras.x + delta; + if ( render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, first ) ) + goto Error; + + ey1 += incr; + if ( set_cell( RAS_VAR_ TRUNC( x ), ey1 ) ) + goto Error; + + if ( ey1 != ey2 ) + { + p = ONE_PIXEL * dx; + lift = p / dy; + rem = p % dy; + if ( rem < 0 ) + { + lift--; + rem += dy; + } + mod -= dy; + + while ( ey1 != ey2 ) + { + delta = lift; + mod += rem; + if ( mod >= 0 ) + { + mod -= dy; + delta++; + } + + x2 = x + delta; + if ( render_scanline( RAS_VAR_ ey1, + x, ONE_PIXEL - first, x2, first ) ) + goto Error; + x = x2; + ey1 += incr; + if ( set_cell( RAS_VAR_ TRUNC( x ), ey1 ) ) + goto Error; + } + } + + if ( render_scanline( RAS_VAR_ ey1, + x, ONE_PIXEL - first, to_x, fy2 ) ) + goto Error; + + End: + ras.x = to_x; + ras.y = to_y; + ras.last_ey = SUBPIXELS( ey2 ); + + return 0; + + Error: + return 1; + } + + + static + void split_conic( FT_Vector* 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; + } + + + static + int render_conic( RAS_ARG_ FT_Vector* control, + FT_Vector* to ) + { + TPos dx, dy; + int top, level; + int* levels; + FT_Vector* arc; + + + dx = DOWNSCALE( ras.x ) + to->x - ( control->x << 1 ); + if ( dx < 0 ) + dx = -dx; + dy = DOWNSCALE( ras.y ) + to->y - ( control->y << 1 ); + if ( dy < 0 ) + dy = -dy; + if ( dx < dy ) + dx = dy; + + level = 1; + dx = dx / ras.conic_level; + while ( dx > 0 ) + { + dx >>= 1; + level++; + } + + /* a shortcut to speed things up */ + if ( level <= 1 ) + { + /* we compute the mid-point directly in order to avoid */ + /* calling split_conic() */ + TPos to_x, to_y, mid_x, mid_y; + + + to_x = UPSCALE( to->x ); + to_y = UPSCALE( to->y ); + mid_x = ( ras.x + to_x + 2 * UPSCALE( control->x ) ) / 4; + mid_y = ( ras.y + to_y + 2 * UPSCALE( control->y ) ) / 4; + + return render_line( RAS_VAR_ mid_x, mid_y ) || + render_line( RAS_VAR_ to_x, to_y ); + } + + arc = ras.bez_stack; + levels = ras.lev_stack; + top = 0; + levels[0] = level; + + arc[0].x = UPSCALE( to->x ); + arc[0].y = UPSCALE( to->y ); + arc[1].x = UPSCALE( control->x ); + arc[1].y = UPSCALE( control->y ); + arc[2].x = ras.x; + arc[2].y = ras.y; + + while ( top >= 0 ) + { + level = levels[top]; + if ( level > 1 ) + { + /* check that the arc crosses the current band */ + TPos min, max, y; + + + min = max = arc[0].y; + + y = arc[1].y; + if ( y < min ) min = y; + if ( y > max ) max = y; + + y = arc[2].y; + if ( y < min ) min = y; + if ( y > max ) max = y; + + if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 ) + goto Draw; + + split_conic( arc ); + arc += 2; + top++; + levels[top] = levels[top - 1] = level - 1; + continue; + } + + Draw: + { + TPos to_x, to_y, mid_x, mid_y; + + + to_x = arc[0].x; + to_y = arc[0].y; + mid_x = ( ras.x + to_x + 2 * arc[1].x ) / 4; + mid_y = ( ras.y + to_y + 2 * arc[1].y ) / 4; + + if ( render_line( RAS_VAR_ mid_x, mid_y ) || + render_line( RAS_VAR_ to_x, to_y ) ) + return 1; + + top--; + arc -= 2; + } + } + return 0; + } + + + static + void split_cubic( FT_Vector* 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; + } + + + static + int render_cubic( RAS_ARG_ FT_Vector* control1, + FT_Vector* control2, + FT_Vector* to ) + { + TPos dx, dy, da, db; + int top, level; + int* levels; + FT_Vector* arc; + + + dx = DOWNSCALE( ras.x ) + to->x - ( control1->x << 1 ); + if ( dx < 0 ) + dx = -dx; + dy = DOWNSCALE( ras.y ) + to->y - ( control1->y << 1 ); + if ( dy < 0 ) + dy = -dy; + if ( dx < dy ) + dx = dy; + da = dx; + + dx = DOWNSCALE( ras.x ) + to->x - 3 * ( control1->x + control2->x ); + if ( dx < 0 ) + dx = -dx; + dy = DOWNSCALE( ras.y ) + to->y - 3 * ( control1->x + control2->y ); + if ( dy < 0 ) + dy = -dy; + if ( dx < dy ) + dx = dy; + db = dx; + + level = 1; + da = da / ras.cubic_level; + db = db / ras.conic_level; + while ( da > 0 || db > 0 ) + { + da >>= 1; + db >>= 2; + level++; + } + + if ( level <= 1 ) + { + TPos to_x, to_y, mid_x, mid_y; + + + to_x = UPSCALE( to->x ); + to_y = UPSCALE( to->y ); + mid_x = ( ras.x + to_x + + 3 * UPSCALE( control1->x + control2->x ) ) / 8; + mid_y = ( ras.y + to_y + + 3 * UPSCALE( control1->y + control2->y ) ) / 8; + + return render_line( RAS_VAR_ mid_x, mid_y ) || + render_line( RAS_VAR_ to_x, to_y ); + } + + arc = ras.bez_stack; + arc[0].x = UPSCALE( to->x ); + arc[0].y = UPSCALE( to->y ); + arc[1].x = UPSCALE( control2->x ); + arc[1].y = UPSCALE( control2->y ); + arc[2].x = UPSCALE( control1->x ); + arc[2].y = UPSCALE( control1->y ); + arc[3].x = ras.x; + arc[3].y = ras.y; + + levels = ras.lev_stack; + top = 0; + levels[0] = level; + + while ( top >= 0 ) + { + level = levels[top]; + if ( level > 1 ) + { + /* check that the arc crosses the current band */ + TPos min, max, y; + + + min = max = arc[0].y; + y = arc[1].y; + if ( y < min ) min = y; + if ( y > max ) max = y; + y = arc[2].y; + if ( y < min ) min = y; + if ( y > max ) max = y; + y = arc[3].y; + if ( y < min ) min = y; + if ( y > max ) max = y; + if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 ) + goto Draw; + split_cubic( arc ); + arc += 3; + top ++; + levels[top] = levels[top - 1] = level - 1; + continue; + } + + Draw: + { + TPos to_x, to_y, mid_x, mid_y; + + + to_x = arc[0].x; + to_y = arc[0].y; + mid_x = ( ras.x + to_x + 3 * ( arc[1].x + arc[2].x ) ) / 8; + mid_y = ( ras.y + to_y + 3 * ( arc[1].y + arc[2].y ) ) / 8; + + if ( render_line( RAS_VAR_ mid_x, mid_y ) || + render_line( RAS_VAR_ to_x, to_y ) ) + return 1; + top --; + arc -= 3; + } + } + return 0; + } + + + /* a macro comparing two cell pointers. Returns true if a <= b. */ +#if 1 +#define PACK( a ) ( ( (long)(a)->y << 16 ) + (a)->x ) +#define LESS_THAN( a, b ) ( PACK( a ) < PACK( b ) ) +#else /* 1 */ +#define LESS_THAN( a, b ) ( (a)->y < (b)->y || \ + ( (a)->y == (b)->y && (a)->x < (b)->x ) ) +#endif /* 1 */ + +#define SWAP_CELLS( a, b, temp ) do \ + { \ + temp = *(a); \ + *(a) = *(b); \ + *(b) = temp; \ + } while ( 0 ) +#define DEBUG_SORT +#define QUICK_SORT + +#ifdef SHELL_SORT + + /* A simple shell sort algorithm that works directly on our */ + /* cells table.. */ + static + void shell_sort ( PCell cells, + int count ) + { + PCell i, j, limit = cells + count; + TCell temp; + int gap; + + + /* compute initial gap */ + for ( gap = 0; ++gap < count; gap *= 3 ) + ; + + while ( gap /= 3 ) + { + for ( i = cells + gap; i < limit; i++ ) + { + for ( j = i - gap; ; j -= gap ) + { + PCell k = j + gap; + + + if ( LESS_THAN( j, k ) ) + break; + + SWAP_CELLS( j, k, temp ); + + if ( j < cells + gap ) + break; + } + } + } + } + +#endif /* SHELL_SORT */ + + +#ifdef QUICK_SORT + + /* This is a non-recursive quicksort that directly process our cells */ + /* array. It should be faster than calling the stdlib qsort(), and we */ + /* can even tailor our insertion threshold... */ + +#define QSORT_THRESHOLD 9 /* below this size, a sub-array will be sorted */ + /* through a normal insertion sort.. */ + + static + void quick_sort( PCell cells, + int count ) + { + PCell stack[40]; /* should be enough ;-) */ + PCell* top; /* top of stack */ + PCell base, limit; + TCell temp; + + + limit = cells + count; + base = cells; + top = stack; + + for (;;) + { + int len = limit - base; + PCell i, j, pivot; + + + if ( len > QSORT_THRESHOLD ) + { + /* we use base + len/2 as the pivot */ + pivot = base + len / 2; + SWAP_CELLS( base, pivot, temp ); + + i = base + 1; + j = limit - 1; + + /* now ensure that *i <= *base <= *j */ + if ( LESS_THAN( j, i ) ) + SWAP_CELLS( i, j, temp ); + + if ( LESS_THAN( base, i ) ) + SWAP_CELLS( base, i, temp ); + + if ( LESS_THAN( j, base ) ) + SWAP_CELLS( base, j, temp ); + + for (;;) + { + do i++; while ( LESS_THAN( i, base ) ); + do j--; while ( LESS_THAN( base, j ) ); + + if ( i > j ) + break; + + SWAP_CELLS( i, j, temp ); + } + + SWAP_CELLS( base, j, temp ); + + /* now, push the largest sub-array */ + if ( j - base > limit - i ) + { + top[0] = base; + top[1] = j; + base = i; + } + else + { + top[0] = i; + top[1] = limit; + limit = j; + } + top += 2; + } + else + { + /* the sub-array is small, perform insertion sort */ + j = base; + i = j + 1; + + for ( ; i < limit; j = i, i++ ) + { + for ( ; LESS_THAN( j + 1, j ); j-- ) + { + SWAP_CELLS( j + 1, j, temp ); + if ( j == base ) + break; + } + } + if ( top > stack ) + { + top -= 2; + base = top[0]; + limit = top[1]; + } + else + break; + } + } + } + +#endif /* QUICK_SORT */ + + +#ifdef DEBUG_GRAYS +#ifdef DEBUG_SORT + + static + int check_sort( PCell cells, + int count ) + { + PCell p, q; + + + for ( p = cells + count - 2; p >= cells; p-- ) + { + q = p + 1; + if ( !LESS_THAN( p, q ) ) + return 0; + } + return 1; + } + +#endif /* DEBUG_SORT */ +#endif /* DEBUG_GRAYS */ + + + static + int Move_To( FT_Vector* to, + FT_Raster raster ) + { + TPos x, y; + + + /* record current cell, if any */ + record_cell( (PRaster)raster ); + + /* start to a new position */ + x = UPSCALE( to->x ); + y = UPSCALE( to->y ); + start_cell( (PRaster)raster, TRUNC( x ), TRUNC( y ) ); + ((PRaster)raster)->x = x; + ((PRaster)raster)->y = y; + return 0; + } + + + static + int Line_To( FT_Vector* to, + FT_Raster raster ) + { + return render_line( (PRaster)raster, + UPSCALE( to->x ), UPSCALE( to->y ) ); + } + + + static + int Conic_To( FT_Vector* control, + FT_Vector* to, + FT_Raster raster ) + { + return render_conic( (PRaster)raster, control, to ); + } + + + static + int Cubic_To( FT_Vector* control1, + FT_Vector* control2, + FT_Vector* to, + FT_Raster raster ) + { + return render_cubic( (PRaster)raster, control1, control2, to ); + } + + + static + void grays_render_span( int y, + int count, + FT_Span* spans, + PRaster raster ) + { + unsigned char* p; + FT_Bitmap* map = &raster->target; + + + /* first of all, compute the scanline offset */ + p = (unsigned char*)map->buffer - y * map->pitch; + if ( map->pitch >= 0 ) + p += ( map->rows - 1 ) * map->pitch; + + for ( ; count > 0; count--, spans++ ) + { + if ( spans->coverage ) +#if 1 + memset( p + spans->x, (unsigned char)spans->coverage, spans->len ); +#else /* 1 */ + { + q = p + spans->x; + limit = q + spans->len; + for ( ; q < limit; q++ ) + q[0] = (unsigned char)spans->coverage; + } +#endif /* 1 */ + } + } + + +#ifdef DEBUG_GRAYS + +#include + + static + void dump_cells( RAS_ARG ) + { + PCell cell, limit; + int y = -1; + + + cell = ras.cells; + limit = cell + ras.num_cells; + + for ( ; cell < limit; cell++ ) + { + if ( cell->y != y ) + { + fprintf( stderr, "\n%2d: ", cell->y ); + y = cell->y; + } + fprintf( stderr, "[%d %d %d]", + cell->x, cell->area, cell->cover ); + } + fprintf(stderr, "\n" ); + } + +#endif /* DEBUG_GRAYS */ + + + static + void grays_hline( RAS_ARG_ TScan x, + TScan y, + TPos area, + int acount ) + { + FT_Span* span; + int count; + int coverage; + + + /* compute the coverage line's coverage, depending on the */ + /* outline fill rule */ + /* */ + /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */ + /* */ + coverage = area >> ( PIXEL_BITS * 2 + 1 - 8); /* use range 0..256 */ + + if ( ras.outline.flags & ft_outline_even_odd_fill ) + { + if ( coverage < 0 ) + coverage = -coverage; + + while ( coverage >= 512 ) + coverage -= 512; + + if ( coverage > 256 ) + coverage = 512 - coverage; + else if ( coverage == 256 ) + coverage = 255; + } + else + { + /* normal non-zero winding rule */ + if ( coverage < 0 ) + coverage = -coverage; + + if ( coverage >= 256 ) + coverage = 255; + } + + y += ras.min_ey; + x += ras.min_ex; + + if ( coverage ) + { + /* see if we can add this span to the current list */ + count = ras.num_gray_spans; + span = ras.gray_spans + count - 1; + if ( count > 0 && + ras.span_y == y && + (int)span->x + span->len == (int)x && + span->coverage == coverage ) + { + span->len += acount; + return; + } + + if ( ras.span_y != y || count >= FT_MAX_GRAY_SPANS ) + { + if ( ras.render_span ) + ras.render_span( ras.span_y, count, ras.gray_spans, + ras.render_span_data ); + /* ras.render_span( span->y, ras.gray_spans, count ); */ + +#ifdef DEBUG_GRAYS + + if ( ras.span_y >= 0 ) + { + int n; + + + fprintf( stderr, "y=%3d ", ras.span_y ); + span = ras.gray_spans; + for ( n = 0; n < count; n++, span++ ) + fprintf( stderr, "[%d..%d]:%02x ", + span->x, span->x + span->len - 1, span->coverage ); + fprintf( stderr, "\n" ); + } + +#endif /* DEBUG_GRAYS */ + + ras.num_gray_spans = 0; + ras.span_y = y; + + count = 0; + span = ras.gray_spans; + } + else + span++; + + /* add a gray span to the current list */ + span->x = (short)x; + span->len = (unsigned short)acount; + span->coverage = (unsigned char)coverage; + ras.num_gray_spans++; + } + } + + + static + void grays_sweep( RAS_ARG_ FT_Bitmap* target ) + { + TScan x, y, cover, area; + PCell start, cur, limit; + + UNUSED( target ); + + + cur = ras.cells; + limit = cur + ras.num_cells; + + cover = 0; + ras.span_y = -1; + ras.num_gray_spans = 0; + + for (;;) + { + start = cur; + y = start->y; + x = start->x; + + area = start->area; + cover += start->cover; + + /* accumulate all start cells */ + for (;;) + { + ++cur; + if ( cur >= limit || cur->y != start->y || cur->x != start->x ) + break; + + area += cur->area; + cover += cur->cover; + } + + /* if the start cell has a non-null area, we must draw an */ + /* individual gray pixel there */ + if ( area && x >= 0 ) + { + grays_hline( RAS_VAR_ x, y, cover * ( ONE_PIXEL * 2 ) - area, 1 ); + x++; + } + + if ( x < 0 ) + x = 0; + + if ( cur < limit && start->y == cur->y ) + { + /* draw a gray span between the start cell and the current one */ + if ( cur->x > x ) + grays_hline( RAS_VAR_ x, y, + cover * ( ONE_PIXEL * 2 ), cur->x - x ); + } + else + { + /* draw a gray span until the end of the clipping region */ + if ( cover && x < ras.max_ex - ras.min_ex ) + grays_hline( RAS_VAR_ x, y, + cover * ( ONE_PIXEL * 2 ), + ras.max_ex - x - ras.min_ex ); + cover = 0; + } + + if ( cur >= limit ) + break; + } + + if ( ras.render_span && ras.num_gray_spans > 0 ) + ras.render_span( ras.span_y, ras.num_gray_spans, + ras.gray_spans, ras.render_span_data ); + +#ifdef DEBUG_GRAYS + + { + int n; + FT_Span* span; + + + fprintf( stderr, "y=%3d ", ras.span_y ); + span = ras.gray_spans; + for ( n = 0; n < ras.num_gray_spans; n++, span++ ) + fprintf( stderr, "[%d..%d]:%02x ", + span->x, span->x + span->len - 1, span->coverage ); + fprintf( stderr, "\n" ); + } + +#endif /* DEBUG_GRAYS */ + + } + + +#ifdef _STANDALONE_ + + /*************************************************************************/ + /* */ + /* The following function should only compile in stand_alone mode, */ + /* i.e., when building this component without the rest of FreeType. */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* */ + /* FT_Outline_Decompose */ + /* */ + /* */ + /* Walks over an outline's structure to decompose it into individual */ + /* segments and Bezier arcs. This function is also able to emit */ + /* `move to' and `close to' operations to indicate the start and end */ + /* of new contours in the outline. */ + /* */ + /* */ + /* outline :: A pointer to the source target. */ + /* */ + /* interface :: A table of `emitters', i.e,. function pointers called */ + /* during decomposition to indicate path operations. */ + /* */ + /* user :: A typeless pointer which is passed to each emitter */ + /* during the decomposition. It can be used to store */ + /* the state during the decomposition. */ + /* */ + /* */ + /* Error code. 0 means sucess. */ + /* */ + static + int FT_Outline_Decompose( FT_Outline* outline, + FT_Outline_Funcs* interface, + void* user ) + { +#undef SCALED +#define SCALED( x ) ( ( (x) << shift ) - delta ) + + FT_Vector v_last; + FT_Vector v_control; + FT_Vector v_start; + + FT_Vector* point; + FT_Vector* limit; + char* tags; + + int n; /* index of contour in outline */ + int first; /* index of first point in contour */ + int error; + char tag; /* current point's state */ + + int shift = interface->shift; + FT_Pos delta = interface->delta; + + + first = 0; + + for ( n = 0; n < outline->n_contours; n++ ) + { + int last; /* index of last point in contour */ + + + last = outline->contours[n]; + limit = outline->points + last; + + v_start = outline->points[first]; + v_last = outline->points[last]; + + v_start.x = SCALED( v_start.x ); v_start.y = SCALED( v_start.y ); + v_last.x = SCALED( v_last.x ); v_last.y = SCALED( v_last.y ); + + v_control = v_start; + + point = outline->points + first; + tags = outline->tags + first; + tag = FT_CURVE_TAG( tags[0] ); + + /* A contour cannot start with a cubic control point! */ + if ( tag == FT_Curve_Tag_Cubic ) + goto Invalid_Outline; + + /* check first point to determine origin */ + if ( tag == FT_Curve_Tag_Conic ) + { + /* first point is conic control. Yes, this happens. */ + if ( FT_CURVE_TAG( 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--; + } + + error = interface->move_to( &v_start, user ); + if ( error ) + goto Exit; + + while ( point < limit ) + { + point++; + tags++; + + tag = FT_CURVE_TAG( tags[0] ); + switch ( tag ) + { + case FT_Curve_Tag_On: /* emit a single line_to */ + { + FT_Vector vec; + + + vec.x = SCALED( point->x ); + vec.y = SCALED( point->y ); + + error = interface->line_to( &vec, user ); + if ( error ) + goto Exit; + continue; + } + + case FT_Curve_Tag_Conic: /* consume conic arcs */ + { + v_control.x = SCALED( point->x ); + v_control.y = SCALED( point->y ); + + Do_Conic: + if ( point < limit ) + { + FT_Vector vec; + FT_Vector v_middle; + + + point++; + tags++; + tag = FT_CURVE_TAG( tags[0] ); + + vec.x = SCALED( point->x ); + vec.y = SCALED( point->y ); + + if ( tag == FT_Curve_Tag_On ) + { + error = interface->conic_to( &v_control, &vec, user ); + if ( error ) + goto Exit; + continue; + } + + if ( tag != FT_Curve_Tag_Conic ) + goto Invalid_Outline; + + v_middle.x = ( v_control.x + vec.x ) / 2; + v_middle.y = ( v_control.y + vec.y ) / 2; + + error = interface->conic_to( &v_control, &v_middle, user ); + if ( error ) + goto Exit; + + v_control = vec; + goto Do_Conic; + } + + error = interface->conic_to( &v_control, &v_start, user ); + goto Close; + } + + default: /* FT_Curve_Tag_Cubic */ + { + FT_Vector vec1, vec2; + + + if ( point + 1 > limit || + FT_CURVE_TAG( tags[1] ) != FT_Curve_Tag_Cubic ) + goto Invalid_Outline; + + point += 2; + tags += 2; + + vec1.x = SCALED( point[-2].x ); vec1.y = SCALED( point[-2].y ); + vec2.x = SCALED( point[-1].x ); vec2.y = SCALED( point[-1].y ); + + if ( point <= limit ) + { + FT_Vector vec; + + + vec.x = SCALED( point->x ); + vec.y = SCALED( point->y ); + + error = interface->cubic_to( &vec1, &vec2, &vec, user ); + if ( error ) + goto Exit; + continue; + } + + error = interface->cubic_to( &vec1, &vec2, &v_start, user ); + goto Close; + } + } + } + + /* close the contour with a line segment */ + error = interface->line_to( &v_start, user ); + + Close: + if ( error ) + goto Exit; + + first = last + 1; + } + + return 0; + + Exit: + return error; + + Invalid_Outline: + return ErrRaster_Invalid_Outline; + } + +#endif /* _STANDALONE_ */ + + + typedef struct TBand_ + { + FT_Pos min, max; + + } TBand; + + + static + int grays_convert_glyph( RAS_ARG_ FT_Outline* outline ) + { + static + FT_Outline_Funcs interface = + { + (FT_Outline_MoveTo_Func)Move_To, + (FT_Outline_LineTo_Func)Line_To, + (FT_Outline_ConicTo_Func)Conic_To, + (FT_Outline_CubicTo_Func)Cubic_To, + 0, + 0 + }; + + TBand bands[40], *band; + int n, num_bands; + TPos min, max, max_y; + + + /* Set up state in the raster object */ + compute_cbox( RAS_VAR_ outline ); + + /* clip to target bitmap, exit if nothing to do */ + if ( ras.max_ex <= 0 || ras.min_ex >= ras.target.width || + ras.max_ey <= 0 || ras.min_ey >= ras.target.rows ) + return 0; + + if ( ras.min_ex < 0 ) ras.min_ex = 0; + if ( ras.min_ey < 0 ) ras.min_ey = 0; + + if ( ras.max_ex > ras.target.width ) ras.max_ex = ras.target.width; + if ( ras.max_ey > ras.target.rows ) ras.max_ey = ras.target.rows; + + /* simple heuristic used to speed-up the bezier decomposition */ + /* see the code in render_conic and render_cubic for more details */ + ras.conic_level = 32; + ras.cubic_level = 16; + + { + int level = 0; + + + if ( ras.max_ex > 24 || ras.max_ey > 24 ) + level++; + if ( ras.max_ex > 120 || ras.max_ey > 120 ) + level += 2; + + ras.conic_level <<= level; + ras.cubic_level <<= level; + } + + /* setup vertical bands */ + num_bands = ( ras.max_ey - ras.min_ey ) / ras.band_size; + if ( num_bands == 0 ) num_bands = 1; + if ( num_bands >= 39 ) num_bands = 39; + + ras.band_shoot = 0; + + min = ras.min_ey; + max_y = ras.max_ey; + + for ( n = 0; n < num_bands; n++, min = max ) + { + max = min + ras.band_size; + if ( n == num_bands - 1 || max > max_y ) + max = max_y; + + bands[0].min = min; + bands[0].max = max; + band = bands; + + while ( band >= bands ) + { + FT_Pos bottom, top, middle; + int error; + + + ras.num_cells = 0; + ras.invalid = 1; + ras.min_ey = band->min; + ras.max_ey = band->max; + + error = FT_Outline_Decompose( outline, &interface, &ras ) || + record_cell( RAS_VAR ); + + if ( !error ) + { +#ifdef SHELL_SORT + shell_sort( ras.cells, ras.num_cells ); +#else + quick_sort( ras.cells, ras.num_cells ); +#endif + +#ifdef DEBUG_GRAYS + check_sort( ras.cells, ras.num_cells ); + dump_cells( RAS_VAR ); +#endif + + grays_sweep( RAS_VAR_ &ras.target ); + band--; + continue; + } + + /* render pool overflow, we will reduce the render band by half */ + bottom = band->min; + top = band->max; + middle = bottom + ( ( top - bottom ) >> 1 ); + + /* waoow! This is too complex for a single scanline, something */ + /* must be really rotten here! */ + if ( middle == bottom ) + { +#ifdef DEBUG_GRAYS + fprintf( stderr, "Rotten glyph!\n" ); +#endif + return 1; + } + + if ( bottom-top >= ras.band_size ) + ras.band_shoot++; + + band[1].min = bottom; + band[1].max = middle; + band[0].min = middle; + band[0].max = top; + band++; + } + } + + if ( ras.band_shoot > 8 && ras.band_size > 16 ) + ras.band_size = ras.band_size / 2; + + return 0; + } + + + extern + int grays_raster_render( PRaster raster, + FT_Raster_Params* params ) + { + FT_Outline* outline = (FT_Outline*)params->source; + FT_Bitmap* target_map = params->target; + + + if ( !raster || !raster->cells || !raster->max_cells ) + return -1; + + /* return immediately if the outline is empty */ + if ( outline->n_points == 0 || outline->n_contours <= 0 ) + return 0; + + if ( !outline || !outline->contours || !outline->points ) + return ErrRaster_Invalid_Outline; + + if ( outline->n_points != + outline->contours[outline->n_contours - 1] + 1 ) + return ErrRaster_Invalid_Outline; + + if ( !target_map || !target_map->buffer ) + return -1; + + /* XXXX: this version does not support monochrome rendering yet! */ + if ( !(params->flags & ft_raster_flag_aa) ) + return ErrRaster_Invalid_Mode; + + ras.outline = *outline; + ras.target = *target_map; + ras.num_cells = 0; + ras.invalid = 1; + + ras.render_span = (FT_Raster_Span_Func)grays_render_span; + ras.render_span_data = &ras; + + if ( params->flags & ft_raster_flag_direct ) + { + ras.render_span = (FT_Raster_Span_Func)params->gray_spans; + ras.render_span_data = params->user; + } + + return grays_convert_glyph( (PRaster)raster, outline ); + } + + + /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/ + /**** a static object. *****/ + +#ifdef _STANDALONE_ + + static + int grays_raster_new( void* memory, + FT_Raster* araster ) + { + static TRaster the_raster; + + UNUSED( memory ); + + + *araster = (FT_Raster)&the_raster; + memset( &the_raster, 0, sizeof ( the_raster ) ); + + return 0; + } + + + static + void grays_raster_done( FT_Raster raster ) + { + /* nothing */ + UNUSED( raster ); + } + +#else /* _STANDALONE_ */ + + static + int grays_raster_new( FT_Memory memory, + FT_Raster* araster ) + { + FT_Error error; + PRaster raster; + + + *araster = 0; + if ( !ALLOC( raster, sizeof ( TRaster ) ) ) + { + raster->memory = memory; + *araster = (FT_Raster)raster; + } + + return error; + } + + + static + void grays_raster_done( FT_Raster raster ) + { + FT_Memory memory = (FT_Memory)((PRaster)raster)->memory; + + + FREE( raster ); + } + +#endif /* _STANDALONE_ */ + + + static + void grays_raster_reset( FT_Raster raster, + const char* pool_base, + long pool_size ) + { + PRaster rast = (PRaster)raster; + + + if ( raster && pool_base && pool_size >= 4096 ) + init_cells( rast, (char*)pool_base, pool_size ); + + rast->band_size = ( pool_size / sizeof ( TCell ) ) / 8; + } + + + FT_Raster_Funcs ft_grays_raster = + { + ft_glyph_format_outline, + + (FT_Raster_New_Func) grays_raster_new, + (FT_Raster_Reset_Func) grays_raster_reset, + (FT_Raster_Set_Mode_Func)0, + (FT_Raster_Render_Func) grays_raster_render, + (FT_Raster_Done_Func) grays_raster_done + }; + + +/* END */ diff --git a/src/smooth/ftgrays.h b/src/smooth/ftgrays.h new file mode 100644 index 000000000..feb74843b --- /dev/null +++ b/src/smooth/ftgrays.h @@ -0,0 +1,49 @@ +/***************************************************************************/ +/* */ +/* ftgrays.h */ +/* */ +/* FreeType smooth renderer declaration */ +/* */ +/* 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 FTGRAYS_H +#define FTGRAYS_H + +#ifdef __cplusplus + extern "C" { +#endif + +#ifdef _STANDALONE_ +#include "ftimage.h" +#else +#include +#endif + + /*************************************************************************/ + /* */ + /* To make ftgrays.h independent from configuration files we check */ + /* whether FT_EXPORT_DEF has been defined already. */ + /* */ + /* On some systems and compilers (Win32 mostly), an extra keyword is */ + /* necessary to compile the library as a DLL. */ + /* */ +#ifndef FT_EXPORT_VAR +#define FT_EXPORT_VAR(x) extern x +#endif + + FT_EXPORT_VAR(FT_Raster_Funcs) ft_grays_raster; + + #ifdef __cplusplus + } + #endif + +#endif diff --git a/src/smooth/ftsmooth.c b/src/smooth/ftsmooth.c new file mode 100644 index 000000000..0b25d44e1 --- /dev/null +++ b/src/smooth/ftsmooth.c @@ -0,0 +1,176 @@ +#include +#include +#include + + /* initialise renderer - init its raster */ + static FT_Error ft_smooth_init( FT_Renderer render ) + { + FT_Library library = FT_MODULE_LIBRARY(render); + + render->clazz->raster_class->raster_reset( render->raster, + library->raster_pool, library->raster_pool_size ); + + return 0; + } + + + + /* sets render-specific mode */ + static FT_Error ft_smooth_set_mode( FT_Renderer render, + FT_ULong mode_tag, + FT_Pointer data ) + { + /* we simply pass it to the raster */ + return render->clazz->raster_class->raster_set_mode( + render->raster, mode_tag, data ); + } + + /* transform a given glyph image */ + static FT_Error ft_smooth_transform( FT_Renderer render, + FT_GlyphSlot slot, + FT_Matrix* matrix, + FT_Vector* delta ) + { + FT_Error error = FT_Err_Ok; + + if (slot->format != render->glyph_format) + { + error = FT_Err_Invalid_Argument; + goto Exit; + } + + if (matrix) + FT_Outline_Transform( &slot->outline, matrix ); + + if (delta) + FT_Outline_Translate( &slot->outline, delta->x, delta->y ); + + Exit: + return error; + } + + /* return the glyph's control box */ + static void ft_smooth_get_cbox( FT_Renderer render, + FT_GlyphSlot slot, + FT_BBox *cbox ) + { + MEM_Set( cbox, 0, sizeof(*cbox) ); + + if (slot->format == render->glyph_format) + FT_Outline_Get_CBox( &slot->outline, cbox ); + } + + + /* convert a slot's glyph image into a bitmap */ + static FT_Error ft_smooth_render( FT_Renderer render, + FT_GlyphSlot slot, + FT_UInt mode, + FT_Vector* origin ) + { + FT_Error error; + FT_Outline* outline; + FT_BBox cbox; + FT_UInt width, height, pitch; + FT_Bitmap* bitmap; + FT_Memory memory; + + FT_Raster_Params params; + + /* check glyph image format */ + if (slot->format != render->glyph_format) + { + error = FT_Err_Invalid_Argument; + goto Exit; + } + + /* check mode */ + if ( !(mode & ft_render_mode_antialias) ) + return FT_Err_Cannot_Render_Glyph; + + outline = &slot->outline; + + /* translate the outline to the new origin if needed */ + if (origin) + FT_Outline_Translate( outline, origin->x, origin->y ); + + /* compute the control box, and grid fit it */ + FT_Outline_Get_CBox( outline, &cbox ); + + cbox.xMin &= -64; + cbox.yMin &= -64; + cbox.xMax = (cbox.xMax+63) & -64; + cbox.yMax = (cbox.yMax+63) & -64; + + width = (cbox.xMax - cbox.xMin) >> 6; + height = (cbox.yMax - cbox.yMin) >> 6; + bitmap = &slot->bitmap; + memory = slot->face->memory; + + /* release old bitmap buffer */ + if ((slot->flags & ft_glyph_own_bitmap)) + { + FREE(bitmap->buffer); + slot->flags &= ~ft_glyph_own_bitmap; + } + + /* allocate new one, depends on pixel format */ + pitch = width; + bitmap->pixel_mode = ft_pixel_mode_grays; + bitmap->num_grays = 256; + bitmap->width = width; + bitmap->rows = height; + bitmap->pitch = pitch; + + if (ALLOC( bitmap->buffer, (FT_ULong)pitch * height )) + goto Exit; + + slot->flags |= ft_glyph_own_bitmap; + + /* translate outline to render it into the bitmap */ + FT_Outline_Translate( outline, -cbox.xMin, -cbox.yMin ); + + /* set up parameters */ + params.target = bitmap; + params.source = outline; + params.flags = ft_raster_flag_aa; + + /* render outline into the bitmap */ + error = render->raster_render( render->raster, ¶ms ); + if (error) goto Exit; + + slot->format = ft_glyph_format_bitmap; + slot->bitmap_left = cbox.xMin >> 6; + slot->bitmap_top = cbox.yMax >> 6; + + Exit: + return error; + } + + + const FT_Renderer_Class ft_smooth_renderer_class = + { + { + ft_module_renderer, + sizeof( FT_RendererRec ), + + "smooth", + 0x10000, + 0x20000, + + 0, /* module specific interface */ + + (FT_Module_Constructor) ft_smooth_init, + (FT_Module_Destructor) 0, + (FT_Module_Requester) 0 + }, + + ft_glyph_format_outline, + + (FTRenderer_render) ft_smooth_render, + (FTRenderer_transform) ft_smooth_transform, + (FTRenderer_getCBox) ft_smooth_get_cbox, + (FTRenderer_setMode) ft_smooth_set_mode, + + (FT_Raster_Funcs*) &ft_grays_raster + }; + diff --git a/src/smooth/ftsmooth.h b/src/smooth/ftsmooth.h new file mode 100644 index 000000000..67e0ecc90 --- /dev/null +++ b/src/smooth/ftsmooth.h @@ -0,0 +1,8 @@ +#ifndef FTSMOOTH_H +#define FTSMOOTH_H + +#include + + FT_EXPORT_VAR(const FT_Renderer_Class) ft_smooth_renderer_class; + +#endif /* FTSMOOTH_H */ diff --git a/src/smooth/module.mk b/src/smooth/module.mk new file mode 100644 index 000000000..b93bc4d3d --- /dev/null +++ b/src/smooth/module.mk @@ -0,0 +1,7 @@ +make_module_list: add_smooth_renderer + +add_smooth_renderer: + $(OPEN_DRIVER)ft_smooth_renderer_class$(CLOSE_DRIVER) + $(ECHO_DRIVER)smooth $(ECHO_DRIVER_DESC)anti-aliased bitmap renderer$(ECHO_DRIVER_DONE) + +# EOF diff --git a/src/smooth/rules.mk b/src/smooth/rules.mk new file mode 100644 index 000000000..e0a08099e --- /dev/null +++ b/src/smooth/rules.mk @@ -0,0 +1,74 @@ +# +# FreeType 2 renderer module build rules +# + + +# 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. + + +# renderer driver directory +# +SMOOTH_DIR := $(SRC_)smooth +SMOOTH_DIR_ := $(SMOOTH_DIR)$(SEP) + + +# additional include flags used when compiling the driver +# +SMOOTH_INCLUDE := $(SMOOTH_DIR) + +# compilation flags for the driver +# +SMOOTH_CFLAGS := $(SMOOTH_INCLUDE:%=$I%) +SMOOTH_COMPILE := $(FT_COMPILE) $(SMOOTH_CFLAGS) + + +# SMOOTH driver sources (i.e., C files) +# +SMOOTH_DRV_SRC := $(SMOOTH_DIR_)ftgrays.c \ + $(SMOOTH_DIR_)ftsmooth.c + +# SMOOTH driver headers +# +SMOOTH_DRV_H := $(SMOOTH_DRV_SRC:%c=%h) + + +# SMOOTH driver object(s) +# +# SMOOTH_DRV_OBJ_M is used during `multi' builds. +# SMOOTH_DRV_OBJ_S is used during `single' builds. +# +SMOOTH_DRV_OBJ_M := $(SMOOTH_DRV_SRC:$(SMOOTH_DIR_)%.c=$(OBJ_)%.$O) +SMOOTH_DRV_OBJ_S := $(OBJ_)smooth.$O + +# SMOOTH driver source file for single build +# +SMOOTH_DRV_SRC_S := $(SMOOTH_DIR_)smooth.c + + +# SMOOTH driver - single object +# +$(SMOOTH_DRV_OBJ_S): $(SMOOTH_DRV_SRC_S) $(SMOOTH_DRV_SRC) \ + $(FREETYPE_H) $(SMOOTH_DRV_H) + $(SMOOTH_COMPILE) $T$@ $(SMOOTH_DRV_SRC_S) + + +# SMOOTH driver - multiple objects +# +$(OBJ_)%.$O: $(SMOOTH_DIR_)%.c $(FREETYPE_H) $(SMOOTH_DRV_H) + $(SMOOTH_COMPILE) $T$@ $< + + +# update main driver object lists +# +DRV_OBJS_S += $(SMOOTH_DRV_OBJ_S) +DRV_OBJS_M += $(SMOOTH_DRV_OBJ_M) + + +# EOF diff --git a/src/smooth/smooth.c b/src/smooth/smooth.c new file mode 100644 index 000000000..2657c0279 --- /dev/null +++ b/src/smooth/smooth.c @@ -0,0 +1,4 @@ +#define FT_MAKE_OPTION_SINGLE_OBJECT +#include +#include + diff --git a/src/smooth/smooth.h b/src/smooth/smooth.h new file mode 100644 index 000000000..091d1ec5a --- /dev/null +++ b/src/smooth/smooth.h @@ -0,0 +1,14 @@ +#ifndef RENDERER_H +#define RENDERER_H + +#include + +#ifndef FT_CONFIG_OPTION_NO_STD_RASTER + FT_EXPORT_VAR(const FT_Renderer_Class) ft_std_renderer_class; +#endif + +#ifndef FT_CONFIG_OPTION_NO_SMOOTH_RASTER + FT_EXPORT_VAR(const FT_Renderer_Class) ft_smooth_renderer_class; +#endif + +#endif /* RENDERER_H */