7dff772e59
Fixed cleaning rules for the demo programs.
4226 lines
157 KiB
C
4226 lines
157 KiB
C
/***************************************************************************/
|
|
/* */
|
|
/* ftraster.c */
|
|
/* */
|
|
/* The FreeType glyph rasterizer (body). */
|
|
/* */
|
|
/* Copyright 1996-2000 by */
|
|
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
|
|
/* */
|
|
/* This file is part of the FreeType project, and may only be used */
|
|
/* modified and distributed under the terms of the FreeType project */
|
|
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
|
|
/* this file you indicate that you have read the license and */
|
|
/* understand and accept it fully. */
|
|
/* */
|
|
/***************************************************************************/
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* The `raster' component implements FreeType's scan-line converter, the */
|
|
/* one used to generate bitmaps and pixmaps from vectorial outline */
|
|
/* descriptions. */
|
|
/* */
|
|
/* It has been rewritten entirely for FreeType 2.0, in order to become */
|
|
/* completely independent of the rest of the library. It should now be */
|
|
/* possible to include it more easily in all kinds of libraries and */
|
|
/* applications, which do not necessarily need the font engines and API. */
|
|
/* */
|
|
/* This version contains the following features: */
|
|
/* */
|
|
/* - Support for third-order Bezier arcs. */
|
|
/* */
|
|
/* - Improved performance of the 5-levels anti-aliasing algorithm. */
|
|
/* */
|
|
/* - 17-levels anti-aliasing for smoother curves, though the difference */
|
|
/* isn't always noticeable, depending on your palette. */
|
|
/* */
|
|
/* - An API to decompose a raster outline into a path (i.e., into a */
|
|
/* a series of segments and arcs). */
|
|
/* */
|
|
/* Planned additions: */
|
|
/* */
|
|
/* - Getting rid of the second pass for horizontal drop-out detection. */
|
|
/* I've got a few ideas, but I'll have to experiment in Pascal with */
|
|
/* them. to avoid damaging of the rendering of glyphs at small sizes. */
|
|
/* */
|
|
/* - Adding a `composition' callback, which should be invoked during */
|
|
/* anti-aliased rendering. In short, it will allow line-by-line */
|
|
/* composition (i.e., transparencies, etc.) of the output in a fairly */
|
|
/* portable way. Of course, a single sweep is required there. */
|
|
/* */
|
|
/*************************************************************************/
|
|
|
|
|
|
#include <ftraster.h>
|
|
#ifndef _STANDALONE_
|
|
#include <ftconfig.h>
|
|
#endif
|
|
|
|
#ifndef EXPORT_FUNC
|
|
#define EXPORT_FUNC /* nothing */
|
|
#endif
|
|
|
|
#undef FT_COMPONENT
|
|
#define FT_COMPONENT trace_raster
|
|
|
|
#ifdef _STANDALONE_
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* The following defines are used when the raster is compiled as a */
|
|
/* stand-alone object. Each of them is commented, and you're free to */
|
|
/* toggle them to suit your needs. */
|
|
/* */
|
|
/*************************************************************************/
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* FT_RASTER_INT_IS_32 */
|
|
/* */
|
|
/* Set this configuration macro to the unsigned type which has 32 */
|
|
/* bits. */
|
|
/* */
|
|
#define FT_RASTER_INT_IS_32
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* FT_RASTER_OPTION_ANTI_ALIAS */
|
|
/* */
|
|
/* Define this configuration macro if you want to support */
|
|
/* anti-aliasing. */
|
|
/* */
|
|
#define FT_RASTER_OPTION_ANTI_ALIAS
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* FT_RASTER_OPTION_CONIC_BEZIERS */
|
|
/* */
|
|
/* Define this configuration macro if your source outlines contain */
|
|
/* second-order Bezier arcs. Typically, these are TrueType outlines. */
|
|
/* */
|
|
#define FT_RASTER_CONIC_BEZIERS
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* FT_RASTER_OPTION_CUBIC_BEZIERS */
|
|
/* */
|
|
/* Define this configuration macro if your source outlines contain */
|
|
/* third-order Bezier arcs. Typically, these are Type1 outlines. */
|
|
/* */
|
|
#define FT_RASTER_CUBIC_BEZIERS
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* FT_RASTER_ANTI_ALIAS_5 */
|
|
/* */
|
|
/* Define this configuration macro if you want to enable the 5-grays */
|
|
/* anti-aliasing mode. Ignored if FT_RASTER_OPTION_ANTI_ALIAS isn't */
|
|
/* defined. */
|
|
/* */
|
|
#define FT_RASTER_ANTI_ALIAS_5
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* FT_RASTER_ANTI_ALIAS_17 */
|
|
/* */
|
|
/* Define this configuration macro if you want to enable the 17-grays */
|
|
/* anti-aliasing mode. Ignored if FT_RASTER_OPTION_ANTI_ALIAS isn't */
|
|
/* defined. */
|
|
/* */
|
|
/* #define FT_RASTER_ANTI_ALIAS_17 */
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* FT_RASTER_LITTLE_ENDIAN */
|
|
/* FT_RASTER_BIG_ENDIAN */
|
|
/* */
|
|
/* The default anti-alias routines are processor-independent, but */
|
|
/* slow. Define one of these macros to suit your own system, and */
|
|
/* enjoy greatly improved rendering speed. */
|
|
/* */
|
|
|
|
/* #define FT_RASTER_LITTLE_ENDIAN */
|
|
/* #define FT_RASTER_BIG_ENDIAN */
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* FT_RASTER_CONSTANT_PRECISION */
|
|
/* */
|
|
/* Define this configuration macro if you want to use a constant */
|
|
/* precision for the internal sub-pixel coordinates. Otherwise, the */
|
|
/* precision is either 64 or 1024 units per pixel, depending on the */
|
|
/* outline's "high_precision" flag.. */
|
|
/* */
|
|
/* This results in a speed boost, but the macro can be undefined if */
|
|
/* it results in rendering errors (mainly changed drop-outs).. */
|
|
/* */
|
|
#define FT_RASTER_CONSTANT_PRECISION
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* FT_PRECISION_BITS */
|
|
/* */
|
|
/* When the macro FT_RASTER_CONSTANT_PRECISION is defined, this */
|
|
/* constant holds the number of bits used for the internal sub-pixels */
|
|
/* */
|
|
/* This number should be at least 6, but use at least 8 if you */
|
|
/* intend to generate small glyph images (use 6 for a printer, for */
|
|
/* example..) */
|
|
/* */
|
|
#define FT_PRECISION_BITS 8
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* FT_DYNAMIC_BEZIER_STEPS */
|
|
/* */
|
|
/* Set this macro to enable the bezier decomposition to be */
|
|
/* dynamically computed. This is interesting when the precision is */
|
|
/* constant, as it speeds things a bit while keeping a very good */
|
|
/* accuracy on the bezier intersections.. */
|
|
/* */
|
|
#define FT_DYNAMIC_BEZIER_STEPS
|
|
|
|
|
|
#else /* _STANDALONE_ */
|
|
|
|
#include <freetype.h>
|
|
#include <ftconfig.h>
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* The following defines are used when the raster is compiled within the */
|
|
/* FreeType base layer. Don't change these unless you really know what */
|
|
/* you're doing. */
|
|
/* */
|
|
/*************************************************************************/
|
|
|
|
|
|
#ifdef FT_CONFIG_OPTION_ANTI_ALIAS
|
|
#define FT_RASTER_OPTION_ANTI_ALIAS
|
|
#endif
|
|
|
|
#define FT_RASTER_CONIC_BEZIERS
|
|
#define FT_RASTER_CUBIC_BEZIERS
|
|
|
|
#define FT_RASTER_ANTI_ALIAS_5
|
|
/* #define FT_RASTER_ANTI_ALIAS_17 */
|
|
|
|
#ifdef FT_CONFIG_OPTION_LITTLE_ENDIAN
|
|
#define FT_RASTER_LITTLE_ENDIAN
|
|
#endif
|
|
|
|
#ifdef FT_CONFIG_OPTION_BIG_ENDIAN
|
|
#define FT_RASTER_BIG_ENDIAN
|
|
#endif
|
|
|
|
#define FT_RASTER_CONSTANT_PRECISION
|
|
#define FT_DYNAMIC_BEZIER_STEPS
|
|
#define FT_PRECISION_BITS 8
|
|
|
|
#endif /* _STANDALONE_ */
|
|
|
|
|
|
/* to keep the compiler happy */
|
|
#ifndef PTRACE2
|
|
#define PTRACE2(x) /*void*/
|
|
#endif
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* FT_RASTER_ANY_ENDIAN indicates that no endianess was defined by one */
|
|
/* of the configuration macros. */
|
|
/* */
|
|
#if !defined( FT_RASTER_LITTLE_ENDIAN ) && !defined( FT_RASTER_BIG_ENDIAN )
|
|
#define FT_RASTER_ANY_ENDIAN
|
|
#endif
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* The rasterizer is a very general purpose component. Please leave the */
|
|
/* following redefinitions here (you never know your target */
|
|
/* environment). */
|
|
/* */
|
|
/*************************************************************************/
|
|
|
|
#ifndef TRUE
|
|
#define TRUE 1
|
|
#endif
|
|
|
|
#ifndef FALSE
|
|
#define FALSE 0
|
|
#endif
|
|
|
|
#ifndef NULL
|
|
#define NULL (void*)0
|
|
#endif
|
|
|
|
|
|
#ifndef UNUSED
|
|
#define UNUSED( arg ) ( (void)(arg) )
|
|
#endif
|
|
|
|
|
|
#undef FAILURE
|
|
#define FAILURE TRUE
|
|
|
|
#undef SUCCESS
|
|
#define SUCCESS FALSE
|
|
|
|
#ifndef ABS
|
|
#define ABS(x) ( (x) < 0 ? -(x) : (x) )
|
|
#endif
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Please don't touch the following macros. Their importance is */
|
|
/* historical to FreeType, but they have some nice effects, like getting */
|
|
/* rid of all `->' symbols when accessing the raster object (replacing */
|
|
/* them with a simple `.'). */
|
|
/* */
|
|
/*************************************************************************/
|
|
|
|
/* used in function signatures to define the _first_ argument */
|
|
#define RAS_ARG_ FT_Raster raster,
|
|
#define RAS_ARG FT_Raster raster
|
|
|
|
/* used to call a function within this component, first parameter */
|
|
#define RAS_VAR_ raster,
|
|
#define RAS_VAR raster
|
|
|
|
/* used to access the current raster object, with a `.' instead of a */
|
|
/* `->' */
|
|
#define ras (*raster)
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Error codes returned by the scan-line converter/raster. */
|
|
/* */
|
|
#define ErrRaster_Ok 0
|
|
#define ErrRaster_Uninitialized_Object 1
|
|
#define ErrRaster_Overflow 2
|
|
#define ErrRaster_Negative_Height 3
|
|
#define ErrRaster_Invalid_Outline 4
|
|
#define ErrRaster_Invalid_Map 5
|
|
#define ErrRaster_AntiAlias_Unsupported 6
|
|
#define ErrRaster_Invalid_Pool 7
|
|
#define ErrRaster_Unimplemented 8
|
|
#define ErrRaster_Bad_Palette_Count 9
|
|
|
|
|
|
#define Flow_Up 1
|
|
#define Flow_Down -1
|
|
|
|
#define SET_High_Precision( p ) Set_High_Precision( RAS_VAR_ p )
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Fast MulDiv, as `b' is always < 64. Don't use intermediate */
|
|
/* precision. */
|
|
/* */
|
|
#define FMulDiv( a, b, c ) ( (a) * (b) / (c) )
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Define DEBUG_RASTER if you want to generate a debug version of the */
|
|
/* rasterizer. This will progressively draw the glyphs while all the */
|
|
/* computation are done directly on the graphics screen (the glyphs will */
|
|
/* will be shown inverted). */
|
|
/* */
|
|
/* Note that DEBUG_RASTER should only be used for debugging with b/w */
|
|
/* rendering, not with gray levels. */
|
|
/* */
|
|
/* The definition of DEBUG_RASTER should appear in the file */
|
|
/* `ftconfig.h'. */
|
|
/* */
|
|
#ifdef DEBUG_RASTER
|
|
extern char* vio; /* A pointer to VRAM or display buffer */
|
|
#endif
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* The maximum number of stacked Bezier curves. Setting this constant */
|
|
/* to more than 32 is a pure waste of space. */
|
|
/* */
|
|
#define MaxBezier 32
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* The number fractional bits of *input* coordinates. We always use the */
|
|
/* 26.6 format (i.e, 6 bits for the fractional part), but hackers are */
|
|
/* free to experiment with different values. */
|
|
/* */
|
|
#define INPUT_BITS 6
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* An unsigned type that is exactly 32 bits on your platform. This */
|
|
/* means `unsigned long' on 16-bit machines, and `unsigned int' on */
|
|
/* others. */
|
|
/* */
|
|
#ifdef _STANDALONE_
|
|
#if defined( FT_RASTER_INT_IS_32 )
|
|
typedef unsigned int FT_Word32;
|
|
#elif defined( FT_RASTER_LONG_IS_32 )
|
|
typedef unsigned long FT_Word32;
|
|
#else
|
|
#error "no 32bit type found - please check your configuration"
|
|
#endif
|
|
#endif
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* A pointer to an unsigned char. */
|
|
/* */
|
|
typedef unsigned char Byte, *PByte;
|
|
typedef int TResult;
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* The type of the pixel coordinates used within the render pool during */
|
|
/* scan-line conversion. We use longs to store either 26.6 or 22.10 */
|
|
/* fixed float values, depending on the `precision' we want to use */
|
|
/* (i.e., low resp. high precision). These are ideals in order to */
|
|
/* subdivise Bezier arcs in halves by simple additions and shifts. */
|
|
/* */
|
|
/* Note that this is an 8-bytes integer on 64 bits systems. */
|
|
/* */
|
|
typedef long TPos, *PPos;
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* The type of a scanline position/coordinate within a map. */
|
|
/* */
|
|
typedef int TScan, *PScan;
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* States and directions of each line, arc, and profile. */
|
|
/* */
|
|
typedef enum _TDirection
|
|
{
|
|
Unknown,
|
|
Ascending,
|
|
Descending,
|
|
Flat
|
|
|
|
} TDirection;
|
|
|
|
|
|
struct _TProfile;
|
|
typedef struct _TProfile TProfile;
|
|
typedef TProfile* PProfile;
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* The `master' structure used for decomposing outlines. */
|
|
/* */
|
|
struct _TProfile
|
|
{
|
|
TPos X; /* current coordinate during sweep */
|
|
PProfile link; /* link to next profile - various purpose */
|
|
PPos offset; /* start of profile's data in render pool */
|
|
int flow; /* Profile orientation: Asc/Descending */
|
|
TScan height; /* profile's height in scanlines */
|
|
TScan start; /* profile's starting scanline */
|
|
|
|
TScan countL; /* number of lines to step before this */
|
|
/* profile becomes drawable */
|
|
|
|
PProfile next; /* next profile in same contour, used */
|
|
/* during drop-out control */
|
|
};
|
|
|
|
typedef PProfile TProfileList;
|
|
typedef PProfile* PProfileList;
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* A simple record used to implement a stack of bands, required by the */
|
|
/* sub-banding mechanism. */
|
|
/* */
|
|
typedef struct _TBand
|
|
{
|
|
TScan y_min; /* band's minimum */
|
|
TScan y_max; /* band's maximum */
|
|
|
|
} TBand;
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* The size in _TPos_ of a profile record in the render pool. */
|
|
/* */
|
|
#define AlignProfileSize \
|
|
( (sizeof ( TProfile ) + sizeof ( TPos ) - 1) / sizeof ( TPos ) )
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Prototypes used for sweep function dispatch. */
|
|
/* */
|
|
typedef void (*Function_Sweep_Init)( RAS_ARG_ int* min,
|
|
int* max );
|
|
|
|
typedef void (*Function_Sweep_Span)( RAS_ARG_ TScan y,
|
|
TPos x1,
|
|
TPos x2 );
|
|
|
|
typedef int (*Function_Test_Pixel)( RAS_ARG_ TScan y,
|
|
int x );
|
|
|
|
typedef void (*Function_Set_Pixel)( RAS_ARG_ TScan y,
|
|
int x,
|
|
int color );
|
|
|
|
typedef void (*Function_Sweep_Step)( RAS_ARG );
|
|
|
|
typedef struct Raster_Render_
|
|
{
|
|
Function_Sweep_Init init;
|
|
Function_Sweep_Span span;
|
|
Function_Sweep_Step step;
|
|
Function_Test_Pixel test_pixel;
|
|
Function_Set_Pixel set_pixel;
|
|
|
|
} Raster_Render;
|
|
|
|
|
|
#ifdef FT_RASTER_CONSTANT_PRECISION
|
|
|
|
#define PRECISION_BITS FT_PRECISION_BITS
|
|
#define PRECISION (1 << PRECISION_BITS)
|
|
#define PRECISION_MASK (-1L << PRECISION_BITS)
|
|
#define PRECISION_HALF (PRECISION >> 1)
|
|
#define PRECISION_JITTER (PRECISION >> 5)
|
|
#define PRECISION_STEP PRECISION_HALF
|
|
|
|
#else
|
|
|
|
#define PRECISION_BITS ras.precision_bits
|
|
#define PRECISION ras.precision
|
|
#define PRECISION_MASK ras.precision_mask
|
|
#define PRECISION_HALF ras.precision_half
|
|
#define PRECISION_JITTER ras.precision_jitter
|
|
#define PRECISION_STEP ras.precision_step
|
|
|
|
#endif
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Compute lowest integer coordinate below a given value. */
|
|
/* */
|
|
#define FLOOR( x ) ( (x) & PRECISION_MASK )
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Compute highest integer coordinate above a given value. */
|
|
/* */
|
|
#define CEILING( x ) ( ((x) + PRECISION - 1) & PRECISION_MASK )
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Get integer coordinate of a given 26.6 or 22.10 `x' coordinate -- no */
|
|
/* rounding. */
|
|
/* */
|
|
#define TRUNC( x ) ( (signed long)(x) >> PRECISION_BITS )
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Get the fractional part of a given coordinate. */
|
|
/* */
|
|
#define FRAC( x ) ( (x) & (PRECISION-1) )
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Scale an `input coordinate' (as found in FT_Outline structures) into */
|
|
/* a `work coordinate' which depends on current resolution and render */
|
|
/* mode. */
|
|
/* */
|
|
#define SCALED( x ) ( ((x) << ras.scale_shift) - ras.scale_delta )
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* DEBUG_PSET is used to plot a single pixel in VRam during debug mode. */
|
|
/* */
|
|
#ifdef DEBUG_RASTER
|
|
#define DEBUG_PSET Pset()
|
|
#else
|
|
#define DEBUG_PSET
|
|
#endif
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* This structure defines a point in a plane. */
|
|
/* */
|
|
typedef struct _TPoint
|
|
{
|
|
TPos x, y;
|
|
|
|
} TPoint;
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* The most used variables are at the beginning of the structure. Thus, */
|
|
/* their offset can be coded with less opcodes which results in a */
|
|
/* smaller executable. */
|
|
/* */
|
|
struct FT_RasterRec_
|
|
{
|
|
PPos cursor; /* Current cursor in render pool */
|
|
|
|
PPos pool; /* The render pool base address */
|
|
PPos pool_size; /* The render pool's size */
|
|
PPos pool_limit; /* Limit of profiles zone in pool */
|
|
|
|
int bit_width; /* target bitmap width */
|
|
PByte bit_buffer; /* target bitmap buffer */
|
|
PByte pix_buffer; /* target pixmap buffer */
|
|
|
|
TPoint last;
|
|
long minY, maxY;
|
|
|
|
int error;
|
|
|
|
#ifndef FT_RASTER_CONSTANT_PRECISION
|
|
int precision_bits; /* precision related variables */
|
|
int precision;
|
|
int precision_half;
|
|
long precision_mask;
|
|
int precision_shift;
|
|
int precision_step;
|
|
int precision_jitter;
|
|
#endif
|
|
|
|
FT_Outline* outline;
|
|
|
|
int n_points; /* number of points in current glyph */
|
|
int n_contours; /* number of contours in current glyph */
|
|
int n_extrema; /* number of `extrema' scanlines */
|
|
|
|
TPoint* arc; /* current Bezier arc pointer */
|
|
|
|
int num_profs; /* current number of profiles */
|
|
|
|
char fresh; /* signals a fresh new profile which */
|
|
/* `start' field must be completed */
|
|
char joint; /* signals that the last arc ended */
|
|
/* exactly on a scanline. Allows */
|
|
/* removal of doublets */
|
|
PProfile cur_prof; /* current profile */
|
|
PProfile start_prof; /* head of linked list of profiles */
|
|
PProfile first_prof; /* contour's first profile in case */
|
|
/* of impact */
|
|
TDirection state; /* rendering state */
|
|
|
|
FT_Bitmap target; /* description of target bit/pixmap */
|
|
|
|
int trace_bit; /* current offset in target bitmap */
|
|
int trace_pix; /* current offset in target pixmap */
|
|
int trace_incr; /* sweep's increment in target bitmap */
|
|
|
|
/* dispatch variables */
|
|
|
|
Raster_Render render;
|
|
|
|
int scale_shift; /* == 0 for bitmaps */
|
|
/* == 1 for 5-levels pixmaps */
|
|
/* == 2 for 17-levels pixmaps */
|
|
|
|
int scale_delta; /* ras.precision_half for bitmaps */
|
|
/* 0 for pixmaps */
|
|
|
|
char dropout_mode; /* current drop_out control method */
|
|
|
|
char second_pass; /* indicates whether a horizontal pass */
|
|
/* should be performed to control drop-out */
|
|
/* accurately when calling Render_Glyph. */
|
|
/* Note that there is no horizontal pass */
|
|
/* during gray rendering. */
|
|
|
|
char flipped; /* this flag is set during the rendering to */
|
|
/* indicate the second pass. */
|
|
|
|
TBand band_stack[16]; /* band stack used for sub-banding */
|
|
int band_top; /* band stack top */
|
|
|
|
TPoint arcs[2 * MaxBezier + 1]; /* The Bezier stack */
|
|
};
|
|
|
|
|
|
|
|
#ifdef DEBUG_RASTER
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Pset */
|
|
/* */
|
|
/* <Description> */
|
|
/* Used for debugging only. Plots a point in VRAM during rendering */
|
|
/* (not afterwards). */
|
|
/* */
|
|
/* <Note> */
|
|
/* This procedure relies on the value of cProfile->start, which may */
|
|
/* not be set when Pset() is called sometimes. This will usually */
|
|
/* result in a dot plotted on the first screen scanline (far away */
|
|
/* from its original position). */
|
|
/* */
|
|
/* This `feature' reflects nothing wrong in the current */
|
|
/* implementation, and the bitmap is rendered correctly, so don't */
|
|
/* panic if you see `flying' dots in debugging mode. */
|
|
/* */
|
|
static
|
|
void Pset( RAS_ARG )
|
|
{
|
|
long o;
|
|
long x;
|
|
|
|
|
|
x = ras.cursor[-1];
|
|
|
|
switch ( ras.cur_prof->flow )
|
|
{
|
|
case FT_Flow_Up:
|
|
o = Vio_ScanLineWidth *
|
|
( ras.cursor - ras.cur_prof->offset + ras.cur_prof->start ) +
|
|
( x / (PRECISION * 8) );
|
|
break;
|
|
|
|
case FT_Flow_Down:
|
|
o = Vio_ScanLineWidth *
|
|
( ras.cur_prof->start - ras.cursor + ras.cur_prof->offset ) +
|
|
( x / (PRECISION * 8) );
|
|
break;
|
|
}
|
|
|
|
if ( o > 0 )
|
|
Vio[o] |= (unsigned)0x80 >> ( (x/PRECISION) & 7 );
|
|
}
|
|
|
|
|
|
static
|
|
void Clear_Band( RAS_ARG_ TScan y1,
|
|
TScan y2 )
|
|
{
|
|
MEM_Set( Vio + y1*Vio_ScanLineWidth, (y2-y1+1)*Vio_ScanLineWidth, 0 );
|
|
}
|
|
|
|
#endif /* DEBUG_RASTER */
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Set_High_Precision */
|
|
/* */
|
|
/* <Description> */
|
|
/* Sets precision variables according to the parameter flag. */
|
|
/* */
|
|
/* <Input> */
|
|
/* High :: Set to True for high precision (typically for ppem < 18), */
|
|
/* false otherwise. */
|
|
/* */
|
|
static
|
|
void Set_High_Precision( RAS_ARG_ char High )
|
|
{
|
|
#ifdef FT_RASTER_CONSTANT_PRECISION
|
|
(void)High;
|
|
(void)&ras;
|
|
#else
|
|
if ( High )
|
|
{
|
|
ras.precision_bits = 10;
|
|
ras.precision_step = 128;
|
|
ras.precision_jitter = 24;
|
|
}
|
|
else
|
|
{
|
|
ras.precision_bits = 6;
|
|
ras.precision_step = 32;
|
|
ras.precision_jitter = 2;
|
|
}
|
|
|
|
ras.precision = 1L << ras.precision_bits;
|
|
ras.precision_half = ras.precision / 2;
|
|
ras.precision_shift = ras.precision_bits - INPUT_BITS;
|
|
ras.precision_mask = -ras.precision;
|
|
#endif
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* A simple technical note on how the raster works: */
|
|
/* */
|
|
/* Converting an outline into a bitmap is achieved in several steps */
|
|
/* which are: */
|
|
/* */
|
|
/* 1 - Decomposing the outline into successive `profiles'. Each */
|
|
/* profile is simply an array of scanline intersections on a given */
|
|
/* dimension. A profile's main attributes are */
|
|
/* */
|
|
/* o its scanline position boundaries, i.e. `Ymin' and `Ymax'. */
|
|
/* */
|
|
/* o an array of intersection coordinates for each scanline */
|
|
/* between `Ymin' and `Ymax'. */
|
|
/* */
|
|
/* o a direction, indicating wether is was built going `up' or */
|
|
/* `down', as this is very important for filling rules. */
|
|
/* */
|
|
/* 2 - Sweeping the target map's scanlines in order to compute segment */
|
|
/* `spans' which are then filled. Additionaly, this pass performs */
|
|
/* drop-out control. */
|
|
/* */
|
|
/* The outline data is parsed during step 1 only. The profiles are */
|
|
/* built from the bottom of the render pool, used as a stack. The */
|
|
/* following graphics shows the profile list under construction: */
|
|
/* */
|
|
/* ____________________________________________________________ _ _ */
|
|
/* | | | | | */
|
|
/* | profile | coordinates for | profile | coordinates for |--> */
|
|
/* | 1 | profile 1 | 2 | profile 2 |--> */
|
|
/* |_________|___________________|_________|_________________|__ _ _ */
|
|
/* */
|
|
/* ^ ^ */
|
|
/* | | */
|
|
/* start of render pool cursor */
|
|
/* */
|
|
/* The top of the profile stack is kept in the `cursor' variable. */
|
|
/* */
|
|
/* As you can see, a profile record is pushed on top of the render */
|
|
/* pool, which is then followed by its coordinates/intersections. If */
|
|
/* a change of direction is detected in the outline, a new profile is */
|
|
/* generated until the end of the outline. */
|
|
/* */
|
|
/* Note that when all profiles have been generated, the function */
|
|
/* Finalize_Profile_Table() is used to record, for each profile, its */
|
|
/* bottom-most scanline as well as the scanline above its upmost */
|
|
/* boundary. These positions are called `extrema' because they (sort */
|
|
/* of) correspond to local extrema. They are stored in a sorted list */
|
|
/* built from the top of the render pool as a downwards stack: */
|
|
/* */
|
|
/* _ _ _______________________________________ */
|
|
/* | | */
|
|
/* <--| sorted list of | */
|
|
/* <--| extrema scanlines | */
|
|
/* _ _ __________________|____________________| */
|
|
/* */
|
|
/* ^ ^ */
|
|
/* | | */
|
|
/* pool_limit end of render pool */
|
|
/* */
|
|
/* This list is later used during the sweep phase in order to */
|
|
/* optimize performance (see technical note on the sweep below). */
|
|
/* */
|
|
/* Of course, the raster detects whether the two stacks collide and */
|
|
/* handles the situation propertly. */
|
|
/* */
|
|
/*************************************************************************/
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* New_Profile */
|
|
/* */
|
|
/* <Description> */
|
|
/* Creates a new Profile in the render pool. */
|
|
/* */
|
|
/* <Input> */
|
|
/* aState :: The state/orientation of the new profile. */
|
|
/* */
|
|
/* <Return> */
|
|
/* SUCCESS or FAILURE. */
|
|
/* */
|
|
static
|
|
TResult New_Profile( RAS_ARG_ TDirection direction )
|
|
{
|
|
if ( ras.start_prof == NULL )
|
|
{
|
|
ras.cur_prof = (PProfile)ras.cursor; /* current profile */
|
|
ras.start_prof = ras.cur_prof; /* first profile in pool */
|
|
ras.cursor += AlignProfileSize; /* record profile in buffer */
|
|
}
|
|
|
|
/* check for overflow */
|
|
if ( ras.cursor >= ras.pool_limit )
|
|
{
|
|
ras.error = ErrRaster_Overflow;
|
|
return FAILURE;
|
|
}
|
|
|
|
/* record profile direction */
|
|
switch ( direction )
|
|
{
|
|
case Ascending:
|
|
ras.cur_prof->flow = Flow_Up;
|
|
break;
|
|
|
|
case Descending:
|
|
ras.cur_prof->flow = Flow_Down;
|
|
break;
|
|
|
|
default:
|
|
ras.error = ErrRaster_Invalid_Map;
|
|
return FAILURE;
|
|
}
|
|
|
|
/* initialize a few fields */
|
|
{
|
|
PProfile cur = ras.cur_prof;
|
|
|
|
|
|
cur->start = 0; /* current start scanline */
|
|
cur->height = 0; /* current height */
|
|
cur->offset = ras.cursor; /* address of first coordinate */
|
|
cur->link = (PProfile)0; /* link to next profile in pool */
|
|
cur->next = (PProfile)0; /* link to next profile in contour */
|
|
}
|
|
|
|
/* record the first profile in a contour */
|
|
if ( ras.first_prof == NULL )
|
|
ras.first_prof = ras.cur_prof;
|
|
|
|
ras.state = direction;
|
|
ras.fresh = TRUE; /* this profile has no coordinates yet */
|
|
ras.joint = FALSE;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* End_Profile */
|
|
/* */
|
|
/* <Description> */
|
|
/* Finalizes the current Profile and computes its height. If it is */
|
|
/* not 0, the profile's fields are updated and a new profile is */
|
|
/* pushed on top of its coordinates. Otherwise the current profile */
|
|
/* is kept and the recording of intersections is restarted. */
|
|
/* */
|
|
/* <Return> */
|
|
/* SUCCESS or FAILURE. */
|
|
/* */
|
|
static
|
|
TResult End_Profile( RAS_ARG )
|
|
{
|
|
int h;
|
|
|
|
|
|
h = ras.cursor - ras.cur_prof->offset;
|
|
|
|
if ( h < 0 )
|
|
{
|
|
/* This error should _never_ occur unless the raster is buggy */
|
|
ras.error = ErrRaster_Negative_Height;
|
|
return FAILURE;
|
|
}
|
|
|
|
if ( h > 0 )
|
|
{
|
|
PProfile old, new;
|
|
|
|
/* record scanline height in current profile, create a new one */
|
|
/* and set a link from the old one to it */
|
|
old = ras.cur_prof;
|
|
old->height = h;
|
|
ras.cur_prof = new = (PProfile)ras.cursor;
|
|
|
|
ras.cursor += AlignProfileSize;
|
|
|
|
new->height = 0;
|
|
new->offset = ras.cursor;
|
|
old->next = new;
|
|
|
|
ras.num_profs++;
|
|
}
|
|
|
|
/* check for overflow */
|
|
if ( ras.cursor >= ras.pool_limit )
|
|
{
|
|
ras.error = ErrRaster_Overflow;
|
|
return FAILURE;
|
|
}
|
|
|
|
ras.joint = FALSE;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Insert_Extrema */
|
|
/* */
|
|
/* <Description> */
|
|
/* Records that a given scanline contains at least one local */
|
|
/* extremum. The table of extrema is placed at the end of the render */
|
|
/* pool and grows downwards. It is used during the sweep phase. */
|
|
/* */
|
|
/* <Input> */
|
|
/* y :: The coordinate of the scanline containing an extremum. */
|
|
/* */
|
|
static
|
|
TResult Insert_Extrema( RAS_ARG_ TScan y )
|
|
{
|
|
PPos extrema;
|
|
TScan y2;
|
|
int n;
|
|
|
|
|
|
PTRACE2(( "EXTREMA += %d", y ));
|
|
n = ras.n_extrema - 1;
|
|
extrema = ras.pool_size - ras.n_extrema;
|
|
|
|
/* look for first y extremum that is <= */
|
|
while ( n >= 0 && y < extrema[n] )
|
|
n--;
|
|
|
|
/* if it is <, simply insert it, ignore if == */
|
|
if ( n >= 0 && y > extrema[n] )
|
|
while ( n >= 0 )
|
|
{
|
|
y2 = extrema[n];
|
|
extrema[n] = y;
|
|
y = y2;
|
|
n--;
|
|
}
|
|
|
|
if ( n < 0 )
|
|
{
|
|
ras.pool_limit--;
|
|
ras.n_extrema++;
|
|
ras.pool_size[-ras.n_extrema] = y;
|
|
|
|
if ( ras.pool_limit <= ras.cursor )
|
|
{
|
|
ras.error = ErrRaster_Overflow;
|
|
return FAILURE;
|
|
}
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Finalize_Profile_Table */
|
|
/* */
|
|
/* <Description> */
|
|
/* Adjusts all links in the profiles list. Called when the outline */
|
|
/* parsing is done. */
|
|
/* */
|
|
/* <Return> */
|
|
/* SUCCESS or FAILURE. */
|
|
/* */
|
|
static
|
|
TResult Finalize_Profile_Table( RAS_ARG )
|
|
{
|
|
int n, bottom, top;
|
|
PProfile p;
|
|
|
|
|
|
n = ras.num_profs;
|
|
|
|
if ( n > 1 )
|
|
{
|
|
p = ras.start_prof;
|
|
while ( n > 0 )
|
|
{
|
|
if ( n > 1 )
|
|
p->link = (PProfile)( p->offset + p->height );
|
|
else
|
|
p->link = NULL;
|
|
|
|
switch ( p->flow )
|
|
{
|
|
case Flow_Down:
|
|
PTRACE2(( "FLOW DOWN (start = %d, height = %d)",
|
|
p->start, p->height ));
|
|
bottom = p->start - p->height+1;
|
|
top = p->start;
|
|
p->start = bottom;
|
|
p->offset += p->height-1;
|
|
break;
|
|
|
|
case Flow_Up:
|
|
default:
|
|
PTRACE2(( "FLOW UP (start = %d, height = %d)",
|
|
p->start, p->height ));
|
|
bottom = p->start;
|
|
top = p->start + p->height-1;
|
|
}
|
|
|
|
if ( Insert_Extrema( RAS_VAR_ bottom ) ||
|
|
Insert_Extrema( RAS_VAR_ top+1 ) )
|
|
return FAILURE;
|
|
|
|
p = p->link;
|
|
n--;
|
|
}
|
|
}
|
|
else
|
|
ras.start_prof = NULL;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Line_Up */
|
|
/* */
|
|
/* <Description> */
|
|
/* Computes the scan-line intersections of an ascending line segment */
|
|
/* and stores them in the render pool. */
|
|
/* */
|
|
/* <Input> */
|
|
/* x1 :: The start x coordinate. */
|
|
/* y1 :: The start y coordinate. */
|
|
/* x2 :: The end x coordinate. */
|
|
/* y2 :: The end y coordinate. */
|
|
/* miny :: The minimum vertical grid coordinate. */
|
|
/* maxy :: The maximum vertical grid coordinate. */
|
|
/* */
|
|
/* <Return> */
|
|
/* SUCCESS or FAILURE. */
|
|
/* */
|
|
static
|
|
TResult Line_Up( RAS_ARG_ TPos x1, TPos y1,
|
|
TPos x2, TPos y2,
|
|
TPos miny, TPos maxy )
|
|
{
|
|
TPos Dx, Dy;
|
|
int e1, e2, f1, f2, size;
|
|
TPos Ix, Rx, Ax;
|
|
|
|
PPos top;
|
|
|
|
|
|
Dx = x2 - x1;
|
|
Dy = y2 - y1;
|
|
|
|
if ( Dy <= 0 || y2 < miny || y1 > maxy )
|
|
return SUCCESS;
|
|
|
|
/* clip to higher scanline when necessary */
|
|
if ( y2 > maxy )
|
|
{
|
|
/* x2 += FMulDiv( Dx, maxy-y2, Dy ); UNNECESSARY */
|
|
e2 = TRUNC( maxy );
|
|
f2 = 0;
|
|
}
|
|
else
|
|
{
|
|
e2 = TRUNC( y2 );
|
|
f2 = FRAC( y2 );
|
|
}
|
|
|
|
/* clip to lower scanline when necessary */
|
|
if ( y1 < miny )
|
|
{
|
|
TPos x, y;
|
|
|
|
/* we use a binary search to compute the lower
|
|
// clipping intersection. That's because we don't
|
|
// want to use an external function like FT_MulDiv
|
|
// to compute it directly.
|
|
*/
|
|
if ( y2 == miny ) goto Exit;
|
|
do
|
|
{
|
|
x = (x1 + x2) >> 1;
|
|
y = (y1 + y2) >> 1;
|
|
|
|
if (y <= miny)
|
|
{
|
|
x1 = x;
|
|
y1 = y;
|
|
}
|
|
else
|
|
{
|
|
x2 = x;
|
|
y2 = y;
|
|
}
|
|
}
|
|
while ( y1 < miny );
|
|
|
|
e1 = TRUNC( miny );
|
|
f1 = 0;
|
|
}
|
|
else
|
|
{
|
|
e1 = TRUNC( y1 );
|
|
f1 = FRAC( y1 );
|
|
}
|
|
|
|
/* adjust start point so that we begin on an integer scanline position */
|
|
if ( f1 > 0 )
|
|
{
|
|
if ( e1 == e2 ) goto Exit;
|
|
else
|
|
{
|
|
x1 += FMulDiv( Dx, PRECISION - f1, Dy );
|
|
e1 += 1;
|
|
}
|
|
}
|
|
else
|
|
if ( ras.joint )
|
|
{
|
|
ras.cursor--;
|
|
ras.joint = FALSE;
|
|
}
|
|
|
|
ras.joint = ( f2 == 0 );
|
|
|
|
/* if this is a `fresh' profile, record its starting scanline */
|
|
if ( ras.fresh )
|
|
{
|
|
ras.cur_prof->start = e1;
|
|
ras.fresh = FALSE;
|
|
}
|
|
|
|
/* check for overflow */
|
|
size = e2 - e1 + 1;
|
|
if ( ras.cursor + size >= ras.pool_limit )
|
|
{
|
|
ras.error = ErrRaster_Overflow;
|
|
return FAILURE;
|
|
}
|
|
|
|
/* compute decision variables and push the intersections on top */
|
|
/* of the render pool */
|
|
Dx <<= PRECISION_BITS;
|
|
Ix = Dx / Dy;
|
|
Rx = Dx % Dy;
|
|
if (Rx < 0)
|
|
{
|
|
Ix --;
|
|
Rx += Dy;
|
|
}
|
|
|
|
Ax = -Dy;
|
|
Rx <<= 1;
|
|
Dy <<= 1;
|
|
|
|
top = ras.cursor;
|
|
|
|
while ( size > 0 )
|
|
{
|
|
*top++ = x1;
|
|
|
|
DEBUG_PSET;
|
|
|
|
x1 += Ix;
|
|
Ax += Rx;
|
|
if ( Ax >= 0 )
|
|
{
|
|
Ax -= Dy;
|
|
x1 ++;
|
|
}
|
|
size--;
|
|
}
|
|
|
|
ras.cursor = top;
|
|
Exit:
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Line_Down */
|
|
/* */
|
|
/* <Description> */
|
|
/* Computes the scan-line intersections of a descending line segment */
|
|
/* and stores them in the render pool. */
|
|
/* */
|
|
/* <Input> */
|
|
/* x1 :: The start x coordinate. */
|
|
/* y1 :: The start y coordinate. */
|
|
/* x2 :: The end x coordinate. */
|
|
/* y2 :: The end y coordinate. */
|
|
/* miny :: The minimum vertical grid coordinate. */
|
|
/* maxy :: The maximum vertical grid coordinate. */
|
|
/* */
|
|
/* <Return> */
|
|
/* SUCCESS or FAILURE. */
|
|
/* */
|
|
static
|
|
TResult Line_Down( RAS_ARG_ TPos x1, TPos y1,
|
|
TPos x2, TPos y2,
|
|
TPos miny, TPos maxy )
|
|
{
|
|
TResult result, fresh;
|
|
|
|
|
|
/* simply invert the coordinates and call Line_Up */
|
|
fresh = ras.fresh;
|
|
result = Line_Up( RAS_VAR_ x1, -y1, x2, -y2, -maxy, -miny );
|
|
|
|
/* if this was a fresh profile, invert the recorded start position */
|
|
if ( fresh && !ras.fresh )
|
|
ras.cur_prof->start = -ras.cur_prof->start;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* A function type describing the functions used to split bezier arcs */
|
|
typedef void (*TSplitter)( TPoint* base );
|
|
|
|
#ifdef FT_DYNAMIC_BEZIER_STEPS
|
|
static
|
|
TPos Dynamic_Bezier_Threshold( RAS_ARG_ int degree, TPoint* arc )
|
|
{
|
|
TPos min_x, max_x, min_y, max_y, A, B;
|
|
TPos wide_x, wide_y, threshold;
|
|
TPoint* cur = arc;
|
|
TPoint* limit = cur + degree;
|
|
|
|
/* first of all, set the threshold to the maximum x or y extent */
|
|
min_x = max_x = arc[0].x;
|
|
min_y = max_y = arc[0].y;
|
|
cur++;
|
|
for ( ; cur < limit; cur++ )
|
|
{
|
|
TPos x = cur->x;
|
|
TPos y = cur->y;
|
|
|
|
if ( x < min_x ) min_x = x;
|
|
if ( x > max_x ) max_x = x;
|
|
|
|
if ( y < min_y ) min_y = y;
|
|
if ( y > max_y ) max_y = y;
|
|
}
|
|
wide_x = (max_x - min_x) << 4;
|
|
wide_y = (max_y - min_y) << 4;
|
|
|
|
threshold = wide_x;
|
|
if (threshold < wide_y) threshold = wide_y;
|
|
|
|
/* now compute the second and third order error values */
|
|
|
|
wide_x = arc[0].x + arc[1].x - arc[2].x*2;
|
|
wide_y = arc[0].y + arc[1].y - arc[2].y*2;
|
|
|
|
if (wide_x < 0) wide_x = -wide_x;
|
|
if (wide_y < 0) wide_y = -wide_y;
|
|
|
|
A = wide_x; if ( A < wide_y ) A = wide_y;
|
|
|
|
if (degree >= 3)
|
|
{
|
|
wide_x = arc[3].x - arc[0].x + 3*(arc[2].x - arc[3].x);
|
|
wide_y = arc[3].y - arc[0].y + 3*(arc[2].y - arc[3].y);
|
|
|
|
if (wide_x < 0) wide_x = -wide_x;
|
|
if (wide_y < 0) wide_y = -wide_y;
|
|
|
|
B = wide_x; if ( B < wide_y ) B = wide_y;
|
|
}
|
|
else
|
|
B = 0;
|
|
|
|
while ( A > 0 || B > 0 )
|
|
{
|
|
threshold >>= 1;
|
|
A >>= 2;
|
|
B >>= 3;
|
|
}
|
|
|
|
if (threshold < PRECISION_STEP)
|
|
threshold = PRECISION_STEP;
|
|
|
|
return threshold;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Bezier_Up */
|
|
/* */
|
|
/* <Description> */
|
|
/* Computes the scan-line intersections of an ascending second-order */
|
|
/* Bezier arc and stores them in the render pool. The arc is taken */
|
|
/* from the top of the stack. */
|
|
/* */
|
|
/* <Input> */
|
|
/* miny :: The minimum vertical grid coordinate. */
|
|
/* maxy :: The maximum vertical grid coordinate. */
|
|
/* */
|
|
/* <Return> */
|
|
/* SUCCESS or FAILURE. */
|
|
/* */
|
|
static
|
|
TResult Bezier_Up( RAS_ARG_ int degree,
|
|
TSplitter splitter,
|
|
TPos miny,
|
|
TPos maxy )
|
|
{
|
|
TPos y1, y2, e, e2, e0, threshold;
|
|
int f1;
|
|
|
|
TPoint* arc;
|
|
TPoint* start_arc;
|
|
|
|
PPos top;
|
|
|
|
|
|
arc = ras.arc;
|
|
y1 = arc[degree].y;
|
|
y2 = arc[0].y;
|
|
top = ras.cursor;
|
|
|
|
if ( y2 < miny || y1 > maxy )
|
|
goto Fin;
|
|
|
|
e2 = FLOOR( y2 ); /* integer end y */
|
|
|
|
if ( e2 > maxy )
|
|
e2 = FLOOR(maxy);
|
|
|
|
e0 = CEILING(miny);
|
|
|
|
if ( y1 < miny )
|
|
{
|
|
e = e0; /* integer start y == current scanline */
|
|
}
|
|
else
|
|
{
|
|
e = CEILING( y1 ); /* integer start y == current scanline */
|
|
f1 = FRAC( y1 ); /* fractional shift of start y */
|
|
e0 = e; /* first integer scanline to be pushed */
|
|
|
|
if ( f1 == 0 ) /* do we start on an integer scanline? */
|
|
{
|
|
if ( ras.joint )
|
|
{
|
|
top--;
|
|
ras.joint = FALSE;
|
|
}
|
|
|
|
*top++ = arc[degree].x; /* write directly start position */
|
|
|
|
DEBUG_PSET;
|
|
|
|
e += PRECISION; /* go to next scanline */
|
|
}
|
|
}
|
|
|
|
/* record start position if necessary */
|
|
if ( ras.fresh )
|
|
{
|
|
ras.cur_prof->start = TRUNC( e0 );
|
|
ras.fresh = FALSE;
|
|
}
|
|
|
|
/* exit if the current scanline is already above the max scanline */
|
|
if ( e2 < e )
|
|
goto Fin;
|
|
|
|
/* check for overflow */
|
|
if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.pool_limit )
|
|
{
|
|
ras.cursor = top;
|
|
ras.error = ErrRaster_Overflow;
|
|
return FAILURE;
|
|
}
|
|
|
|
#ifdef FT_DYNAMIC_BEZIER_STEPS
|
|
/* compute dynamic bezier step threshold */
|
|
threshold = Dynamic_Bezier_Threshold( RAS_VAR_ degree, arc );
|
|
#else
|
|
threshold = PRECISION_STEP;
|
|
#endif
|
|
|
|
start_arc = arc;
|
|
|
|
/* loop while there is still an arc on the bezier stack */
|
|
/* and the current scan line is below y max == e2 */
|
|
while ( arc >= start_arc && e <= e2 )
|
|
{
|
|
ras.joint = FALSE;
|
|
|
|
y2 = arc[0].y; /* final y of the top-most arc */
|
|
|
|
if ( y2 > e ) /* the arc intercepts the current scanline */
|
|
{
|
|
y1 = arc[degree].y; /* start y of top-most arc */
|
|
|
|
if ( y2 >= e + PRECISION || y2 - y1 >= threshold )
|
|
{
|
|
/* if the arc's height is too great, split it */
|
|
splitter( arc );
|
|
arc += degree;
|
|
}
|
|
else
|
|
{
|
|
/* otherwise, approximate it as a segment and compute */
|
|
/* its intersection with the current scanline */
|
|
*top++ = arc[degree].x +
|
|
FMulDiv( arc[0].x-arc[degree].x,
|
|
e - y1,
|
|
y2 - y1 );
|
|
|
|
DEBUG_PSET;
|
|
|
|
arc -= degree; /* pop the arc */
|
|
e += PRECISION; /* go to next scanline */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( y2 == e ) /* if the arc falls on the scanline */
|
|
{ /* record its _joint_ intersection */
|
|
ras.joint = TRUE;
|
|
*top++ = arc[0].x;
|
|
|
|
DEBUG_PSET;
|
|
|
|
e += PRECISION; /* go to next scanline */
|
|
}
|
|
arc -= degree; /* pop the arc */
|
|
}
|
|
}
|
|
|
|
Fin:
|
|
ras.cursor = top;
|
|
ras.arc -= degree;
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Bezier_Down */
|
|
/* */
|
|
/* <Description> */
|
|
/* Computes the scan-line intersections of a descending second-order */
|
|
/* Bezier arc and stores them in the render pool. The arc is taken */
|
|
/* from the top of the stack. */
|
|
/* */
|
|
/* <Input> */
|
|
/* miny :: The minimum vertical grid coordinate. */
|
|
/* maxy :: The maximum vertical grid coordinate. */
|
|
/* */
|
|
/* <Return> */
|
|
/* SUCCESS or FAILURE. */
|
|
/* */
|
|
static
|
|
TResult Bezier_Down( RAS_ARG_ int degree,
|
|
TSplitter splitter,
|
|
TPos miny,
|
|
TPos maxy )
|
|
{
|
|
TPoint* arc = ras.arc;
|
|
TResult result, fresh;
|
|
|
|
arc[0].y = -arc[0].y;
|
|
arc[1].y = -arc[1].y;
|
|
arc[2].y = -arc[2].y;
|
|
if (degree > 2)
|
|
arc[3].y = -arc[3].y;
|
|
|
|
fresh = ras.fresh;
|
|
|
|
result = Bezier_Up( RAS_VAR_ degree, splitter, -maxy, -miny );
|
|
|
|
if ( fresh && !ras.fresh )
|
|
ras.cur_prof->start = -ras.cur_prof->start;
|
|
|
|
arc[0].y = -arc[0].y;
|
|
return result;
|
|
}
|
|
|
|
|
|
#ifdef FT_RASTER_CONIC_BEZIERS
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Split_Conic */
|
|
/* */
|
|
/* <Description> */
|
|
/* Subdivides one second-order Bezier arc into two joint sub-arcs in */
|
|
/* the Bezier stack. */
|
|
/* */
|
|
/* <Note> */
|
|
/* This routine is the `beef' of the component. It is one of _the_ */
|
|
/* inner loops that should be optimized like hell to get the best */
|
|
/* performance. */
|
|
/* */
|
|
static
|
|
void Split_Conic( TPoint* base )
|
|
{
|
|
TPos a, b;
|
|
|
|
|
|
base[4].x = base[2].x;
|
|
b = base[1].x;
|
|
a = base[3].x = ( base[2].x + b + 1 ) >> 1;
|
|
b = base[1].x = ( base[0].x + b + 1 ) >> 1;
|
|
base[2].x = ( a + b + 1 ) >> 1;
|
|
|
|
base[4].y = base[2].y;
|
|
b = base[1].y;
|
|
a = base[3].y = ( base[2].y + b + 1 ) >> 1;
|
|
b = base[1].y = ( base[0].y + b + 1 ) >> 1;
|
|
base[2].y = ( a + b ) / 2;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Push_Conic */
|
|
/* */
|
|
/* <Description> */
|
|
/* Clears the Bezier stack and pushes a new arc on top of it. */
|
|
/* */
|
|
/* <Input> */
|
|
/* p2 :: A pointer to the second (control) point. */
|
|
/* p3 :: A pointer to the third (end) point. */
|
|
/* */
|
|
/* <Note> */
|
|
/* The first point is taken as `raster->last', so it doesn't appear */
|
|
/* in the signature. */
|
|
/* */
|
|
static
|
|
void Push_Conic( RAS_ARG_ FT_Vector* p2,
|
|
FT_Vector* p3 )
|
|
{
|
|
#undef STORE
|
|
#define STORE( _arc, point ) \
|
|
{ \
|
|
TPos x = SCALED( point->x ); \
|
|
TPos y = SCALED( point->y ); \
|
|
\
|
|
\
|
|
if ( ras.flipped ) \
|
|
{ \
|
|
_arc.x = y; \
|
|
_arc.y = x; \
|
|
} \
|
|
else \
|
|
{ \
|
|
_arc.x = x; \
|
|
_arc.y = y; \
|
|
} \
|
|
}
|
|
|
|
TPoint* arc;
|
|
|
|
|
|
ras.arc = arc = ras.arcs;
|
|
|
|
arc[2] = ras.last;
|
|
STORE( arc[1], p2 );
|
|
STORE( arc[0], p3 );
|
|
#undef STORE
|
|
}
|
|
|
|
#endif /* FT_RASTER_CONIC_BEZIERS */
|
|
|
|
|
|
|
|
#ifdef FT_RASTER_CUBIC_BEZIERS
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> Split_Cubic */
|
|
/* */
|
|
/* <Description> */
|
|
/* Subdivides a third-order Bezier arc into two joint sub-arcs in */
|
|
/* the Bezier stack. */
|
|
/* */
|
|
/* <Note> */
|
|
/* This routine is the `beef' of the component. It is one of _the_ */
|
|
/* inner loops that should be optimized like hell to get the best */
|
|
/* performance. */
|
|
/* */
|
|
static
|
|
void Split_Cubic( TPoint* base )
|
|
{
|
|
TPos a, b, c, d;
|
|
|
|
|
|
base[6].x = base[3].x;
|
|
c = base[1].x;
|
|
d = base[2].x;
|
|
base[1].x = a = ( base[0].x + c + 1 ) >> 1;
|
|
base[5].x = b = ( base[3].x + d + 1 ) >> 1;
|
|
c = ( c + d + 1 ) >> 1;
|
|
base[2].x = a = ( a + c + 1 ) >> 1;
|
|
base[4].x = b = ( b + c + 1 ) >> 1;
|
|
base[3].x = ( a + b + 1 ) >> 1;
|
|
|
|
base[6].y = base[3].y;
|
|
c = base[1].y;
|
|
d = base[2].y;
|
|
base[1].y = a = ( base[0].y + c + 1 ) >> 1;
|
|
base[5].y = b = ( base[3].y + d + 1 ) >> 1;
|
|
c = ( c + d + 1 ) >> 1;
|
|
base[2].y = a = ( a + c + 1 ) >> 1;
|
|
base[4].y = b = ( b + c + 1 ) >> 1;
|
|
base[3].y = ( a + b + 1 ) >> 1;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Push_Cubic */
|
|
/* */
|
|
/* <Description> */
|
|
/* Clears the Bezier stack and pushes a new third-order Bezier arc on */
|
|
/* top of it. */
|
|
/* */
|
|
/* <Input> */
|
|
/* p2 :: A pointer to the second (control) point. */
|
|
/* p3 :: A pointer to the third (control) point. */
|
|
/* p4 :: A pointer to the fourth (end) point. */
|
|
/* */
|
|
/* <Note> */
|
|
/* The first point is taken as `raster->last', so it doesn't appear */
|
|
/* in the signature. */
|
|
/* */
|
|
/* This is the same as Push_Conic(), except that it deals with */
|
|
/* third-order Beziers. */
|
|
/* */
|
|
static
|
|
void Push_Cubic( RAS_ARG_ FT_Vector* p2,
|
|
FT_Vector* p3,
|
|
FT_Vector* p4 )
|
|
{
|
|
#undef STORE
|
|
#define STORE( _arc, point ) \
|
|
{ \
|
|
TPos x = SCALED( point->x ); \
|
|
TPos y = SCALED( point->y ); \
|
|
\
|
|
if ( ras.flipped ) \
|
|
{ \
|
|
_arc.x = y; \
|
|
_arc.y = x; \
|
|
} \
|
|
else \
|
|
{ \
|
|
_arc.x = x; \
|
|
_arc.y = y; \
|
|
} \
|
|
}
|
|
|
|
TPoint* arc;
|
|
ras.arc = arc = ras.arcs;
|
|
|
|
arc[3] = ras.last;
|
|
STORE( arc[2], p2 );
|
|
STORE( arc[1], p3 );
|
|
STORE( arc[0], p4 );
|
|
|
|
#undef STORE
|
|
}
|
|
|
|
#endif /* FT_RASTER_CUBIC_BEZIERS */
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Check_Contour */
|
|
/* */
|
|
/* <Description> */
|
|
/* Performs some checks at contour closure. */
|
|
/* */
|
|
/* <Return> */
|
|
/* SUCCESS or FAILURE. */
|
|
/* */
|
|
static
|
|
TResult Check_Contour( RAS_ARG )
|
|
{
|
|
PProfile lastProfile;
|
|
|
|
/* Sometimes, the first and last profile in a contour join on */
|
|
/* an integer scan-line; we must then remove the last intersection */
|
|
/* from the last profile to get rid of doublets */
|
|
if ( ( FRAC( ras.last.y ) == 0 &&
|
|
ras.last.y >= ras.minY &&
|
|
ras.last.y <= ras.maxY ) )
|
|
{
|
|
if ( ras.first_prof && ras.first_prof->flow == ras.cur_prof->flow )
|
|
ras.cursor--;
|
|
}
|
|
|
|
lastProfile = ras.cur_prof;
|
|
if ( End_Profile( RAS_VAR ) )
|
|
return FAILURE;
|
|
|
|
/* close the `next profile in contour' linked list */
|
|
lastProfile->next = ras.first_prof;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Move_To */
|
|
/* */
|
|
/* <Description> */
|
|
/* This function injects a new contour in the render pool. */
|
|
/* */
|
|
/* <Input> */
|
|
/* to :: A pointer to the contour's first point. */
|
|
/* raster :: A pointer to the current raster object. */
|
|
/* */
|
|
/* <Return> */
|
|
/* Error code. 0 means success. */
|
|
/* */
|
|
/* <Note> */
|
|
/* This function is used as a `FTRasterMoveTo_Func' by the outline */
|
|
/* decomposer. */
|
|
/* */
|
|
static
|
|
int Move_To( FT_Vector* to,
|
|
FT_Raster raster )
|
|
{
|
|
TPos scaled_x, scaled_y;
|
|
|
|
|
|
/* if there was already a contour being built, perform some checks */
|
|
if ( ras.start_prof )
|
|
if ( Check_Contour( RAS_VAR ) )
|
|
return FAILURE;
|
|
|
|
/* set the `current last point' */
|
|
scaled_x = SCALED( to->x );
|
|
scaled_y = SCALED( to->y );
|
|
|
|
if ( ras.flipped )
|
|
{
|
|
ras.last.x = scaled_y;
|
|
ras.last.y = scaled_x;
|
|
}
|
|
else
|
|
{
|
|
ras.last.x = scaled_x;
|
|
ras.last.y = scaled_y;
|
|
}
|
|
|
|
ras.state = Unknown;
|
|
ras.first_prof = NULL;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Line_To */
|
|
/* */
|
|
/* <Description> */
|
|
/* This function injects a new line segment in the render pool and */
|
|
/* adjusts the profiles list accordingly. */
|
|
/* */
|
|
/* <Input> */
|
|
/* to :: A pointer to the target position. */
|
|
/* raster :: A pointer to the current raster object. */
|
|
/* */
|
|
/* <Return> */
|
|
/* Error code. 0 means success. */
|
|
/* */
|
|
/* <Note> */
|
|
/* This function is used as a `FTRasterLineTo_Func' by the outline */
|
|
/* decomposer. */
|
|
/* */
|
|
static
|
|
int Line_To( FT_Vector* to,
|
|
FT_Raster raster )
|
|
{
|
|
TPos x, scaled_x;
|
|
TPos y, scaled_y;
|
|
|
|
|
|
scaled_x = SCALED( to->x );
|
|
scaled_y = SCALED( to->y );
|
|
|
|
if ( ras.flipped )
|
|
{
|
|
x = scaled_y;
|
|
y = scaled_x;
|
|
}
|
|
else
|
|
{
|
|
x = scaled_x;
|
|
y = scaled_y;
|
|
}
|
|
|
|
/* First, detect a change of direction */
|
|
if ( y != ras.last.y )
|
|
{
|
|
TDirection new_state = ( (y > ras.last.y) ? Ascending : Descending );
|
|
|
|
|
|
if ( ras.state != new_state )
|
|
{
|
|
if ( ras.state != Unknown &&
|
|
End_Profile( RAS_VAR ) )
|
|
goto Fail;
|
|
|
|
if ( New_Profile( RAS_VAR_ new_state ) )
|
|
goto Fail;
|
|
}
|
|
}
|
|
|
|
/* Then compute the lines */
|
|
switch ( ras.state )
|
|
{
|
|
case Ascending:
|
|
if ( Line_Up ( RAS_VAR_ ras.last.x, ras.last.y,
|
|
x, y, ras.minY, ras.maxY ) )
|
|
goto Fail;
|
|
break;
|
|
|
|
case Descending:
|
|
if ( Line_Down( RAS_VAR_ ras.last.x, ras.last.y,
|
|
x, y, ras.minY, ras.maxY ) )
|
|
goto Fail;
|
|
break;
|
|
|
|
default:
|
|
;
|
|
}
|
|
|
|
ras.last.x = x;
|
|
ras.last.y = y;
|
|
|
|
return SUCCESS;
|
|
|
|
Fail:
|
|
return FAILURE;
|
|
}
|
|
|
|
|
|
#ifdef FT_RASTER_CONIC_BEZIERS
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Conic_To */
|
|
/* */
|
|
/* <Description> */
|
|
/* Injects a new conic Bezier arc and adjusts the profile list */
|
|
/* accordingly. */
|
|
/* */
|
|
/* <Input> */
|
|
/* control :: A pointer to an intermediate control point. */
|
|
/* to :: A pointer to the end point. */
|
|
/* raster :: A handle to the current raster object. */
|
|
/* */
|
|
/* <Return> */
|
|
/* Error code. 0 means success. */
|
|
/* */
|
|
/* <Note> */
|
|
/* This function is used as a `FTRasterConicTo_Func' by the outline */
|
|
/* decomposer. */
|
|
/* */
|
|
static
|
|
int Conic_To( FT_Vector* control,
|
|
FT_Vector* to,
|
|
FT_Raster raster )
|
|
{
|
|
TPos y1, y2, y3, x3, ymin, ymax;
|
|
TDirection state_bez;
|
|
|
|
|
|
Push_Conic( RAS_VAR_ control, to );
|
|
|
|
do
|
|
{
|
|
y1 = ras.arc[2].y;
|
|
y2 = ras.arc[1].y;
|
|
y3 = ras.arc[0].y;
|
|
x3 = ras.arc[0].x;
|
|
|
|
/* first, categorize the Bezier arc */
|
|
|
|
if ( y1 <= y3 )
|
|
{
|
|
ymin = y1;
|
|
ymax = y3;
|
|
}
|
|
else
|
|
{
|
|
ymin = y3;
|
|
ymax = y1;
|
|
}
|
|
|
|
if ( y2 < ymin || y2 > ymax )
|
|
{
|
|
/* this arc has no given direction, split it !! */
|
|
Split_Conic( ras.arc );
|
|
ras.arc += 2;
|
|
}
|
|
else if ( y1 == y3 )
|
|
{
|
|
/* this arc is flat, ignore it and pop it from the bezier stack */
|
|
ras.arc -= 2;
|
|
}
|
|
else
|
|
{
|
|
/* the arc is y-monotonous, either ascending or descending */
|
|
/* detect a change of direction */
|
|
state_bez = y1 < y3 ? Ascending : Descending;
|
|
if ( ras.state != state_bez )
|
|
{
|
|
/* finalize current profile if any */
|
|
if ( ras.state != Unknown &&
|
|
End_Profile( RAS_VAR ) )
|
|
goto Fail;
|
|
|
|
/* create a new profile */
|
|
if ( New_Profile( RAS_VAR_ state_bez ) )
|
|
goto Fail;
|
|
}
|
|
|
|
/* now call the appropriate routine */
|
|
if ( state_bez == Ascending )
|
|
{
|
|
if ( Bezier_Up( RAS_VAR_ 2, Split_Conic, ras.minY, ras.maxY ) )
|
|
goto Fail;
|
|
}
|
|
else
|
|
if ( Bezier_Down( RAS_VAR_ 2, Split_Conic, ras.minY, ras.maxY ) )
|
|
goto Fail;
|
|
}
|
|
|
|
} while ( ras.arc >= ras.arcs );
|
|
|
|
ras.last.x = x3;
|
|
ras.last.y = y3;
|
|
|
|
return SUCCESS;
|
|
|
|
Fail:
|
|
return FAILURE;
|
|
}
|
|
|
|
#else /* FT_RASTER_CONIC_BEZIERS */
|
|
|
|
|
|
static
|
|
int Conic_To( FT_Vector* control,
|
|
FT_Vector* to,
|
|
FT_Raster raster )
|
|
{
|
|
UNUSED( control );
|
|
UNUSED( to );
|
|
UNUSED( raster );
|
|
|
|
return ErrRaster_Invalid_Outline;
|
|
}
|
|
|
|
|
|
#endif /* FT_RASTER_CONIC_BEZIERS */
|
|
|
|
|
|
#ifdef FT_RASTER_CUBIC_BEZIERS
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Cubic_To */
|
|
/* */
|
|
/* <Description> */
|
|
/* Injects a new cubic Bezier arc and adjusts the profile list */
|
|
/* accordingly. */
|
|
/* */
|
|
/* <Input> */
|
|
/* control1 :: A pointer to the first control point. */
|
|
/* control2 :: A pointer to the second control point. */
|
|
/* to :: A pointer to the end point. */
|
|
/* raster :: A handle to the current raster object. */
|
|
/* */
|
|
/* <Return> */
|
|
/* Error code. 0 means success. */
|
|
/* */
|
|
/* <Note> */
|
|
/* This function is used as a `FTRasterCubicTo_Func' by the outline */
|
|
/* decomposer. */
|
|
/* */
|
|
static
|
|
int Cubic_To( FT_Vector* control1,
|
|
FT_Vector* control2,
|
|
FT_Vector* to,
|
|
FT_Raster raster )
|
|
{
|
|
TPos y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
|
|
TDirection state_bez;
|
|
|
|
|
|
Push_Cubic( RAS_VAR_ control1, control2, to );
|
|
|
|
do
|
|
{
|
|
y1 = ras.arc[3].y;
|
|
y2 = ras.arc[2].y;
|
|
y3 = ras.arc[1].y;
|
|
y4 = ras.arc[0].y;
|
|
x4 = ras.arc[0].x;
|
|
|
|
/* first, categorize the Bezier arc */
|
|
|
|
if ( y1 <= y4 )
|
|
{
|
|
ymin1 = y1;
|
|
ymax1 = y4;
|
|
}
|
|
else
|
|
{
|
|
ymin1 = y4;
|
|
ymax1 = y1;
|
|
}
|
|
|
|
if ( y2 <= y3 )
|
|
{
|
|
ymin2 = y2;
|
|
ymax2 = y3;
|
|
}
|
|
else
|
|
{
|
|
ymin2 = y3;
|
|
ymax2 = y2;
|
|
}
|
|
|
|
if ( ymin2 < ymin1 || ymax2 > ymax1 )
|
|
{
|
|
/* this arc has no given direction, split it! */
|
|
Split_Cubic( ras.arc );
|
|
ras.arc += 3;
|
|
}
|
|
else if ( y1 == y4 )
|
|
{
|
|
/* this arc is flat, ignore it and pop it from the bezier stack */
|
|
ras.arc -= 3;
|
|
}
|
|
else
|
|
{
|
|
state_bez = ( y1 <= y4 ) ? Ascending : Descending;
|
|
|
|
/* detect a change of direction */
|
|
if ( ras.state != state_bez )
|
|
{
|
|
if ( ras.state != Unknown &&
|
|
End_Profile( RAS_VAR ) )
|
|
goto Fail;
|
|
|
|
if ( New_Profile( RAS_VAR_ state_bez ) )
|
|
goto Fail;
|
|
}
|
|
|
|
/* compute intersections */
|
|
if ( state_bez == Ascending )
|
|
{
|
|
if ( Bezier_Up ( RAS_VAR_ 3, Split_Cubic, ras.minY, ras.maxY ) )
|
|
goto Fail;
|
|
}
|
|
else
|
|
if ( Bezier_Down ( RAS_VAR_ 3, Split_Cubic, ras.minY, ras.maxY ) )
|
|
goto Fail;
|
|
}
|
|
|
|
} while ( ras.arc >= ras.arcs );
|
|
|
|
ras.last.x = x4;
|
|
ras.last.y = y4;
|
|
|
|
return SUCCESS;
|
|
|
|
Fail:
|
|
return FAILURE;
|
|
}
|
|
|
|
|
|
#else /* FT_RASTER_CUBIC_BEZIERS */
|
|
|
|
|
|
int Cubic_To( FT_Vector* control1,
|
|
FT_Vector* control2,
|
|
FT_Vector* to,
|
|
FT_Raster raster )
|
|
{
|
|
UNUSED( control1 );
|
|
UNUSED( control2 );
|
|
UNUSED( to );
|
|
UNUSED( raster );
|
|
|
|
return ErrRaster_Invalid_Outline;
|
|
}
|
|
|
|
|
|
#endif /* FT_RASTER_CUBIC_BEZIERS */
|
|
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* The following function is compiled in the raster only when it is */
|
|
/* compile as a stand-alone module.. */
|
|
|
|
/* It can, otherwise, be found in the FreeType base layer */
|
|
|
|
#ifdef _STANDALONE_
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* FT_Decompose_Outline */
|
|
/* */
|
|
/* <Description> */
|
|
/* 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. */
|
|
/* */
|
|
/* <Input> */
|
|
/* 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. */
|
|
/* */
|
|
/* <Return> */
|
|
/* Error code. 0 means sucess. */
|
|
/* */
|
|
static
|
|
int FT_Decompose_Outline( FT_Outline* outline,
|
|
FT_Outline_Funcs* interface,
|
|
void* user )
|
|
{
|
|
typedef enum _phases
|
|
{
|
|
phase_point,
|
|
phase_conic,
|
|
phase_cubic,
|
|
phase_cubic2
|
|
|
|
} TPhase;
|
|
|
|
FT_Vector v_first;
|
|
FT_Vector v_last;
|
|
FT_Vector v_control;
|
|
FT_Vector v_control2;
|
|
FT_Vector v_start;
|
|
|
|
FT_Vector* point;
|
|
char* flags;
|
|
|
|
int n; /* index of contour in outline */
|
|
int first; /* index of first point in contour */
|
|
int index; /* current point's index */
|
|
|
|
int error;
|
|
|
|
char tag; /* current point's state */
|
|
TPhase phase;
|
|
|
|
|
|
first = 0;
|
|
|
|
for ( n = 0; n < outline->n_contours; n++ )
|
|
{
|
|
int last; /* index of last point in contour */
|
|
|
|
|
|
last = outline->contours[n];
|
|
|
|
v_first = outline->points[first];
|
|
v_last = outline->points[last];
|
|
|
|
v_start = v_control = v_first;
|
|
|
|
tag = FT_CURVE_TAG( outline->flags[first] );
|
|
index = first;
|
|
|
|
/* A contour cannot start with a cubic control point! */
|
|
|
|
if ( tag == FT_Curve_Tag_Cubic )
|
|
return ErrRaster_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->flags[last] ) == FT_Curve_Tag_On )
|
|
{
|
|
/* start at last point if it is on the curve */
|
|
v_start = v_last;
|
|
}
|
|
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;
|
|
}
|
|
phase = phase_conic;
|
|
}
|
|
else
|
|
phase = phase_point;
|
|
|
|
|
|
/* Begin a new contour with MOVE_TO */
|
|
|
|
error = interface->move_to( &v_start, user );
|
|
if ( error )
|
|
return error;
|
|
|
|
point = outline->points + first;
|
|
flags = outline->flags + first;
|
|
|
|
/* now process each contour point individually */
|
|
|
|
while ( index < last )
|
|
{
|
|
index++;
|
|
point++;
|
|
flags++;
|
|
|
|
tag = FT_CURVE_TAG( flags[0] );
|
|
|
|
switch ( phase )
|
|
{
|
|
case phase_point: /* the previous point was on the curve */
|
|
|
|
switch ( tag )
|
|
{
|
|
/* two succesive on points -> emit segment */
|
|
case FT_Curve_Tag_On:
|
|
error = interface->line_to( point, user );
|
|
break;
|
|
|
|
/* on point + conic control -> remember control point */
|
|
case FT_Curve_Tag_Conic:
|
|
v_control = point[0];
|
|
phase = phase_conic;
|
|
break;
|
|
|
|
/* on point + cubic control -> remember first control */
|
|
default:
|
|
v_control = point[0];
|
|
phase = phase_cubic;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case phase_conic: /* the previous point was a conic control */
|
|
|
|
switch ( tag )
|
|
{
|
|
/* conic control + on point -> emit conic arc */
|
|
case FT_Curve_Tag_On:
|
|
error = interface->conic_to( &v_control, point, user );
|
|
phase = phase_point;
|
|
break;
|
|
|
|
/* two successive conics -> emit conic arc `in between' */
|
|
case FT_Curve_Tag_Conic:
|
|
{
|
|
FT_Vector v_middle;
|
|
|
|
|
|
v_middle.x = (v_control.x + point->x)/2;
|
|
v_middle.y = (v_control.y + point->y)/2;
|
|
|
|
error = interface->conic_to( &v_control,
|
|
&v_middle, user );
|
|
v_control = point[0];
|
|
}
|
|
break;
|
|
|
|
default:
|
|
error = ErrRaster_Invalid_Outline;
|
|
}
|
|
break;
|
|
|
|
case phase_cubic: /* the previous point was a cubic control */
|
|
|
|
/* this point _must_ be a cubic control too */
|
|
if ( tag != FT_Curve_Tag_Cubic )
|
|
return ErrRaster_Invalid_Outline;
|
|
|
|
v_control2 = point[0];
|
|
phase = phase_cubic2;
|
|
break;
|
|
|
|
|
|
case phase_cubic2: /* the two previous points were cubics */
|
|
|
|
/* this point _must_ be an on point */
|
|
if ( tag != FT_Curve_Tag_On )
|
|
error = ErrRaster_Invalid_Outline;
|
|
else
|
|
error = interface->cubic_to( &v_control, &v_control2,
|
|
point, user );
|
|
phase = phase_point;
|
|
break;
|
|
}
|
|
|
|
/* lazy error testing */
|
|
if ( error )
|
|
return error;
|
|
}
|
|
|
|
/* end of contour, close curve cleanly */
|
|
error = 0;
|
|
|
|
tag = FT_CURVE_TAG( outline->flags[first] );
|
|
|
|
switch ( phase )
|
|
{
|
|
case phase_point:
|
|
if ( tag == FT_Curve_Tag_On )
|
|
error = interface->line_to( &v_first, user );
|
|
break;
|
|
|
|
case phase_conic:
|
|
error = interface->conic_to( &v_control, &v_start, user );
|
|
break;
|
|
|
|
case phase_cubic2:
|
|
if ( tag == FT_Curve_Tag_On )
|
|
error = interface->cubic_to( &v_control, &v_control2,
|
|
&v_first, user );
|
|
else
|
|
error = ErrRaster_Invalid_Outline;
|
|
break;
|
|
|
|
default:
|
|
error = ErrRaster_Invalid_Outline;
|
|
break;
|
|
}
|
|
|
|
if ( error )
|
|
return error;
|
|
|
|
first = last + 1;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Convert_Glyph */
|
|
/* */
|
|
/* <Description> */
|
|
/* Converts a glyph into a series of segments and arcs and makes a */
|
|
/* profiles list with them. */
|
|
/* */
|
|
/* <InOut> */
|
|
/* outline :: The glyph outline. */
|
|
/* */
|
|
/* <Return> */
|
|
/* SUCCESS or FAILURE. */
|
|
/* */
|
|
static
|
|
TResult Convert_Glyph( RAS_ARG_ FT_Outline* outline )
|
|
{
|
|
static
|
|
FT_Outline_Funcs interface =
|
|
{
|
|
(FT_Outline_MoveTo_Func)Move_To,
|
|
(FT_Outline_LineTo_Func)Line_To,
|
|
(FT_Outline_ConicTo_Func)Conic_To,
|
|
(FT_Outline_CubicTo_Func)Cubic_To
|
|
};
|
|
|
|
/* Set up state in the raster object */
|
|
ras.start_prof = NULL;
|
|
ras.joint = FALSE;
|
|
ras.fresh = FALSE;
|
|
|
|
ras.pool_limit = ras.pool_size - AlignProfileSize;
|
|
|
|
ras.n_extrema = 0;
|
|
|
|
ras.cur_prof = (PProfile)ras.cursor;
|
|
ras.cur_prof->offset = ras.cursor;
|
|
ras.num_profs = 0;
|
|
|
|
/* Now decompose curve */
|
|
if ( FT_Decompose_Outline( outline, &interface, &ras ) )
|
|
return FAILURE;
|
|
/* XXX: the error condition is in ras.error */
|
|
|
|
/* Check the last contour if needed */
|
|
if ( Check_Contour( RAS_VAR ) )
|
|
return FAILURE;
|
|
|
|
/* Finalize profiles list */
|
|
return Finalize_Profile_Table( RAS_VAR );
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Init_Linked */
|
|
/* */
|
|
/* Inits an empty linked list. */
|
|
/* */
|
|
static
|
|
void Init_Linked( TProfileList* l )
|
|
{
|
|
*l = NULL;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* InsNew */
|
|
/* */
|
|
/* Inserts a new Profile in a linked list. */
|
|
/* */
|
|
static
|
|
void InsNew( PProfileList list,
|
|
PProfile profile )
|
|
{
|
|
PProfile *old, current;
|
|
TPos x;
|
|
|
|
|
|
old = list;
|
|
current = *old;
|
|
x = profile->X;
|
|
|
|
while ( current )
|
|
{
|
|
if ( x < current->X )
|
|
break;
|
|
old = ¤t->link;
|
|
current = *old;
|
|
}
|
|
|
|
profile->link = current;
|
|
*old = profile;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* DelOld */
|
|
/* */
|
|
/* Removes an old Profile from a linked list. */
|
|
/* */
|
|
static
|
|
void DelOld( PProfileList list,
|
|
PProfile profile )
|
|
{
|
|
PProfile *old, current;
|
|
|
|
|
|
old = list;
|
|
current = *old;
|
|
|
|
while ( current )
|
|
{
|
|
if ( current == profile )
|
|
{
|
|
*old = current->link;
|
|
return;
|
|
}
|
|
|
|
old = ¤t->link;
|
|
current = *old;
|
|
}
|
|
|
|
/* We should never reach this place, unless the Profile was not */
|
|
/* part of the list. */
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Update */
|
|
/* */
|
|
/* Updates all X offsets of a drawing list. */
|
|
/* */
|
|
static
|
|
void Update( PProfile first )
|
|
{
|
|
PProfile current = first;
|
|
|
|
|
|
while ( current )
|
|
{
|
|
current->X = *current->offset;
|
|
current->offset += current->flow;
|
|
current->height--;
|
|
current = current->link;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Sort */
|
|
/* */
|
|
/* Sorts a trace list. In 95%, the list is already sorted. We need */
|
|
/* an algorithm which is fast in this case. Bubble sort is enough */
|
|
/* and simple to implement. */
|
|
/* */
|
|
static
|
|
void Sort( PProfileList list )
|
|
{
|
|
PProfile *old, current, next;
|
|
|
|
|
|
/* First, set the new X coordinate of each profile */
|
|
Update( *list );
|
|
|
|
/* Then sort them */
|
|
old = list;
|
|
current = *old;
|
|
|
|
if ( !current )
|
|
return;
|
|
|
|
next = current->link;
|
|
|
|
while ( next )
|
|
{
|
|
if ( current->X <= next->X )
|
|
{
|
|
old = ¤t->link;
|
|
current = *old;
|
|
|
|
if ( !current )
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
*old = next;
|
|
current->link = next->link;
|
|
next->link = current;
|
|
|
|
old = list;
|
|
current = *old;
|
|
}
|
|
|
|
next = current->link;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/******** ********/
|
|
/******** Vertical Bitmap Sweep Routines ********/
|
|
/******** ********/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Vertical_Sweep_Init */
|
|
/* */
|
|
/* <Description> */
|
|
/* Initializes the vertical bitmap sweep. Called by the generic */
|
|
/* sweep/draw routine before its loop. */
|
|
/* */
|
|
/* <Input> */
|
|
/* min :: The address of the current minimum scanline. */
|
|
/* max :: The address of the current maximum scanline. */
|
|
/* */
|
|
static
|
|
void Vertical_Sweep_Init( RAS_ARG_ int* min, int* max )
|
|
{
|
|
long pitch;
|
|
|
|
UNUSED( max );
|
|
|
|
pitch = ras.target.pitch;
|
|
|
|
/* start from the bottom line, going up !! */
|
|
ras.trace_bit = - *min * pitch;
|
|
ras.trace_incr = -pitch;
|
|
|
|
if (pitch > 0)
|
|
ras.trace_bit += pitch*(ras.target.rows-1);
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Vertical_Sweep_Span */
|
|
/* */
|
|
/* <Description> */
|
|
/* Draws a single horizontal bitmap span during the vertical bitmap */
|
|
/* sweep. */
|
|
/* */
|
|
/* <Input> */
|
|
/* y :: The current scanline. */
|
|
/* x1 :: The left span edge. */
|
|
/* x2 :: The right span edge. */
|
|
/* */
|
|
static
|
|
void Vertical_Sweep_Span( RAS_ARG_ TScan y,
|
|
TPos x1,
|
|
TPos x2 )
|
|
{
|
|
TPos e1, e2;
|
|
int c1, c2;
|
|
Byte f1, f2;
|
|
PByte target;
|
|
|
|
UNUSED( y );
|
|
|
|
/* Drop-out control */
|
|
e1 = TRUNC( CEILING( x1 ) );
|
|
if ( x2 - x1 - PRECISION <= PRECISION_JITTER )
|
|
e2 = e1;
|
|
else
|
|
e2 = TRUNC( FLOOR( x2 ) );
|
|
|
|
if ( e1 <= e2 && e2 >= 0 && e1 < ras.bit_width )
|
|
{
|
|
if ( e1 < 0 ) e1 = 0;
|
|
if ( e2 >= ras.bit_width ) e2 = ras.bit_width - 1;
|
|
|
|
c1 = e1 >> 3;
|
|
c2 = e2 >> 3;
|
|
|
|
f1 = ((unsigned char)0xFF >> (e1 & 7));
|
|
f2 = ~((unsigned char)0x7F >> (e2 & 7));
|
|
|
|
target = ras.bit_buffer + ras.trace_bit + c1;
|
|
c2 -= c1;
|
|
|
|
if ( c2 > 0 )
|
|
{
|
|
target[0] |= f1;
|
|
|
|
/* memset() is slower than the following code on many platforms. */
|
|
/* This is due to the fact that, in the vast majority of cases, */
|
|
/* the span length in bytes is relatively small. */
|
|
c2--;
|
|
while ( c2 > 0 )
|
|
{
|
|
*(++target) = 0xFF;
|
|
c2--;
|
|
}
|
|
target[1] |= f2;
|
|
}
|
|
else
|
|
*target |= ( f1 & f2 );
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Vertical_Test_Pixel */
|
|
/* */
|
|
/* <Description> */
|
|
/* Tests a pixel `light' during the vertical bitmap sweep. Used */
|
|
/* during drop-out control only. */
|
|
/* */
|
|
/* <Input> */
|
|
/* y :: The current scanline. */
|
|
/* x :: The current x coordinate. */
|
|
/* */
|
|
static
|
|
int Vertical_Test_Pixel( RAS_ARG_ TScan y,
|
|
int x )
|
|
{
|
|
int c1 = x >> 3;
|
|
|
|
|
|
UNUSED( y );
|
|
|
|
return ( x >= 0 && x < ras.bit_width &&
|
|
ras.bit_buffer[ras.trace_bit + c1] & (0x80 >> (x & 7)) );
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Vertical_Set_Pixel */
|
|
/* */
|
|
/* <Description> */
|
|
/* Sets a single pixel in a bitmap during the vertical sweep. Used */
|
|
/* during drop-out control. */
|
|
/* */
|
|
/* <Input> */
|
|
/* y :: The current scanline. */
|
|
/* x :: The current x coordinate. */
|
|
/* color :: Ignored by this function. */
|
|
/* */
|
|
static
|
|
void Vertical_Set_Pixel( RAS_ARG_ int y,
|
|
int x,
|
|
int color )
|
|
{
|
|
UNUSED( color );
|
|
UNUSED( y );
|
|
|
|
if ( x >= 0 && x < ras.bit_width )
|
|
ras.bit_buffer[ras.trace_bit+(x >> 3)] |= (char)(0x80 >> (x & 7));
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Vertical_Sweep_Step */
|
|
/* */
|
|
/* <Description> */
|
|
/* Called whenever the sweep jumps to another scanline. Only updates */
|
|
/* the pointers in the vertical bitmap sweep. */
|
|
/* */
|
|
static
|
|
void Vertical_Sweep_Step( RAS_ARG )
|
|
{
|
|
ras.trace_bit += ras.trace_incr;
|
|
}
|
|
|
|
|
|
static
|
|
const Raster_Render vertical_render_mono =
|
|
{
|
|
&Vertical_Sweep_Init,
|
|
&Vertical_Sweep_Span,
|
|
&Vertical_Sweep_Step,
|
|
&Vertical_Test_Pixel,
|
|
&Vertical_Set_Pixel
|
|
};
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/******** ********/
|
|
/******** Horizontal Bitmap Sweep Routines ********/
|
|
/******** ********/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Horizontal_Sweep_Init */
|
|
/* */
|
|
/* <Description> */
|
|
/* Initializes the horizontal bitmap sweep. Called by the generic */
|
|
/* sweep/draw routine before its loop. */
|
|
/* */
|
|
/* <Input> */
|
|
/* min :: The address of the current minimum pixel column. */
|
|
/* max :: The address of the current maximum pixel column. */
|
|
/* */
|
|
static
|
|
void Horizontal_Sweep_Init( RAS_ARG_ int* min,
|
|
int* max )
|
|
{
|
|
UNUSED( ras );
|
|
UNUSED( min );
|
|
UNUSED( max );
|
|
|
|
/* nothing, really */
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Horizontal_Sweep_Span */
|
|
/* */
|
|
/* <Description> */
|
|
/* Draws a single vertical bitmap span during the horizontal bitmap */
|
|
/* sweep. Actually, this function is only used to check for weird */
|
|
/* drop-out cases. */
|
|
/* */
|
|
/* <Input> */
|
|
/* y :: The current pixel column. */
|
|
/* x1 :: The top span edge. */
|
|
/* x2 :: The bottom span edge. */
|
|
/* */
|
|
static
|
|
void Horizontal_Sweep_Span( RAS_ARG_ TScan y,
|
|
TPos x1,
|
|
TPos x2 )
|
|
{
|
|
TPos e1, e2;
|
|
PByte bits;
|
|
Byte f1;
|
|
|
|
UNUSED( y );
|
|
|
|
/* During the horizontal sweep, we only take care of drop-outs */
|
|
if ( x2 - x1 < PRECISION )
|
|
{
|
|
e1 = CEILING( x1 );
|
|
e2 = FLOOR( x2 );
|
|
|
|
if ( e1 == e2 )
|
|
{
|
|
bits = ras.bit_buffer + (y >> 3);
|
|
f1 = (Byte)(0x80 >> (y & 7));
|
|
|
|
e1 = TRUNC( e1 );
|
|
|
|
if ( e1 >= 0 && e1 < ras.target.rows )
|
|
{
|
|
long pitch = ras.target.pitch;
|
|
long offset = - pitch * e1;
|
|
|
|
if (pitch > 0)
|
|
offset += (ras.target.rows-1)*pitch;
|
|
|
|
bits[offset] |= f1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Horizontal_Test_Pixel */
|
|
/* */
|
|
/* <Description> */
|
|
/* Tests a pixel `light' during the horizontal bitmap sweep. Used */
|
|
/* during drop-out control only. */
|
|
/* */
|
|
/* <Input> */
|
|
/* y :: The current pixel column. */
|
|
/* x :: The current row/scanline. */
|
|
/* */
|
|
static
|
|
int Horizontal_Test_Pixel( RAS_ARG_ int y,
|
|
int x )
|
|
{
|
|
char* bits = (char*)ras.bit_buffer + (y >> 3);
|
|
int f1 = (Byte)(0x80 >> (y & 7));
|
|
long pitch = ras.target.pitch;
|
|
long offset = - pitch * x;
|
|
|
|
if (pitch > 0)
|
|
offset += (ras.target.rows-1)*pitch;
|
|
|
|
return ( x >= 0 && x < ras.target.rows && (bits[0] & f1) );
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Horizontal_Set_Pixel */
|
|
/* */
|
|
/* <Description> */
|
|
/* Sets a single pixel in a bitmap during the horizontal sweep. Used */
|
|
/* during drop-out control. */
|
|
/* */
|
|
/* <Input> */
|
|
/* y :: The current pixel column. */
|
|
/* x :: The current row/scanline. */
|
|
/* color :: Ignored by this function. */
|
|
/* */
|
|
static
|
|
void Horizontal_Set_Pixel( RAS_ARG_ int y,
|
|
int x,
|
|
int color )
|
|
{
|
|
char* bits = (char*)ras.bit_buffer + (y >> 3);
|
|
int f1 = (Byte)(0x80 >> (y & 7));
|
|
|
|
|
|
UNUSED( color );
|
|
|
|
if ( x >= 0 && x < ras.target.rows )
|
|
{
|
|
long pitch = ras.target.pitch;
|
|
long offset = - x*pitch;
|
|
|
|
if (pitch > 0)
|
|
offset += (ras.target.rows-1)*pitch;
|
|
|
|
bits[offset] |= f1;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Horizontal_Sweep_Step */
|
|
/* */
|
|
/* <Description> */
|
|
/* Called whenever the sweep jumps to another pixel column. */
|
|
/* */
|
|
static
|
|
void Horizontal_Sweep_Step( RAS_ARG )
|
|
{
|
|
UNUSED( ras.target );
|
|
|
|
/* Nothing, really */
|
|
}
|
|
|
|
|
|
static
|
|
const Raster_Render horizontal_render_mono =
|
|
{
|
|
&Horizontal_Sweep_Init,
|
|
&Horizontal_Sweep_Span,
|
|
&Horizontal_Sweep_Step,
|
|
&Horizontal_Test_Pixel,
|
|
&Horizontal_Set_Pixel
|
|
};
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/******** ********/
|
|
/******** Anti-Aliased Vertical Bitmap Sweep Routines ********/
|
|
/******** ********/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
#ifdef FT_RASTER_OPTION_ANTI_ALIAS
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Vertical_Gray_Sweep_Init */
|
|
/* */
|
|
/* <Description> */
|
|
/* Initializes the vertical bitmap sweep. Called by the generic */
|
|
/* sweep/draw routine before its loop. */
|
|
/* */
|
|
/* <Input> */
|
|
/* min :: The address of the current minimum scanline. */
|
|
/* max :: The address of the current maximum scanline. */
|
|
/* */
|
|
static
|
|
void Vertical_Gray_Sweep_Init( RAS_ARG_ int* min, int* max )
|
|
{
|
|
long pitch;
|
|
|
|
UNUSED( max );
|
|
|
|
pitch = ras.target.pitch;
|
|
|
|
/* start from the bottom line, going up */
|
|
ras.trace_incr = -pitch;
|
|
ras.trace_bit = - *min * pitch;
|
|
|
|
if (pitch > 0)
|
|
ras.trace_bit += (ras.target.rows-1)*pitch;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Vertical_Gray_Sweep_Span */
|
|
/* */
|
|
/* <Description> */
|
|
/* Draws a single horizontal bitmap span during the vertical bitmap */
|
|
/* sweep. */
|
|
/* */
|
|
/* <Input> */
|
|
/* y :: The current scanline. */
|
|
/* x1 :: The left span edge. */
|
|
/* x2 :: The right span edge. */
|
|
/* */
|
|
static
|
|
void Vertical_Gray_Sweep_Span( RAS_ARG_ TScan y,
|
|
TPos x1,
|
|
TPos x2 )
|
|
{
|
|
TPos e1, e2;
|
|
int shift = PRECISION_BITS - 6;
|
|
PByte target;
|
|
|
|
UNUSED( y );
|
|
|
|
x1 += PRECISION_HALF;
|
|
x2 += PRECISION_HALF;
|
|
|
|
#ifdef FT_RASTER_OPTION_CONTRAST
|
|
if ( x2-x1 < PRECISION )
|
|
{
|
|
x1 = ((x1+x2) >> 1) - PRECISION_HALF;
|
|
x2 = x1 + PRECISION;
|
|
}
|
|
#endif
|
|
|
|
e1 = TRUNC( x1 );
|
|
e2 = TRUNC( x2 );
|
|
|
|
if ( e1 <= e2 && e2 >= 0 && e1 < ras.bit_width )
|
|
{
|
|
x1 = FRAC(x1) >> shift;
|
|
x2 = FRAC(x2) >> shift;
|
|
|
|
if ( e1 < 0 )
|
|
{
|
|
e1 = 0;
|
|
x1 = 0;
|
|
}
|
|
|
|
if ( e2 > ras.bit_width )
|
|
{
|
|
e2 = ras.bit_width-1;
|
|
x2 = 0;
|
|
}
|
|
|
|
target = ras.bit_buffer + ras.trace_bit + e1;
|
|
e2 -= e1;
|
|
|
|
if ( e2 > 0 )
|
|
{
|
|
if (x1 > 0) target[0] += (Byte)(64-x1) << 1;
|
|
else target[0] = 127;
|
|
e2--;
|
|
while (e2 > 0)
|
|
{
|
|
*(++target) = 127;
|
|
e2--;
|
|
}
|
|
if (x2)
|
|
target[1] += (Byte)x2 << 1;
|
|
}
|
|
else
|
|
{
|
|
target[0] += (Byte)(x2-x1) << 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Vertical_Gray_Test_Pixel */
|
|
/* */
|
|
/* <Description> */
|
|
/* Tests a pixel `light' during the vertical bitmap sweep. Used */
|
|
/* during drop-out control only. */
|
|
/* */
|
|
/* <Input> */
|
|
/* y :: The current scanline. */
|
|
/* x :: The current x coordinate. */
|
|
/* */
|
|
static
|
|
int Vertical_Gray_Test_Pixel( RAS_ARG_ TScan y,
|
|
int x )
|
|
{
|
|
UNUSED( y );
|
|
|
|
#if 0
|
|
/* as a rule of thumb, do not add a drop-out if the current */
|
|
/* gray level is over 0.5 */
|
|
|
|
return ( x >= 0 && x < ras.bit_width &&
|
|
ras.bit_buffer[ras.trace_bit + x] >= 64 );
|
|
#else
|
|
UNUSED(x);
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Vertical_Gray_Set_Pixel */
|
|
/* */
|
|
/* <Description> */
|
|
/* Sets a single pixel in a bitmap during the vertical sweep. Used */
|
|
/* during drop-out control. */
|
|
/* */
|
|
/* <Input> */
|
|
/* y :: The current scanline. */
|
|
/* x :: The current x coordinate. */
|
|
/* color :: Ignored by this function. */
|
|
/* */
|
|
static
|
|
void Vertical_Gray_Set_Pixel( RAS_ARG_ int y,
|
|
int x,
|
|
int color )
|
|
{
|
|
UNUSED( y );
|
|
|
|
if ( x >= 0 && x < ras.bit_width )
|
|
{
|
|
unsigned char* pixel;
|
|
|
|
pixel = ras.bit_buffer + ras.trace_bit + x;
|
|
|
|
/* do not add too much to the pixel gray level */
|
|
color += *pixel;
|
|
if (color < 64)
|
|
color = 64;
|
|
|
|
*pixel = ( color >= 127 ? 127 : (unsigned char)color );
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Vertical_Sweep_Step */
|
|
/* */
|
|
/* <Description> */
|
|
/* Called whenever the sweep jumps to another scanline. Only updates */
|
|
/* the pointers in the vertical bitmap sweep. */
|
|
/* */
|
|
static
|
|
void Vertical_Gray_Sweep_Step( RAS_ARG )
|
|
{
|
|
ras.trace_bit += ras.trace_incr;
|
|
}
|
|
|
|
|
|
|
|
static
|
|
const Raster_Render vertical_render_gray =
|
|
{
|
|
&Vertical_Gray_Sweep_Init,
|
|
&Vertical_Gray_Sweep_Span,
|
|
&Vertical_Gray_Sweep_Step,
|
|
&Vertical_Gray_Test_Pixel,
|
|
&Vertical_Gray_Set_Pixel
|
|
};
|
|
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/******** ********/
|
|
/******** Horizontal Bitmap Sweep Routines ********/
|
|
/******** ********/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Horizontal_Sweep_Init */
|
|
/* */
|
|
/* <Description> */
|
|
/* Initializes the horizontal bitmap sweep. Called by the generic */
|
|
/* sweep/draw routine before its loop. */
|
|
/* */
|
|
/* <Input> */
|
|
/* min :: The address of the current minimum pixel column. */
|
|
/* max :: The address of the current maximum pixel column. */
|
|
/* */
|
|
static
|
|
void Horizontal_Gray_Sweep_Init( RAS_ARG_ int* min,
|
|
int* max )
|
|
{
|
|
UNUSED( ras );
|
|
UNUSED( min );
|
|
UNUSED( max );
|
|
|
|
/* nothing, really */
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Horizontal_Gray_Sweep_Span */
|
|
/* */
|
|
/* <Description> */
|
|
/* Draws a single vertical bitmap span during the horizontal bitmap */
|
|
/* sweep. */
|
|
/* */
|
|
/* <Input> */
|
|
/* y :: The current scanline. */
|
|
/* x1 :: The left span edge. */
|
|
/* x2 :: The right span edge. */
|
|
/* */
|
|
static
|
|
void Horizontal_Gray_Sweep_Span( RAS_ARG_ TScan y,
|
|
TPos x1,
|
|
TPos x2 )
|
|
{
|
|
TPos e1, e2;
|
|
int shift = PRECISION_BITS - 6;
|
|
int incr;
|
|
PByte bits;
|
|
Byte b;
|
|
|
|
UNUSED( y );
|
|
|
|
x1 += PRECISION_HALF;
|
|
x2 += PRECISION_HALF;
|
|
|
|
#ifdef FT_RASTER_OPTION_CONTRAST
|
|
if (x2-x1 < PRECISION)
|
|
{
|
|
x1 = ((x1+x2) >> 1) - PRECISION_HALF;
|
|
x2 = x1 + PRECISION;
|
|
}
|
|
#endif
|
|
|
|
e1 = TRUNC( x1 );
|
|
e2 = TRUNC( x2 );
|
|
|
|
if ( e1 <= e2 && e2 >= 0 && e1 < ras.bit_width )
|
|
{
|
|
x1 = FRAC(x1) >> shift;
|
|
x2 = FRAC(x2) >> shift;
|
|
|
|
if ( e1 < 0 )
|
|
{
|
|
e1 = 0;
|
|
x1 = 0;
|
|
}
|
|
|
|
if ( e2 >= ras.bit_width )
|
|
{
|
|
e2 = ras.bit_width;
|
|
x2 = 0;
|
|
}
|
|
|
|
incr = -ras.target.pitch;
|
|
bits = ras.bit_buffer + y;
|
|
bits += incr * e1;
|
|
if (incr < 0)
|
|
bits -= incr*(ras.target.rows-1);
|
|
|
|
e2 -= e1;
|
|
|
|
if ( e2 > 0 )
|
|
{
|
|
b = bits[0];
|
|
if (b < 127) b++;
|
|
b = (Byte)((64-x1) + (b >> 1));
|
|
bits[0] = b;
|
|
|
|
if ( e2 < 24 )
|
|
{
|
|
e2--;
|
|
while (e2 > 0)
|
|
{
|
|
bits += incr;
|
|
b = bits[0];
|
|
|
|
if (b < 127)
|
|
bits[0] = (Byte)(63+((b+1) >> 1));
|
|
|
|
e2--;
|
|
}
|
|
}
|
|
else
|
|
bits += incr*(e2-1);
|
|
|
|
if (x2)
|
|
{
|
|
bits += incr;
|
|
b = bits[0];
|
|
if (b < 127) b++;
|
|
b = (Byte)(x2 + (b >> 1));
|
|
bits[0] = b;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
b = bits[0];
|
|
if (b < 127) b++;
|
|
b = (Byte)((b >> 1)+(x2-x1));
|
|
bits[0] = b;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Horizontal_Gray_Test_Pixel */
|
|
/* */
|
|
/* <Description> */
|
|
/* Tests a pixel `light' during the horizontal bitmap sweep. Used */
|
|
/* during drop-out control only. */
|
|
/* */
|
|
/* <Input> */
|
|
/* y :: The current pixel column. */
|
|
/* x :: The current row/scanline. */
|
|
/* */
|
|
static
|
|
int Horizontal_Gray_Test_Pixel( RAS_ARG_ int y,
|
|
int x )
|
|
{
|
|
#if 0
|
|
unsigned char* pixel = (unsigned char*)ras.bit_buffer + y;
|
|
|
|
if ( ras.target.flow == Flow_Down )
|
|
pixel += (ras.target.rows-1 - x) * ras.target.cols;
|
|
else
|
|
pixel += x * ras.target.cols;
|
|
|
|
return ( x >= 0 && x < ras.target.rows &&
|
|
*pixel >= 64 );
|
|
#else
|
|
UNUSED(y);
|
|
UNUSED(x);
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Horizontal_Set_Pixel */
|
|
/* */
|
|
/* <Description> */
|
|
/* Sets a single pixel in a bitmap during the horizontal sweep. Used */
|
|
/* during drop-out control. */
|
|
/* */
|
|
/* <Input> */
|
|
/* y :: The current pixel column. */
|
|
/* x :: The current row/scanline. */
|
|
/* color :: Ignored by this function. */
|
|
/* */
|
|
static
|
|
void Horizontal_Gray_Set_Pixel( RAS_ARG_ int y,
|
|
int x,
|
|
int color )
|
|
{
|
|
unsigned char* pixel = (unsigned char*)ras.bit_buffer + y;
|
|
|
|
if ( x >= 0 && x < ras.target.rows )
|
|
{
|
|
long pitch = ras.target.pitch;
|
|
|
|
pixel -= pitch*x;
|
|
if (pitch > 0)
|
|
pixel += pitch*(ras.target.rows-1);
|
|
|
|
color += *pixel;
|
|
if (color < 64)
|
|
color = 64;
|
|
|
|
*pixel = (color >= 127 ? 127 : (unsigned char)color );
|
|
}
|
|
}
|
|
|
|
|
|
static
|
|
void Gray_Ignore( void )
|
|
{
|
|
;
|
|
}
|
|
|
|
|
|
static
|
|
const Raster_Render horizontal_render_gray =
|
|
{
|
|
&Horizontal_Gray_Sweep_Init,
|
|
&Horizontal_Gray_Sweep_Span,
|
|
|
|
(Function_Sweep_Step) &Gray_Ignore,
|
|
&Horizontal_Gray_Test_Pixel,
|
|
&Horizontal_Gray_Set_Pixel,
|
|
};
|
|
|
|
#endif /* FT_RASTER_OPTION_ANTI_ALIAS */
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* A technical note to explain how the scanline sweep is performed: */
|
|
/* */
|
|
/* The function Draw_Sweep() is used to sweep the scanlines of the */
|
|
/* target bitmap or pixmap. For each scanline, it must do the */
|
|
/* following: */
|
|
/* */
|
|
/* - Get the set of all outline intersections for the current */
|
|
/* scanline. */
|
|
/* */
|
|
/* - Sort these intersections (in increasing order). */
|
|
/* */
|
|
/* - Pair intersections to create spans (horizontal pixel segments) */
|
|
/* that are then `drawn' by calling a `sweep_span' function. */
|
|
/* */
|
|
/* - Check for dropouts: If a span is too small to be drawn, it must */
|
|
/* be re-adjusted in order to make it visible again. */
|
|
/* */
|
|
/* The sweep starts from the bottom of the outline (ymin) and goes */
|
|
/* upwards (to ymax). Thus, the function manages the following: */
|
|
/* */
|
|
/* - A linked list of the profiles which are above the current */
|
|
/* scanline. It is called the `wait' list as it contains all the */
|
|
/* profiles waiting to be `activated' during the sweep. It contains */
|
|
/* all profiles initially. */
|
|
/* */
|
|
/* - A linked list of the profiles covering the current scanline, */
|
|
/* i.e., all the profiles that contain an intersection for the */
|
|
/* current scanline. It is called the `draw' list. */
|
|
/* */
|
|
/* A profile travels from the wait list to the draw list if the */
|
|
/* current scanline reaches its bottom border (its ymin). It is also */
|
|
/* removed from the draw list (and becomes unlisted) when the current */
|
|
/* scanline reaches the scanline above its upper border (its ymax). */
|
|
/* */
|
|
/* These positions correspond to the `extrema' table built by */
|
|
/* Finalize_Profile_Table(). */
|
|
/* */
|
|
/* The draw list is always sorted in increasing order of the X */
|
|
/* coordinates. We use a bubble sort because it is easy to implement */
|
|
/* on a linked list, and because in 95% cases, the list is already */
|
|
/* correctly sorted when going from one scanline to the other. */
|
|
/* */
|
|
/* The extrema table gives the scanline coordinates at which at least */
|
|
/* one profile must be removed from the `draw' list, or another one */
|
|
/* must be moved from the `wait' to `draw' lists. */
|
|
/* */
|
|
/* Note that when a dropout is detected, the corresponding span is not */
|
|
/* drawn immediately but kept on a temporary list. All dropout spans */
|
|
/* are drawn after the regular spans on a given scanline. This is a */
|
|
/* requirement of the TrueType specification to properly implement */
|
|
/* some drop-out control modes -- yes, it's weird! */
|
|
/* */
|
|
/* Finally, the parser contains four function pointers that are called */
|
|
/* by Draw_Sweep(). Each rendering mode (monochrome, anti-aliased-5, */
|
|
/* and anti-aliased-17) provide its own set of such functions. These */
|
|
/* are: */
|
|
/* */
|
|
/* sweep_init: Called only when the sweep starts. Used to set */
|
|
/* up some variables. */
|
|
/* */
|
|
/* sweep_span: Used to draw a horizontal span on the current */
|
|
/* scanline. */
|
|
/* */
|
|
/* sweep_test_pixel: Used to test a pixel's intensity, as it is */
|
|
/* required for drop-out control. */
|
|
/* */
|
|
/* sweep_put_pixel: Used to write a single pixel when a drop-out */
|
|
/* needs to be lighted/drawn. */
|
|
/* */
|
|
/*************************************************************************/
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Generic Sweep Drawing routine */
|
|
/* */
|
|
static
|
|
TResult Draw_Sweep( RAS_ARG )
|
|
{
|
|
TScan y, y_change, y_height;
|
|
|
|
PProfile P, Q, P_Left, P_Right;
|
|
|
|
TScan min_Y, max_Y, top, bottom, dropouts;
|
|
|
|
TPos x1, x2, e1, e2;
|
|
|
|
TProfileList wait;
|
|
TProfileList draw;
|
|
|
|
|
|
/* Init empty linked lists */
|
|
Init_Linked( &wait );
|
|
Init_Linked( &draw );
|
|
|
|
/* first, compute min and max Y -- and add profiles to the wait list */
|
|
P = ras.start_prof;
|
|
max_Y = TRUNC( ras.minY );
|
|
min_Y = TRUNC( ras.maxY );
|
|
|
|
while ( P )
|
|
{
|
|
Q = P->link;
|
|
|
|
bottom = P->start;
|
|
top = P->start + P->height-1;
|
|
|
|
if ( min_Y > bottom ) min_Y = bottom;
|
|
if ( max_Y < top ) max_Y = top;
|
|
|
|
P->X = 0;
|
|
InsNew( &wait, P );
|
|
|
|
P = Q;
|
|
}
|
|
|
|
/* Check the extrema table */
|
|
if ( ras.n_extrema == 0 )
|
|
{
|
|
ras.error = ErrRaster_Invalid_Outline;
|
|
return FAILURE;
|
|
}
|
|
|
|
/* Now inits the sweep */
|
|
PTRACE2(( "draw_sweep: initialize sweep\n" ));
|
|
ras.render.init( RAS_VAR_ &min_Y, &max_Y );
|
|
PTRACE2(( " init min_y = %d, max_y = %d\n", min_Y, max_Y ));
|
|
|
|
/* Then compute the distance of each profile from min_Y */
|
|
P = wait;
|
|
while ( P )
|
|
{
|
|
P->countL = P->start - min_Y;
|
|
P = P->link;
|
|
}
|
|
|
|
/* Let's go */
|
|
y = min_Y;
|
|
y_height = 0;
|
|
|
|
if ( ras.n_extrema > 0 &&
|
|
ras.pool_size[-ras.n_extrema] == min_Y )
|
|
ras.n_extrema--;
|
|
|
|
PTRACE2(( "starting loop with n_extrema = %d", ras.n_extrema ));
|
|
while ( ras.n_extrema > 0 )
|
|
{
|
|
PProfile prof = wait;
|
|
|
|
|
|
/* look in the wait list for new activations */
|
|
while ( prof )
|
|
{
|
|
PProfile next = prof->link;
|
|
|
|
prof->countL -= y_height;
|
|
if ( prof->countL == 0 )
|
|
{
|
|
/* move the profile from the wait list to the draw list */
|
|
DelOld( &wait, prof );
|
|
InsNew( &draw, prof );
|
|
}
|
|
prof = next;
|
|
}
|
|
|
|
/* Sort the draw list */
|
|
Sort( &draw );
|
|
|
|
/* compute next y extremum scanline; we won't change the */
|
|
/* elements of the wait and draw lists until there */
|
|
y_change = ras.pool_size[-ras.n_extrema--];
|
|
y_height = y_change - y;
|
|
|
|
PTRACE2(( ">>> y = %d, y_change = %d, y_height = %d",
|
|
y, y_change, y_height ));
|
|
|
|
while ( y < y_change )
|
|
{
|
|
int window;
|
|
PProfile left;
|
|
|
|
|
|
/* Let's trace */
|
|
dropouts = 0;
|
|
|
|
/* skip to next line if there is no active profile there */
|
|
if ( !draw ) goto Next_Line;
|
|
|
|
left = draw;
|
|
window = left->flow;
|
|
prof = left->link;
|
|
|
|
PTRACE2(( ">>> line y = %d", y ));
|
|
|
|
while ( prof )
|
|
{
|
|
PProfile next = prof->link;
|
|
|
|
window += prof->flow;
|
|
|
|
if ( window == 0 )
|
|
{
|
|
x1 = left->X;
|
|
x2 = prof->X;
|
|
|
|
if ( x1 > x2 )
|
|
{
|
|
TPos xs = x1;
|
|
|
|
x1 = x2;
|
|
x2 = xs;
|
|
}
|
|
|
|
if ( x2 - x1 <= PRECISION && ras.dropout_mode )
|
|
{
|
|
e1 = CEILING( x1 );
|
|
e2 = FLOOR( x2 );
|
|
|
|
if ( e1 > e2 || e2 == e1 + PRECISION )
|
|
{
|
|
/* a drop out was detected */
|
|
left->X = x1;
|
|
prof->X = x2;
|
|
|
|
/* mark profiles for drop-out processing */
|
|
left->countL = 1;
|
|
prof->countL = 2;
|
|
dropouts++;
|
|
goto Skip_To_Next;
|
|
}
|
|
}
|
|
|
|
PTRACE2(( "drawing span ( y=%d, x1=%d, x2=%d )", y, x1, x2 ));
|
|
ras.render.span( RAS_VAR_ y, x1, x2 );
|
|
|
|
Skip_To_Next:
|
|
left = next;
|
|
}
|
|
prof = next;
|
|
}
|
|
|
|
/* now perform the dropouts _after_ the span drawing */
|
|
/* drop-outs processing has been moved out of the loop */
|
|
/* for performance tuning */
|
|
if ( dropouts > 0 )
|
|
goto Scan_DropOuts;
|
|
|
|
Next_Line:
|
|
ras.render.step( RAS_VAR );
|
|
|
|
y++;
|
|
|
|
if ( y < y_change )
|
|
Sort( &draw );
|
|
|
|
PTRACE2(( "line sorted for next operation" ));
|
|
}
|
|
|
|
/* Now finalize the profiles that needs it */
|
|
|
|
PTRACE2(( "finalizing profiles..." ));
|
|
{
|
|
PProfile prof, next;
|
|
|
|
prof = draw;
|
|
while ( prof )
|
|
{
|
|
next = prof->link;
|
|
if (prof->height == 0)
|
|
DelOld( &draw, prof );
|
|
prof = next;
|
|
}
|
|
}
|
|
|
|
PTRACE2(( "profiles finalized for this run" ));
|
|
}
|
|
|
|
/* for gray-scaling, flushes the bitmap scanline cache */
|
|
while ( y <= max_Y )
|
|
{
|
|
ras.render.step( RAS_VAR );
|
|
y++;
|
|
}
|
|
|
|
return SUCCESS;
|
|
|
|
|
|
Scan_DropOuts :
|
|
P_Left = draw;
|
|
|
|
|
|
while ( dropouts > 0 )
|
|
{
|
|
TPos e1, e2;
|
|
PProfile left, right;
|
|
|
|
while ( P_Left->countL != 1 )
|
|
P_Left = P_Left->link;
|
|
P_Right = P_Left->link;
|
|
while ( P_Right->countL != 2 )
|
|
P_Right = P_Right->link;
|
|
|
|
P_Left->countL = 0;
|
|
P_Right->countL = 0;
|
|
|
|
/* Now perform the dropout control */
|
|
x1 = P_Left->X;
|
|
x2 = P_Right->X;
|
|
|
|
left = ( ras.flipped ? P_Right : P_Left );
|
|
right = ( ras.flipped ? P_Left : P_Right );
|
|
|
|
PTRACE2(( "performing drop-out control ( x1= %d, x2 = %d )",
|
|
x1, x2 ));
|
|
|
|
e1 = CEILING( x1 );
|
|
e2 = FLOOR ( x2 );
|
|
|
|
if ( e1 > e2 )
|
|
{
|
|
if ( e1 == e2 + PRECISION )
|
|
{
|
|
switch ( ras.dropout_mode )
|
|
{
|
|
case 1:
|
|
e1 = e2;
|
|
break;
|
|
|
|
case 4:
|
|
e1 = CEILING( (x1 + x2 + 1) / 2 );
|
|
break;
|
|
|
|
case 2:
|
|
case 5:
|
|
/* Drop-out Control Rule #4 */
|
|
|
|
/* The spec is not very clear regarding rule #4. It */
|
|
/* presents a method that is way too costly to implement */
|
|
/* while the general idea seems to get rid of `stubs'. */
|
|
/* */
|
|
/* Here, we only get rid of stubs recognized when: */
|
|
/* */
|
|
/* upper stub: */
|
|
/* */
|
|
/* - P_Left and P_Right are in the same contour */
|
|
/* - P_Right is the successor of P_Left in that contour */
|
|
/* - y is the top of P_Left and P_Right */
|
|
/* */
|
|
/* lower stub: */
|
|
/* */
|
|
/* - P_Left and P_Right are in the same contour */
|
|
/* - P_Left is the successor of P_Right in that contour */
|
|
/* - y is the bottom of P_Left */
|
|
/* */
|
|
|
|
/* upper stub test */
|
|
if ( ( left->next == right && left->height <= 0 ) ||
|
|
|
|
/* lower stub test */
|
|
( right->next == left && left->start == y ) ||
|
|
|
|
/* check that the rightmost pixel isn't set */
|
|
ras.render.test_pixel( RAS_VAR_ y, TRUNC(e1)) )
|
|
goto Next_Dropout;
|
|
|
|
if ( ras.dropout_mode == 2 )
|
|
e1 = e2;
|
|
else
|
|
e1 = CEILING( (x1 + x2 + 1)/2 );
|
|
|
|
break;
|
|
|
|
default:
|
|
goto Next_Dropout; /* Unsupported mode */
|
|
}
|
|
}
|
|
else
|
|
goto Next_Dropout;
|
|
}
|
|
|
|
PTRACE2(( " -> setting pixel" ));
|
|
ras.render.set_pixel( RAS_VAR_ y,
|
|
TRUNC( e1 ),
|
|
(x2 - x1) >> ras.scale_shift );
|
|
Next_Dropout:
|
|
|
|
dropouts--;
|
|
}
|
|
goto Next_Line;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* Render_Single_Pass */
|
|
/* */
|
|
/* <Description> */
|
|
/* Performs one sweep with sub-banding. */
|
|
/* */
|
|
/* <Input> */
|
|
/* flipped :: whether or not we have to flip. */
|
|
/* */
|
|
/* <Returns> */
|
|
/* Error code. 0 means success. */
|
|
/* */
|
|
static
|
|
int Render_Single_Pass( RAS_ARG_ int flipped )
|
|
{
|
|
TBand* band;
|
|
|
|
|
|
ras.flipped = flipped;
|
|
|
|
band = ras.band_stack;
|
|
|
|
PTRACE2(( "raster: entering render_single_pass (flipped = %d)\n",
|
|
flipped ));
|
|
|
|
while ( band >= ras.band_stack )
|
|
{
|
|
ras.maxY = ((long)band[0].y_max << PRECISION_BITS) - 1;
|
|
ras.minY = (long)band[0].y_min << PRECISION_BITS;
|
|
|
|
ras.cursor = ras.pool;
|
|
ras.error = 0;
|
|
|
|
PTRACE2(( "raster: band = [ %d, %d ]\n",
|
|
band[0].y_min,
|
|
band[0].y_max ));
|
|
|
|
if ( Convert_Glyph( RAS_VAR_ ras.outline ) )
|
|
{
|
|
int bottom, top, half;
|
|
|
|
|
|
if ( ras.error != ErrRaster_Overflow )
|
|
return FAILURE;
|
|
ras.error = ErrRaster_Ok;
|
|
|
|
PTRACE2(( "conversion failure, performing sub-banding\n" ));
|
|
|
|
/* sub-banding */
|
|
|
|
#ifdef DEBUG_RASTER
|
|
ClearBand( RAS_VAR_ TRUNC( ras.minY ), TRUNC( ras.maxY ) );
|
|
#endif
|
|
|
|
bottom = band[0].y_min;
|
|
top = band[0].y_max;
|
|
half = ( top - bottom ) >> 1;
|
|
|
|
if ( band >= ras.band_stack + 7 || half == 0 )
|
|
{
|
|
ras.band_top = 0;
|
|
ras.error = ErrRaster_Invalid_Outline;
|
|
return ras.error;
|
|
}
|
|
|
|
band[1].y_min = bottom + half;
|
|
band[1].y_max = top;
|
|
band[0].y_max = bottom + half;
|
|
|
|
band ++;
|
|
}
|
|
else
|
|
{
|
|
PTRACE2(( "conversion succeeded, span drawing sweep\n" ));
|
|
#if 1 /* for debugging */
|
|
if ( ras.start_prof )
|
|
if ( Draw_Sweep( RAS_VAR ) )
|
|
return ras.error;
|
|
#endif
|
|
band --;
|
|
}
|
|
}
|
|
|
|
PTRACE2(( "raster: exiting render_single_pass\n" ));
|
|
|
|
return SUCCESS; /* success */
|
|
}
|
|
|
|
|
|
static
|
|
int Raster_Render1( FT_Raster raster )
|
|
{
|
|
int error;
|
|
|
|
|
|
if ( ras.target.width > ABS(ras.target.pitch)*8 )
|
|
return ErrRaster_Invalid_Map;
|
|
|
|
ras.scale_shift = PRECISION_BITS - INPUT_BITS;
|
|
ras.scale_delta = PRECISION_HALF;
|
|
|
|
/* Vertical Sweep */
|
|
ras.band_top = 0;
|
|
ras.band_stack[0].y_min = 0;
|
|
ras.band_stack[0].y_max = ras.target.rows;
|
|
|
|
ras.render = vertical_render_mono;
|
|
ras.bit_width = ras.target.width;
|
|
ras.bit_buffer = (unsigned char*)ras.target.buffer;
|
|
|
|
if ( (error = Render_Single_Pass( RAS_VAR_ 0 )) != 0 )
|
|
return error;
|
|
|
|
/* Horizontal Sweep */
|
|
|
|
if ( ras.second_pass && ras.dropout_mode != 0 )
|
|
{
|
|
ras.render = horizontal_render_mono;
|
|
ras.band_top = 0;
|
|
ras.band_stack[0].y_min = 0;
|
|
ras.band_stack[0].y_max = ras.target.width;
|
|
|
|
if ( (error = Render_Single_Pass( RAS_VAR_ 1 )) != 0 )
|
|
return error;
|
|
}
|
|
|
|
return ErrRaster_Ok;
|
|
}
|
|
|
|
|
|
#ifdef FT_RASTER_OPTION_ANTI_ALIAS
|
|
|
|
|
|
static
|
|
int Raster_Render8( FT_Raster raster )
|
|
{
|
|
int error;
|
|
|
|
if ( ras.target.width > ABS(ras.target.pitch) )
|
|
return ErrRaster_Invalid_Map;
|
|
|
|
/* Vertical Sweep */
|
|
ras.band_top = 0;
|
|
ras.band_stack[0].y_min = 0;
|
|
ras.band_stack[0].y_max = ras.target.rows;
|
|
|
|
ras.scale_shift = PRECISION_BITS - INPUT_BITS;
|
|
ras.scale_delta = PRECISION_HALF;
|
|
ras.dropout_mode = 2;
|
|
|
|
ras.render = vertical_render_gray;
|
|
ras.bit_width = ras.target.width;
|
|
ras.bit_buffer = (unsigned char*)ras.target.buffer;
|
|
ras.pix_buffer = (unsigned char*)ras.target.buffer;
|
|
|
|
error = Render_Single_Pass( RAS_VAR_ 0 );
|
|
if ( error )
|
|
return error;
|
|
|
|
#if 1
|
|
/* Horizontal Sweep */
|
|
ras.render = horizontal_render_gray;
|
|
ras.band_top = 0;
|
|
ras.bit_width = ras.target.rows;
|
|
ras.band_stack[0].y_min = 0;
|
|
ras.band_stack[0].y_max = ras.target.width;
|
|
|
|
return Render_Single_Pass( RAS_VAR_ 1 );
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
#else /* FT_RASTER_OPTION_ANTI_ALIAS */
|
|
|
|
|
|
static
|
|
int Raster_Render8( FT_Raster raster )
|
|
{
|
|
return ErrRaster_Unimplemented;
|
|
}
|
|
|
|
|
|
#endif /* FT_RASTER_OPTION_ANTI_ALIAS */
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* FT_Raster_Render */
|
|
/* */
|
|
/* <Description> */
|
|
/* Renders an outline into a target bitmap. */
|
|
/* */
|
|
/* <Input> */
|
|
/* raster :: A handle to the raster object used during rendering. */
|
|
/* outline :: A pointer to the source outline record/object. */
|
|
/* bitmap :: A pointer to the target bitmap descriptor. */
|
|
/* */
|
|
/* <Return> */
|
|
/* Error code, interpreted as a FT_Error by FreeType. 0 means */
|
|
/* success. */
|
|
/* */
|
|
EXPORT_FUNC
|
|
int FT_Raster_Render( FT_Raster raster,
|
|
FT_Outline* outline,
|
|
FT_Bitmap* target_map )
|
|
{
|
|
if ( !raster || !raster->pool || !raster->pool_size )
|
|
return ErrRaster_Uninitialized_Object;
|
|
|
|
/* return immediately if the outline is empty */
|
|
if ( outline->n_points == 0 || outline->n_contours <= 0 )
|
|
return ErrRaster_Ok;
|
|
|
|
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 ErrRaster_Invalid_Map;
|
|
|
|
ras.outline = outline;
|
|
ras.target = *target_map;
|
|
|
|
ras.dropout_mode = outline->dropout_mode;
|
|
ras.second_pass = outline->second_pass;
|
|
SET_High_Precision( outline->high_precision );
|
|
|
|
switch ( target_map->pixel_mode )
|
|
{
|
|
case ft_pixel_mode_mono: return Raster_Render1( raster );
|
|
case ft_pixel_mode_grays: return Raster_Render8( raster );
|
|
default: return ErrRaster_Unimplemented;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* FT_Raster_ObjSize */
|
|
/* */
|
|
/* <Description> */
|
|
/* This function returns the size of a raster object in bytes. */
|
|
/* Client applications are thus able to allocate objects in their own */
|
|
/* heap/memory space, without revealing the internal structures of */
|
|
/* the scan-line converter. */
|
|
/* */
|
|
/* <Return> */
|
|
/* The size in bytes of a single raster object. */
|
|
/* */
|
|
EXPORT_FUNC
|
|
long FT_Raster_ObjSize( void )
|
|
{
|
|
return (long)sizeof( struct FT_RasterRec_ );
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* FT_Raster_Init */
|
|
/* */
|
|
/* <Description> */
|
|
/* Initializes a fresh raster object which should have been allocated */
|
|
/* by client applications. This function is also used to set the */
|
|
/* object's render pool. It can be used repeatedly on a single */
|
|
/* object if one wants to change the pool's address or size. */
|
|
/* */
|
|
/* Note that the render pool has no state and is only used during a */
|
|
/* call to FT_Raster_Render(). It is thus theorically possible to */
|
|
/* share it between several non-concurrent components of your */
|
|
/* applications when memory is a scarce resource. */
|
|
/* */
|
|
/* <Input> */
|
|
/* pool_size :: The render pool's size in bytes. This must be at */
|
|
/* least 4 kByte. */
|
|
/* */
|
|
/* <InOut> */
|
|
/* raster :: A handle to the target raster object. */
|
|
/* */
|
|
/* pool_base :: The render pool's base address in memory. */
|
|
/* */
|
|
/* <Return> */
|
|
/* An error condition, used as a FT_Error in the FreeType library. */
|
|
/* 0 means success. */
|
|
/* */
|
|
EXPORT_FUNC
|
|
int FT_Raster_Init( FT_Raster raster,
|
|
const char* pool_base,
|
|
long pool_size )
|
|
{
|
|
/* static const char default_palette[5] = { 0, 1, 2, 3, 4 }; */
|
|
|
|
/* check the object address */
|
|
if ( !raster )
|
|
return ErrRaster_Uninitialized_Object;
|
|
|
|
/* check the render pool - we won't go under 4 Kb */
|
|
if ( !pool_base || pool_size < 4096 )
|
|
return ErrRaster_Invalid_Pool;
|
|
|
|
/* save the pool */
|
|
raster->pool = (PPos)pool_base;
|
|
raster->pool_size = raster->pool + pool_size / sizeof ( TPos );
|
|
|
|
return ErrRaster_Ok;
|
|
}
|
|
|
|
|
|
|
|
FT_Raster_Interface ft_default_raster =
|
|
{
|
|
sizeof( struct FT_RasterRec_ ),
|
|
ft_glyph_format_outline,
|
|
|
|
(FT_Raster_Init_Proc) FT_Raster_Init,
|
|
(FT_Raster_Set_Mode_Proc) 0,
|
|
(FT_Raster_Render_Proc) FT_Raster_Render
|
|
};
|
|
|
|
|
|
/* END */
|