#include "ttraster.h"
#include "ttdebug.h"
#include "tttypes.h"
#include "ttengine.h"
#include "ttcalc.h"
#include "ttmemory.h"
#undef TT_COMPONENT
#define TT_COMPONENT trace_raster
#define RASTER_RENDER_POOL 64000
#define RASTER_GRAY_LINES 2048
#define Raster_Err_None TT_Err_Ok
#define Raster_Err_Not_Ini TT_Err_Raster_Not_Initialized
#define Raster_Err_Overflow TT_Err_Raster_Pool_Overflow
#define Raster_Err_Neg_Height TT_Err_Raster_Negative_Height
#define Raster_Err_Invalid TT_Err_Raster_Invalid_Value
#define Raster_Err_Gray_Unsupported TT_Err_Raster_Gray_Unsupported
#define FMulDiv( a, b, c ) ( (a) * (b) / (c) )
#define SMulDiv TT_MulDiv
#ifdef DEBUG_RASTER
extern Char* Vio;
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef NULL
#define NULL (void*)0
#endif
#define MaxBezier 32
#define Pixel_Bits 6
enum TStates_
{
Unknown,
Ascending,
Descending,
Flat
};
typedef enum TStates_ TStates;
struct TProfile_;
typedef struct TProfile_ TProfile;
typedef TProfile* PProfile;
struct TProfile_
{
TT_F26Dot6 X;
PProfile link;
PStorage offset;
Int flow;
Long height;
Long start;
UShort countL;
PProfile next;
};
typedef PProfile TProfileList;
typedef PProfile* PProfileList;
struct TBand_
{
Short y_min;
Short y_max;
};
typedef struct TBand_ TBand;
#define AlignProfileSize \
(( sizeof(TProfile)+sizeof(long)-1 ) / sizeof(long))
static const Byte LMask[8] =
{ 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };
static const Byte RMask[8] =
{ 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF };
typedef void Function_Sweep_Init( RAS_ARGS Short* min,
Short* max );
typedef void Function_Sweep_Span( RAS_ARGS Short y,
TT_F26Dot6 x1,
TT_F26Dot6 x2,
PProfile left,
PProfile right );
typedef void Function_Sweep_Step( RAS_ARG );
#define FLOOR( x ) ( (x) & -ras.precision )
#define CEILING( x ) ( ((x) + ras.precision - 1) & -ras.precision )
#define TRUNC( x ) ( (signed long)(x) >> ras.precision_bits )
#define FRAC( x ) ( (x) & (ras.precision - 1) )
#define SCALED( x ) ( ((x) << ras.scale_shift) - ras.precision_half )
#ifdef DEBUG_RASTER
#define DEBUG_PSET Pset()
#else
#define DEBUG_PSET
#endif
struct TPoint_
{
Long x, y;
};
typedef struct TPoint_ TPoint;
struct TRaster_Instance_
{
Int precision_bits;
Int precision;
Int precision_half;
Long precision_mask;
Int precision_shift;
Int precision_step;
Int precision_jitter;
Int scale_shift;
PStorage buff;
PStorage sizeBuff;
PStorage maxBuff;
PStorage top;
TT_Error error;
PByte flags;
PUShort outs;
UShort nPoints;
Short nContours;
Int numTurns;
TPoint* arc;
UShort bWidth;
PByte bTarget;
PByte gTarget;
Long lastX, lastY, minY, maxY;
UShort num_Profs;
Bool fresh;
Bool joint;
PProfile cProfile;
PProfile fProfile;
PProfile gProfile;
TStates state;
TT_Raster_Map target;
Long traceOfs;
Long traceG;
Short traceIncr;
Short gray_min_x;
Short gray_max_x;
Function_Sweep_Init* Proc_Sweep_Init;
Function_Sweep_Span* Proc_Sweep_Span;
Function_Sweep_Span* Proc_Sweep_Drop;
Function_Sweep_Step* Proc_Sweep_Step;
TT_Vector* coords;
Byte dropOutControl;
Byte grays[5];
Byte* gray_lines;
Short gray_width;
Bool second_pass;
TPoint arcs[2 * MaxBezier + 1];
TBand band_stack[16];
Int band_top;
Int count_table[256];
};
#ifdef TT_CONFIG_OPTION_STATIC_RASTER
static TRaster_Instance cur_ras;
#define ras cur_ras
#else
#define ras (*raster)
#endif
#ifdef DEBUG_RASTER
static void Pset( RAS_ARG )
{
Long o;
Long x;
x = ras.top[-1];
switch ( ras.cProfile->flow )
{
case TT_Flow_Up:
o = Vio_ScanLineWidth *
( ras.top-ras.cProfile->offset + ras.cProfile->start ) +
( x / (ras.precision*8) );
break;
case TT_Flow_Down:
o = Vio_ScanLineWidth *
( ras.cProfile->start-ras.top + ras.cProfile->offset ) +
( x / (ras.precision*8) );
break;
}
if ( o > 0 )
Vio[o] |= (unsigned)0x80 >> ( (x/ras.precision) & 7 );
}
static void Clear_Band( RAS_ARGS Int y1, Int y2 )
{
MEM_Set( Vio + y1*Vio_ScanLineWidth, (y2-y1+1)*Vio_ScanLineWidth, 0 );
}
#endif
static void Set_High_Precision( RAS_ARGS Bool High )
{
if ( High )
{
ras.precision_bits = 10;
ras.precision_step = 128;
ras.precision_jitter = 24;
}
else
{
ras.precision_bits = 6;
ras.precision_step = 32;
ras.precision_jitter = 2;
}
PTRACE7(( "Set_High_Precision(%s)\n", High ? "true" : "false" ));
ras.precision = 1 << ras.precision_bits;
ras.precision_half = ras.precision / 2;
ras.precision_shift = ras.precision_bits - Pixel_Bits;
ras.precision_mask = -ras.precision;
}
static Bool New_Profile( RAS_ARGS TStates aState )
{
if ( !ras.fProfile )
{
ras.cProfile = (PProfile)ras.top;
ras.fProfile = ras.cProfile;
ras.top += AlignProfileSize;
}
if ( ras.top >= ras.maxBuff )
{
ras.error = Raster_Err_Overflow;
return FAILURE;
}
switch ( aState )
{
case Ascending:
ras.cProfile->flow = TT_Flow_Up;
PTRACE7(( "New ascending profile = %lx\n", (long)ras.cProfile ));
break;
case Descending:
ras.cProfile->flow = TT_Flow_Down;
PTRACE7(( "New descending profile = %lx\n", (long)ras.cProfile ));
break;
default:
PTRACE0(( "Invalid profile direction in Raster:New_Profile !!\n" ));
ras.error = Raster_Err_Invalid;
return FAILURE;
}
ras.cProfile->start = 0;
ras.cProfile->height = 0;
ras.cProfile->offset = ras.top;
ras.cProfile->link = (PProfile)0;
ras.cProfile->next = (PProfile)0;
if ( !ras.gProfile )
ras.gProfile = ras.cProfile;
ras.state = aState;
ras.fresh = TRUE;
ras.joint = FALSE;
return SUCCESS;
}
static Bool End_Profile( RAS_ARG )
{
Long h;
PProfile oldProfile;
h = ras.top - ras.cProfile->offset;
if ( h < 0 )
{
PTRACE0(( "Negative height encountered in End_Profile!\n" ));
ras.error = Raster_Err_Neg_Height;
return FAILURE;
}
if ( h > 0 )
{
PTRACE1(( "Ending profile %lx, start = %ld, height = %ld\n",
(long)ras.cProfile, ras.cProfile->start, h ));
oldProfile = ras.cProfile;
ras.cProfile->height = h;
ras.cProfile = (PProfile)ras.top;
ras.top += AlignProfileSize;
ras.cProfile->height = 0;
ras.cProfile->offset = ras.top;
oldProfile->next = ras.cProfile;
ras.num_Profs++;
}
if ( ras.top >= ras.maxBuff )
{
PTRACE1(( "overflow in End_Profile\n" ));
ras.error = Raster_Err_Overflow;
return FAILURE;
}
ras.joint = FALSE;
return SUCCESS;
}
static
Bool Insert_Y_Turn( RAS_ARGS Int y )
{
PStorage y_turns;
Int y2, n;
n = ras.numTurns-1;
y_turns = ras.sizeBuff - ras.numTurns;
while ( n >= 0 && y < y_turns[n] )
n--;
if ( n >= 0 && y > y_turns[n] )
while ( n >= 0 )
{
y2 = y_turns[n];
y_turns[n] = y;
y = y2;
n--;
}
if ( n < 0 )
{
if (ras.maxBuff <= ras.top)
{
ras.error = Raster_Err_Overflow;
return FAILURE;
}
ras.maxBuff--;
ras.numTurns++;
ras.sizeBuff[-ras.numTurns] = y;
}
return SUCCESS;
}
static
Bool Finalize_Profile_Table( RAS_ARG )
{
Int bottom, top;
UShort n;
PProfile p;
n = ras.num_Profs;
if ( n > 1 )
{
p = ras.fProfile;
while ( n > 0 )
{
if ( n > 1 )
p->link = (PProfile)( p->offset + p->height );
else
p->link = NULL;
switch ( p->flow )
{
case TT_Flow_Down:
bottom = p->start - p->height+1;
top = p->start;
p->start = bottom;
p->offset += p->height-1;
break;
case TT_Flow_Up:
default:
bottom = p->start;
top = p->start + p->height-1;
}
if ( Insert_Y_Turn( RAS_VARS bottom ) ||
Insert_Y_Turn( RAS_VARS top+1 ) )
return FAILURE;
p = p->link;
n--;
}
}
else
ras.fProfile = NULL;
return SUCCESS;
}
static void Split_Bezier( TPoint* base )
{
Long a, b;
base[4].x = base[2].x;
b = base[1].x;
a = base[3].x = ( base[2].x + b ) / 2;
b = base[1].x = ( base[0].x + b ) / 2;
base[2].x = ( a + b ) / 2;
base[4].y = base[2].y;
b = base[1].y;
a = base[3].y = ( base[2].y + b ) / 2;
b = base[1].y = ( base[0].y + b ) / 2;
base[2].y = ( a + b ) / 2;
}
static void Push_Bezier( RAS_ARGS Long x1, Long y1,
Long x2, Long y2,
Long x3, Long y3 )
{
ras.arc = ras.arcs;
ras.arc[2].x = x1; ras.arc[2].y = y1;
ras.arc[1].x = x2; ras.arc[1].y = y2;
ras.arc[0].x = x3; ras.arc[0].y = y3;
}
static Bool Line_Up( RAS_ARGS Long x1, Long y1,
Long x2, Long y2,
Long miny, Long maxy )
{
Long Dx, Dy;
Int e1, e2, f1, f2, size;
Long Ix, Rx, Ax;
PStorage top;
Dx = x2 - x1;
Dy = y2 - y1;
if ( Dy <= 0 || y2 < miny || y1 > maxy )
return SUCCESS;
if ( y1 < miny )
{
x1 += SMulDiv( Dx, miny - y1, Dy );
e1 = TRUNC( miny );
f1 = 0;
}
else
{
e1 = TRUNC( y1 );
f1 = FRAC( y1 );
}
if ( y2 > maxy )
{
e2 = TRUNC( maxy );
f2 = 0;
}
else
{
e2 = TRUNC( y2 );
f2 = FRAC( y2 );
}
if ( f1 > 0 )
{
if ( e1 == e2 ) return SUCCESS;
else
{
x1 += FMulDiv( Dx, ras.precision - f1, Dy );
e1 += 1;
}
}
else
if ( ras.joint )
{
ras.top--;
ras.joint = FALSE;
}
ras.joint = ( f2 == 0 );
if ( ras.fresh )
{
ras.cProfile->start = e1;
ras.fresh = FALSE;
}
size = e2 - e1 + 1;
if ( ras.top + size >= ras.maxBuff )
{
ras.error = Raster_Err_Overflow;
return FAILURE;
}
if ( Dx > 0 )
{
Ix = (ras.precision*Dx) / Dy;
Rx = (ras.precision*Dx) % Dy;
Dx = 1;
}
else
{
Ix = -( (ras.precision*-Dx) / Dy );
Rx = (ras.precision*-Dx) % Dy;
Dx = -1;
}
Ax = -Dy;
top = ras.top;
while ( size > 0 )
{
*top++ = x1;
DEBUG_PSET;
x1 += Ix;
Ax += Rx;
if ( Ax >= 0 )
{
Ax -= Dy;
x1 += Dx;
}
size--;
}
ras.top = top;
return SUCCESS;
}
static Bool Line_Down( RAS_ARGS Long x1, Long y1,
Long x2, Long y2,
Long miny, Long maxy )
{
Bool result, fresh;
fresh = ras.fresh;
result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );
if ( fresh && !ras.fresh )
ras.cProfile->start = -ras.cProfile->start;
return result;
}
static Bool Bezier_Up( RAS_ARGS Long miny, Long maxy )
{
Long y1, y2, e, e2, e0;
Short f1;
TPoint* arc;
TPoint* start_arc;
PStorage top;
arc = ras.arc;
y1 = arc[2].y;
y2 = arc[0].y;
top = ras.top;
if ( y2 < miny || y1 > maxy )
goto Fin;
e2 = FLOOR( y2 );
if ( e2 > maxy )
e2 = maxy;
e0 = miny;
if ( y1 < miny )
e = miny;
else
{
e = CEILING( y1 );
f1 = FRAC( y1 );
e0 = e;
if ( f1 == 0 )
{
if ( ras.joint )
{
top--;
ras.joint = FALSE;
}
*top++ = arc[2].x;
DEBUG_PSET;
e += ras.precision;
}
}
if ( ras.fresh )
{
ras.cProfile->start = TRUNC( e0 );
ras.fresh = FALSE;
}
if ( e2 < e )
goto Fin;
if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff )
{
ras.top = top;
ras.error = Raster_Err_Overflow;
return FAILURE;
}
start_arc = arc;
while ( arc >= start_arc && e <= e2 )
{
ras.joint = FALSE;
y2 = arc[0].y;
if ( y2 > e )
{
y1 = arc[2].y;
if ( y2 - y1 >= ras.precision_step )
{
Split_Bezier( arc );
arc += 2;
}
else
{
*top++ = arc[2].x + FMulDiv( arc[0].x - arc[2].x,
e - y1,
y2 - y1 );
DEBUG_PSET;
arc -= 2;
e += ras.precision;
}
}
else
{
if ( y2 == e )
{
ras.joint = TRUE;
*top++ = arc[0].x;
DEBUG_PSET;
e += ras.precision;
}
arc -= 2;
}
}
Fin:
ras.top = top;
ras.arc -= 2;
return SUCCESS;
}
static Bool Bezier_Down( RAS_ARGS Long miny, Long maxy )
{
TPoint* arc = ras.arc;
Bool result, fresh;
arc[0].y = -arc[0].y;
arc[1].y = -arc[1].y;
arc[2].y = -arc[2].y;
fresh = ras.fresh;
result = Bezier_Up( RAS_VARS -maxy, -miny );
if ( fresh && !ras.fresh )
ras.cProfile->start = -ras.cProfile->start;
arc[0].y = -arc[0].y;
return result;
}
static Bool Line_To( RAS_ARGS Long x, Long y )
{
switch ( ras.state )
{
case Unknown:
if ( y > ras.lastY )
{
if ( New_Profile( RAS_VARS Ascending ) ) return FAILURE;
}
else
{
if ( y < ras.lastY )
if ( New_Profile( RAS_VARS Descending ) ) return FAILURE;
}
break;
case Ascending:
if ( y < ras.lastY )
{
if ( End_Profile( RAS_VAR ) ||
New_Profile( RAS_VARS Descending ) ) return FAILURE;
}
break;
case Descending:
if ( y > ras.lastY )
{
if ( End_Profile( RAS_VAR ) ||
New_Profile( RAS_VARS Ascending ) ) return FAILURE;
}
break;
default:
;
}
switch ( ras.state )
{
case Ascending:
if ( Line_Up ( RAS_VARS ras.lastX, ras.lastY,
x, y, ras.minY, ras.maxY ) )
return FAILURE;
break;
case Descending:
if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
x, y, ras.minY, ras.maxY ) )
return FAILURE;
break;
default:
;
}
ras.lastX = x;
ras.lastY = y;
return SUCCESS;
}
static Bool Bezier_To( RAS_ARGS Long x,
Long y,
Long cx,
Long cy )
{
Long y1, y2, y3, x3;
TStates state_bez;
Push_Bezier( RAS_VARS ras.lastX, ras.lastY, cx, cy, x, y );
do
{
y1 = ras.arc[2].y;
y2 = ras.arc[1].y;
y3 = ras.arc[0].y;
x3 = ras.arc[0].x;
if ( y1 == y2 )
{
if ( y2 == y3 )
state_bez = Flat;
else if ( y2 > y3 )
state_bez = Descending;
else
state_bez = Ascending;
}
else if ( y1 > y2 )
{
if ( y2 >= y3 )
state_bez = Descending;
else
state_bez = Unknown;
}
else if ( y2 <= y3 )
state_bez = Ascending;
else
state_bez = Unknown;
switch ( state_bez )
{
case Flat:
ras.arc -= 2;
break;
case Unknown:
Split_Bezier( ras.arc );
ras.arc += 2;
break;
default:
if ( ras.state != state_bez )
{
if ( ras.state != Unknown )
if ( End_Profile( RAS_VAR ) ) return FAILURE;
if ( New_Profile( RAS_VARS state_bez ) ) return FAILURE;
}
switch ( ras.state )
{
case Ascending:
if ( Bezier_Up ( RAS_VARS ras.minY, ras.maxY ) )
return FAILURE;
break;
case Descending:
if ( Bezier_Down( RAS_VARS ras.minY, ras.maxY ) )
return FAILURE;
break;
default:
;
}
}
} while ( ras.arc >= ras.arcs );
ras.lastX = x3;
ras.lastY = y3;
return SUCCESS;
}
#undef SWAP_
#define SWAP_(x,y) { Long swap = x; x = y; y = swap; }
static Bool Decompose_Curve( RAS_ARGS UShort first,
UShort last,
Bool flipped )
{
Long x, y;
Long cx, cy;
Long mx, my;
Long x_first, y_first;
Long x_last, y_last;
UShort index;
Bool on_curve;
x_first = SCALED( ras.coords[first].x );
y_first = SCALED( ras.coords[first].y );
if ( flipped ) SWAP_( x_first,y_first );
x_last = SCALED( ras.coords[last].x );
y_last = SCALED( ras.coords[last].y );
if ( flipped ) SWAP_( x_last,y_last );
ras.lastX = cx = x_first;
ras.lastY = cy = y_first;
on_curve = (ras.flags[first] & 1);
index = first;
if ( !on_curve )
{
if ( ras.flags[last] & 1 )
{
ras.lastX = x_last;
ras.lastY = y_last;
}
else
{
ras.lastX = (ras.lastX + x_last)/2;
ras.lastY = (ras.lastY + y_last)/2;
x_last = ras.lastX;
y_last = ras.lastY;
}
}
while ( index < last )
{
index++;
x = SCALED( ras.coords[index].x );
y = SCALED( ras.coords[index].y );
if ( flipped ) SWAP_( x, y );
if ( on_curve )
{
on_curve = ( ras.flags[index] & 1 );
if ( on_curve )
{
if ( Line_To( RAS_VARS x, y ) ) return FAILURE;
}
else
{
cx = x;
cy = y;
}
}
else
{
on_curve = ( ras.flags[index] & 1 );
if ( on_curve )
{
if ( Bezier_To( RAS_VARS x, y, cx, cy ) ) return FAILURE;
}
else
{
mx = ( cx + x ) / 2;
my = ( cy + y ) / 2;
if ( Bezier_To( RAS_VARS mx, my, cx, cy ) ) return FAILURE;
cx = x;
cy = y;
}
}
}
if ( ras.flags[first] & 1 )
{
if ( on_curve )
return Line_To( RAS_VARS x_first, y_first );
else
return Bezier_To( RAS_VARS x_first, y_first, cx, cy );
}
else
if ( !on_curve )
return Bezier_To( RAS_VARS x_last, y_last, cx, cy );
return SUCCESS;
}
static Bool Convert_Glyph( RAS_ARGS int flipped )
{
Short i;
UShort start;
PProfile lastProfile;
ras.fProfile = NULL;
ras.joint = FALSE;
ras.fresh = FALSE;
ras.maxBuff = ras.sizeBuff - AlignProfileSize;
ras.numTurns = 0;
ras.cProfile = (PProfile)ras.top;
ras.cProfile->offset = ras.top;
ras.num_Profs = 0;
start = 0;
for ( i = 0; i < ras.nContours; i++ )
{
ras.state = Unknown;
ras.gProfile = NULL;
if ( Decompose_Curve( RAS_VARS start, ras.outs[i], flipped ) )
return FAILURE;
start = ras.outs[i] + 1;
if ( ( FRAC( ras.lastY ) == 0 &&
ras.lastY >= ras.minY &&
ras.lastY <= ras.maxY ) )
if ( ras.gProfile && ras.gProfile->flow == ras.cProfile->flow )
ras.top--;
lastProfile = ras.cProfile;
if ( End_Profile( RAS_VAR ) ) return FAILURE;
if ( ras.gProfile )
lastProfile->next = ras.gProfile;
}
if (Finalize_Profile_Table( RAS_VAR ))
return FAILURE;
return (ras.top < ras.maxBuff ? SUCCESS : FAILURE );
}
static void Init_Linked( TProfileList* l )
{
*l = NULL;
}
static void InsNew( PProfileList list,
PProfile profile )
{
PProfile *old, current;
Long x;
old = list;
current = *old;
x = profile->X;
while ( current )
{
if ( x < current->X )
break;
old = ¤t->link;
current = *old;
}
profile->link = current;
*old = profile;
}
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;
}
}
static void Update( PProfile first )
{
PProfile current = first;
while ( current )
{
current->X = *current->offset;
current->offset += current->flow;
current->height--;
current = current->link;
}
}
static void Sort( PProfileList list )
{
PProfile *old, current, next;
Update( *list );
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;
}
}
static void Vertical_Sweep_Init( RAS_ARGS Short* min, Short* max )
{
switch ( ras.target.flow )
{
case TT_Flow_Up:
ras.traceOfs = *min * ras.target.cols;
ras.traceIncr = ras.target.cols;
break;
default:
ras.traceOfs = ( ras.target.rows - 1 - *min ) * ras.target.cols;
ras.traceIncr = -ras.target.cols;
}
ras.gray_min_x = 0;
ras.gray_max_x = 0;
}
static void Vertical_Sweep_Span( RAS_ARGS Short y,
TT_F26Dot6 x1,
TT_F26Dot6 x2,
PProfile left,
PProfile right )
{
Long e1, e2;
Short c1, c2;
Short f1, f2;
Byte* target;
e1 = TRUNC( CEILING( x1 ) );
if ( x2-x1-ras.precision <= ras.precision_jitter )
e2 = e1;
else
e2 = TRUNC( FLOOR( x2 ) );
if ( e2 >= 0 && e1 < ras.bWidth )
{
if ( e1 < 0 ) e1 = 0;
if ( e2 >= ras.bWidth ) e2 = ras.bWidth-1;
c1 = (Short)(e1 >> 3);
c2 = (Short)(e2 >> 3);
f1 = e1 & 7;
f2 = e2 & 7;
if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1;
if ( ras.gray_max_x < c2 ) ras.gray_max_x = c2;
target = ras.bTarget + ras.traceOfs + c1;
if ( c1 != c2 )
{
*target |= LMask[f1];
if ( c2 > c1 + 1 )
MEM_Set( target + 1, 0xFF, c2 - c1 - 1 );
target[c2 - c1] |= RMask[f2];
}
else
*target |= ( LMask[f1] & RMask[f2] );
}
}
static void Vertical_Sweep_Drop( RAS_ARGS Short y,
TT_F26Dot6 x1,
TT_F26Dot6 x2,
PProfile left,
PProfile right )
{
Long e1, e2;
Short c1, f1;
e1 = CEILING( x1 );
e2 = FLOOR ( x2 );
if ( e1 > e2 )
{
if ( e1 == e2 + ras.precision )
{
switch ( ras.dropOutControl )
{
case 1:
e1 = e2;
break;
case 4:
e1 = CEILING( (x1 + x2 + 1) / 2 );
break;
case 2:
case 5:
{
if ( left->next == right && left->height <= 0 ) return;
if ( right->next == left && left->start == y ) return;
}
e1 = TRUNC( e1 );
c1 = (Short)(e1 >> 3);
f1 = e1 & 7;
if ( e1 >= 0 && e1 < ras.bWidth &&
ras.bTarget[ras.traceOfs + c1] & (0x80 >> f1) )
return;
if ( ras.dropOutControl == 2 )
e1 = e2;
else
e1 = CEILING( (x1 + x2 + 1) / 2 );
break;
default:
return;
}
}
else
return;
}
e1 = TRUNC( e1 );
if ( e1 >= 0 && e1 < ras.bWidth )
{
c1 = (Short)(e1 >> 3);
f1 = e1 & 7;
if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1;
if ( ras.gray_max_x < c1 ) ras.gray_max_x = c1;
ras.bTarget[ras.traceOfs + c1] |= (Char)(0x80 >> f1);
}
}
static void Vertical_Sweep_Step( RAS_ARG )
{
ras.traceOfs += ras.traceIncr;
}
static void Horizontal_Sweep_Init( RAS_ARGS Short* min, Short* max )
{
}
static void Horizontal_Sweep_Span( RAS_ARGS Short y,
TT_F26Dot6 x1,
TT_F26Dot6 x2,
PProfile left,
PProfile right )
{
Long e1, e2;
PByte bits;
Byte f1;
if ( x2-x1 < ras.precision )
{
e1 = CEILING( x1 );
e2 = FLOOR ( x2 );
if ( e1 == e2 )
{
bits = ras.bTarget + (y >> 3);
f1 = (Byte)(0x80 >> (y & 7));
e1 = TRUNC( e1 );
if ( e1 >= 0 && e1 < ras.target.rows )
{
if ( ras.target.flow == TT_Flow_Down )
bits[(ras.target.rows-1 - e1) * ras.target.cols] |= f1;
else
bits[e1 * ras.target.cols] |= f1;
}
}
}
#if 0
e2 = TRUNC( e2 );
if ( e2 >= 0 && e2 < ras.target.rows )
if ( ras.target.flow == TT_Flow_Down )
bits[(ras.target.rows-1-e2) * ras.target.cols] |= f1;
else
bits[e2 * ras.target.cols] |= f1;
#endif
}
static void Horizontal_Sweep_Drop( RAS_ARGS Short y,
TT_F26Dot6 x1,
TT_F26Dot6 x2,
PProfile left,
PProfile right )
{
Long e1, e2;
PByte bits;
Byte f1;
e1 = CEILING( x1 );
e2 = FLOOR ( x2 );
if ( e1 > e2 )
{
if ( e1 == e2 + ras.precision )
{
switch ( ras.dropOutControl )
{
case 1:
e1 = e2;
break;
case 4:
e1 = CEILING( (x1 + x2 + 1) / 2 );
break;
case 2:
case 5:
if ( left->next == right && left->height <= 0 ) return;
if ( right->next == left && left->start == y ) return;
e1 = TRUNC( e1 );
bits = ras.bTarget + (y >> 3);
f1 = (Byte)(0x80 >> (y & 7));
if ( ras.target.flow == TT_Flow_Down )
bits += (ras.target.rows-1-e1) * ras.target.cols;
else
bits += e1 * ras.target.cols;
if ( e1 >= 0 &&
e1 < ras.target.rows &&
*bits & f1 )
return;
if ( ras.dropOutControl == 2 )
e1 = e2;
else
e1 = CEILING( (x1 + x2 + 1) / 2 );
break;
default:
return;
}
}
else
return;
}
bits = ras.bTarget + (y >> 3);
f1 = (Byte)(0x80 >> (y & 7));
e1 = TRUNC( e1 );
if ( e1 >= 0 && e1 < ras.target.rows )
{
if (ras.target.flow==TT_Flow_Down)
bits[(ras.target.rows-1-e1) * ras.target.cols] |= f1;
else
bits[e1 * ras.target.cols] |= f1;
}
}
static void Horizontal_Sweep_Step( RAS_ARG )
{
}
#ifdef TT_CONFIG_OPTION_GRAY_SCALING
static void Vertical_Gray_Sweep_Init( RAS_ARGS Short* min, Short* max )
{
*min = *min & -2;
*max = ( *max + 3 ) & -2;
ras.traceOfs = 0;
switch ( ras.target.flow )
{
case TT_Flow_Up:
ras.traceG = (*min / 2) * ras.target.cols;
ras.traceIncr = ras.target.cols;
break;
default:
ras.traceG = (ras.target.rows-1 - *min/2) * ras.target.cols;
ras.traceIncr = -ras.target.cols;
}
ras.gray_min_x = ras.target.cols;
ras.gray_max_x = -ras.target.cols;
}
static void Vertical_Gray_Sweep_Step( RAS_ARG )
{
Int c1, c2;
PByte pix, bit, bit2;
Int* count = ras.count_table;
Byte* grays;
ras.traceOfs += ras.gray_width;
if ( ras.traceOfs > ras.gray_width )
{
pix = ras.gTarget + ras.traceG + ras.gray_min_x * 4;
grays = ras.grays;
if ( ras.gray_max_x >= 0 )
{
if ( ras.gray_max_x >= ras.target.width )
ras.gray_max_x = ras.target.width-1;
if ( ras.gray_min_x < 0 )
ras.gray_min_x = 0;
bit = ras.bTarget + ras.gray_min_x;
bit2 = bit + ras.gray_width;
c1 = ras.gray_max_x - ras.gray_min_x;
while ( c1 >= 0 )
{
c2 = count[*bit] + count[*bit2];
if ( c2 )
{
pix[0] = grays[(c2 & 0xF000) >> 12];
pix[1] = grays[(c2 & 0x0F00) >> 8];
pix[2] = grays[(c2 & 0x00F0) >> 4];
pix[3] = grays[(c2 & 0x000F) ];
*bit = 0;
*bit2 = 0;
}
bit ++;
bit2++;
pix += 4;
c1 --;
}
}
ras.traceOfs = 0;
ras.traceG += ras.traceIncr;
ras.gray_min_x = ras.target.cols;
ras.gray_max_x = -ras.target.cols;
}
}
static void Horizontal_Gray_Sweep_Span( RAS_ARGS Short y,
TT_F26Dot6 x1,
TT_F26Dot6 x2,
PProfile left,
PProfile right )
{
}
static void Horizontal_Gray_Sweep_Drop( RAS_ARGS Short y,
TT_F26Dot6 x1,
TT_F26Dot6 x2,
PProfile left,
PProfile right )
{
Long e1, e2;
PByte pixel;
Byte color;
e1 = CEILING( x1 );
e2 = FLOOR ( x2 );
if ( e1 > e2 )
{
if ( e1 == e2 + ras.precision )
{
switch ( ras.dropOutControl )
{
case 1:
e1 = e2;
break;
case 4:
e1 = CEILING( (x1 + x2 + 1) / 2 );
break;
case 2:
case 5:
if ( left->next == right && left->height <= 0 ) return;
if ( right->next == left && left->start == y ) return;
if ( ras.dropOutControl == 2 )
e1 = e2;
else
e1 = CEILING( (x1 + x2 + 1) / 2 );
break;
default:
return;
}
}
else
return;
}
if ( e1 >= 0 )
{
if ( x2 - x1 >= ras.precision_half )
color = ras.grays[2];
else
color = ras.grays[1];
e1 = TRUNC( e1 ) / 2;
if ( e1 < ras.target.rows )
{
if ( ras.target.flow == TT_Flow_Down )
pixel = ras.gTarget +
(ras.target.rows - 1 - e1) * ras.target.cols + y / 2;
else
pixel = ras.gTarget +
e1 * ras.target.cols + y / 2;
if (pixel[0] == ras.grays[0])
pixel[0] = color;
}
}
}
#endif
static Bool Draw_Sweep( RAS_ARG )
{
Short y, y_change, y_height;
PProfile P, Q, P_Left, P_Right;
Short min_Y, max_Y, top, bottom, dropouts;
Long x1, x2, xs, e1, e2;
TProfileList wait;
TProfileList draw_left, draw_right;
Init_Linked( &wait );
Init_Linked( &draw_left );
Init_Linked( &draw_right );
P = ras.fProfile;
max_Y = (short)TRUNC( ras.minY );
min_Y = (short)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;
}
if ( ras.numTurns == 0 )
{
ras.error = Raster_Err_Invalid;
return FAILURE;
}
ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y );
P = wait;
while ( P )
{
P->countL = P->start - min_Y;
P = P->link;
}
y = min_Y;
y_height = 0;
if ( ras.numTurns > 0 &&
ras.sizeBuff[-ras.numTurns] == min_Y )
ras.numTurns--;
while ( ras.numTurns > 0 )
{
P = wait;
while ( P )
{
Q = P->link;
P->countL -= y_height;
if ( P->countL == 0 )
{
DelOld( &wait, P );
switch ( P->flow )
{
case TT_Flow_Up: InsNew( &draw_left, P ); break;
case TT_Flow_Down: InsNew( &draw_right, P ); break;
}
}
P = Q;
}
Sort( &draw_left );
Sort( &draw_right );
y_change = (Short)ras.sizeBuff[-ras.numTurns--];
y_height = y_change - y;
while ( y < y_change )
{
dropouts = 0;
P_Left = draw_left;
P_Right = draw_right;
while ( P_Left )
{
x1 = P_Left ->X;
x2 = P_Right->X;
if ( x1 > x2 )
{
xs = x1;
x1 = x2;
x2 = xs;
}
if ( x2-x1 <= ras.precision )
{
e1 = FLOOR( x1 );
e2 = CEILING( x2 );
if ( ras.dropOutControl != 0 &&
(e1 > e2 || e2 == e1 + ras.precision) )
{
P_Left ->X = x1;
P_Right->X = x2;
P_Left->countL = 1;
dropouts++;
goto Skip_To_Next;
}
}
ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right );
Skip_To_Next:
P_Left = P_Left->link;
P_Right = P_Right->link;
}
if (dropouts > 0)
goto Scan_DropOuts;
Next_Line:
ras.Proc_Sweep_Step( RAS_VAR );
y++;
if ( y < y_change )
{
Sort( &draw_left );
Sort( &draw_right );
}
}
{
PProfile Q, P;
P = draw_left;
while ( P )
{
Q = P->link;
if ( P->height == 0 )
DelOld( &draw_left, P );
P = Q;
}
}
{
PProfile Q, P = draw_right;
while ( P )
{
Q = P->link;
if ( P->height == 0 )
DelOld( &draw_right, P );
P = Q;
}
}
}
while ( y <= max_Y )
{
ras.Proc_Sweep_Step( RAS_VAR );
y++;
}
return SUCCESS;
Scan_DropOuts :
P_Left = draw_left;
P_Right = draw_right;
while ( P_Left )
{
if ( P_Left->countL )
{
P_Left->countL = 0;
ras.Proc_Sweep_Drop( RAS_VARS y,
P_Left->X,
P_Right->X,
P_Left,
P_Right );
}
P_Left = P_Left->link;
P_Right = P_Right->link;
}
goto Next_Line;
}
static TT_Error Render_Single_Pass( RAS_ARGS Bool flipped )
{
Short i, j, k;
while ( ras.band_top >= 0 )
{
ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision;
ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision;
ras.top = ras.buff;
ras.error = Raster_Err_None;
if ( Convert_Glyph( RAS_VARS flipped ) )
{
if ( ras.error != Raster_Err_Overflow ) return FAILURE;
ras.error = Raster_Err_None;
#ifdef DEBUG_RASTER
ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) );
#endif
i = ras.band_stack[ras.band_top].y_min;
j = ras.band_stack[ras.band_top].y_max;
k = ( i + j ) / 2;
if ( ras.band_top >= 7 || k < i )
{
ras.band_top = 0;
ras.error = Raster_Err_Invalid;
return ras.error;
}
ras.band_stack[ras.band_top+1].y_min = k;
ras.band_stack[ras.band_top+1].y_max = j;
ras.band_stack[ras.band_top].y_max = k - 1;
ras.band_top++;
}
else
{
if ( ras.fProfile )
if ( Draw_Sweep( RAS_VAR ) ) return ras.error;
ras.band_top--;
}
}
return TT_Err_Ok;
}
LOCAL_FUNC
TT_Error Render_Glyph( RAS_ARGS TT_Outline* glyph,
TT_Raster_Map* target_map )
{
TT_Error error;
if ( glyph->n_points == 0 || glyph->n_contours <= 0 )
return TT_Err_Ok;
if ( !ras.buff )
{
ras.error = Raster_Err_Not_Ini;
return ras.error;
}
if ( glyph->n_points < glyph->contours[glyph->n_contours - 1] )
{
ras.error = TT_Err_Too_Many_Points;
return ras.error;
}
if ( target_map )
ras.target = *target_map;
ras.outs = glyph->contours;
ras.flags = glyph->flags;
ras.nPoints = glyph->n_points;
ras.nContours = glyph->n_contours;
ras.coords = glyph->points;
Set_High_Precision( RAS_VARS glyph->high_precision );
ras.scale_shift = ras.precision_shift;
ras.dropOutControl = glyph->dropout_mode;
ras.second_pass = glyph->second_pass;
ras.Proc_Sweep_Init = Vertical_Sweep_Init;
ras.Proc_Sweep_Span = Vertical_Sweep_Span;
ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
ras.Proc_Sweep_Step = Vertical_Sweep_Step;
ras.band_top = 0;
ras.band_stack[0].y_min = 0;
ras.band_stack[0].y_max = ras.target.rows - 1;
ras.bWidth = ras.target.width;
ras.bTarget = (Byte*)ras.target.bitmap;
if ( (error = Render_Single_Pass( RAS_VARS 0 )) != 0 )
return error;
if ( ras.second_pass && ras.dropOutControl != 0 )
{
ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;
ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
ras.band_top = 0;
ras.band_stack[0].y_min = 0;
ras.band_stack[0].y_max = ras.target.width - 1;
if ( (error = Render_Single_Pass( RAS_VARS 1 )) != 0 )
return error;
}
return TT_Err_Ok;
}
#ifdef TT_CONFIG_OPTION_GRAY_SCALING
LOCAL_FUNC
TT_Error Render_Gray_Glyph( RAS_ARGS TT_Outline* glyph,
TT_Raster_Map* target_map,
Byte* palette )
{
Int i;
TT_Error error;
if ( !ras.buff )
{
ras.error = Raster_Err_Not_Ini;
return ras.error;
}
if ( glyph->n_points == 0 || glyph->n_contours <= 0 )
return TT_Err_Ok;
if ( glyph->n_points < glyph->contours[glyph->n_contours - 1] )
{
ras.error = TT_Err_Too_Many_Points;
return ras.error;
}
if ( palette )
{
for ( i = 0; i < 5; i++ )
ras.grays[i] = palette[i];
}
if ( target_map )
ras.target = *target_map;
ras.outs = glyph->contours;
ras.flags = glyph->flags;
ras.nPoints = glyph->n_points;
ras.nContours = glyph->n_contours;
ras.coords = glyph->points;
Set_High_Precision( RAS_VARS glyph->high_precision );
ras.scale_shift = ras.precision_shift+1;
ras.dropOutControl = glyph->dropout_mode;
ras.second_pass = glyph->second_pass;
ras.band_top = 0;
ras.band_stack[0].y_min = 0;
ras.band_stack[0].y_max = 2 * ras.target.rows - 1;
ras.bWidth = ras.gray_width;
if ( ras.bWidth > ras.target.cols/4 )
ras.bWidth = ras.target.cols/4;
ras.bWidth = ras.bWidth * 8;
ras.bTarget = (Byte*)ras.gray_lines;
ras.gTarget = (Byte*)ras.target.bitmap;
ras.Proc_Sweep_Init = Vertical_Gray_Sweep_Init;
ras.Proc_Sweep_Span = Vertical_Sweep_Span;
ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
ras.Proc_Sweep_Step = Vertical_Gray_Sweep_Step;
error = Render_Single_Pass( RAS_VARS 0 );
if (error)
return error;
if ( ras.second_pass && ras.dropOutControl != 0 )
{
ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
ras.Proc_Sweep_Span = Horizontal_Gray_Sweep_Span;
ras.Proc_Sweep_Drop = Horizontal_Gray_Sweep_Drop;
ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
ras.band_top = 0;
ras.band_stack[0].y_min = 0;
ras.band_stack[0].y_max = ras.target.width * 2 - 1;
error = Render_Single_Pass( RAS_VARS 1 );
if (error)
return error;
}
return TT_Err_Ok;
}
#endif
#undef ras
LOCAL_FUNC
TT_Error TTRaster_Done( PEngine_Instance engine )
{
TRaster_Instance* ras = (TRaster_Instance*)engine->raster_component;
if ( !ras )
return TT_Err_Ok;
FREE( ras->buff );
FREE( ras->gray_lines );
#ifndef TT_CONFIG_OPTION_STATIC_RASTER
FREE( engine->raster_component );
#endif
return TT_Err_Ok;
}
LOCAL_FUNC
TT_Error TTRaster_Init( PEngine_Instance engine )
{
TT_Error error;
Int i, l, j, c;
TRaster_Instance* ras;
#ifdef TT_CONFIG_OPTION_STATIC_RASTER
ras = engine->raster_component = &cur_ras;
#else
if ( ALLOC( engine->raster_component, sizeof ( TRaster_Instance ) ) )
return error;
ras = (TRaster_Instance*)engine->raster_component;
#endif
if ( ALLOC( ras->buff, RASTER_RENDER_POOL ) ||
ALLOC( ras->gray_lines, RASTER_GRAY_LINES ) )
return error;
ras->sizeBuff = ras->buff + ( RASTER_RENDER_POOL/sizeof(long) );
ras->gray_width = RASTER_GRAY_LINES/2;
for ( i = 0; i < 256; i++ )
{
l = 0;
j = i;
for ( c = 0; c < 4; c++ )
{
l <<= 4;
if ( j & 0x80 ) l++;
if ( j & 0x40 ) l++;
j = ( j << 2 ) & 0xFF;
}
ras->count_table[i] = l;
}
ras->dropOutControl = 2;
ras->error = Raster_Err_None;
return TT_Err_Ok;
}