1
/***************************************************************************/
5
/* The FreeType glyph rasterizer (body). */
7
/* Copyright 1996-2001, 2002, 2003, 2005, 2007, 2008, 2009, 2010 by */
8
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
10
/* This file is part of the FreeType project, and may only be used, */
11
/* modified, and distributed under the terms of the FreeType project */
12
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
13
/* this file you indicate that you have read the license and */
14
/* understand and accept it fully. */
16
/***************************************************************************/
18
/*************************************************************************/
20
/* This file can be compiled without the rest of the FreeType engine, by */
21
/* defining the _STANDALONE_ macro when compiling it. You also need to */
22
/* put the files `ftimage.h' and `ftmisc.h' into the $(incdir) */
23
/* directory. Typically, you should do something like */
25
/* - copy `src/raster/ftraster.c' (this file) to your current directory */
27
/* - copy `include/freetype/ftimage.h' and `src/raster/ftmisc.h' */
28
/* to your current directory */
30
/* - compile `ftraster' with the _STANDALONE_ macro defined, as in */
32
/* cc -c -D_STANDALONE_ ftraster.c */
34
/* The renderer can be initialized with a call to */
35
/* `ft_standard_raster.raster_new'; a bitmap can be generated */
36
/* with a call to `ft_standard_raster.raster_render'. */
38
/* See the comments and documentation in the file `ftimage.h' for more */
39
/* details on how the raster works. */
41
/*************************************************************************/
44
/*************************************************************************/
46
/* This is a rewrite of the FreeType 1.x scan-line converter */
48
/*************************************************************************/
52
#define FT_CONFIG_STANDARD_LIBRARY_H <stdlib.h>
54
#include <string.h> /* for memset */
59
#else /* !_STANDALONE_ */
63
#include FT_INTERNAL_CALC_H /* for FT_MulDiv only */
67
#endif /* !_STANDALONE_ */
70
/*************************************************************************/
72
/* A simple technical note on how the raster works */
73
/* ----------------------------------------------- */
75
/* Converting an outline into a bitmap is achieved in several steps: */
77
/* 1 - Decomposing the outline into successive `profiles'. Each */
78
/* profile is simply an array of scanline intersections on a given */
79
/* dimension. A profile's main attributes are */
81
/* o its scanline position boundaries, i.e. `Ymin' and `Ymax' */
83
/* o an array of intersection coordinates for each scanline */
84
/* between `Ymin' and `Ymax' */
86
/* o a direction, indicating whether it was built going `up' or */
87
/* `down', as this is very important for filling rules */
89
/* o its drop-out mode */
91
/* 2 - Sweeping the target map's scanlines in order to compute segment */
92
/* `spans' which are then filled. Additionally, this pass */
93
/* performs drop-out control. */
95
/* The outline data is parsed during step 1 only. The profiles are */
96
/* built from the bottom of the render pool, used as a stack. The */
97
/* following graphics shows the profile list under construction: */
99
/* __________________________________________________________ _ _ */
101
/* | profile | coordinates for | profile | coordinates for |--> */
102
/* | 1 | profile 1 | 2 | profile 2 |--> */
103
/* |_________|_________________|_________|_________________|__ _ _ */
107
/* start of render pool top */
109
/* The top of the profile stack is kept in the `top' variable. */
111
/* As you can see, a profile record is pushed on top of the render */
112
/* pool, which is then followed by its coordinates/intersections. If */
113
/* a change of direction is detected in the outline, a new profile is */
114
/* generated until the end of the outline. */
116
/* Note that when all profiles have been generated, the function */
117
/* Finalize_Profile_Table() is used to record, for each profile, its */
118
/* bottom-most scanline as well as the scanline above its upmost */
119
/* boundary. These positions are called `y-turns' because they (sort */
120
/* of) correspond to local extrema. They are stored in a sorted list */
121
/* built from the top of the render pool as a downwards stack: */
123
/* _ _ _______________________________________ */
125
/* <--| sorted list of | */
126
/* <--| extrema scanlines | */
127
/* _ _ __________________|____________________| */
131
/* maxBuff sizeBuff = end of pool */
133
/* This list is later used during the sweep phase in order to */
134
/* optimize performance (see technical note on the sweep below). */
136
/* Of course, the raster detects whether the two stacks collide and */
137
/* handles the situation properly. */
139
/*************************************************************************/
142
/*************************************************************************/
143
/*************************************************************************/
145
/** CONFIGURATION MACROS **/
147
/*************************************************************************/
148
/*************************************************************************/
150
/* define DEBUG_RASTER if you want to compile a debugging version */
151
/* #define DEBUG_RASTER */
153
/* define FT_RASTER_OPTION_ANTI_ALIASING if you want to support */
154
/* 5-levels anti-aliasing */
155
/* #define FT_RASTER_OPTION_ANTI_ALIASING */
157
/* The size of the two-lines intermediate bitmap used */
158
/* for anti-aliasing, in bytes. */
159
#define RASTER_GRAY_LINES 2048
162
/*************************************************************************/
163
/*************************************************************************/
165
/** OTHER MACROS (do not change) **/
167
/*************************************************************************/
168
/*************************************************************************/
170
/*************************************************************************/
172
/* The macro FT_COMPONENT is used in trace mode. It is an implicit */
173
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
174
/* messages during execution. */
177
#define FT_COMPONENT trace_raster
183
/* This macro is used to indicate that a function parameter is unused. */
184
/* Its purpose is simply to reduce compiler warnings. Note also that */
185
/* simply defining it as `(void)x' doesn't avoid warnings with certain */
186
/* ANSI compilers (e.g. LCC). */
187
#define FT_UNUSED( x ) (x) = (x)
189
/* Disable the tracing mechanism for simplicity -- developers can */
190
/* activate it easily by redefining these two macros. */
192
#define FT_ERROR( x ) do { } while ( 0 ) /* nothing */
196
#define FT_TRACE( x ) do { } while ( 0 ) /* nothing */
197
#define FT_TRACE1( x ) do { } while ( 0 ) /* nothing */
198
#define FT_TRACE6( x ) do { } while ( 0 ) /* nothing */
201
#define Raster_Err_None 0
202
#define Raster_Err_Not_Ini -1
203
#define Raster_Err_Overflow -2
204
#define Raster_Err_Neg_Height -3
205
#define Raster_Err_Invalid -4
206
#define Raster_Err_Unsupported -5
208
#define ft_memset memset
210
#define FT_DEFINE_RASTER_FUNCS( class_, glyph_format_, raster_new_, \
211
raster_reset_, raster_set_mode_, \
212
raster_render_, raster_done_ ) \
213
const FT_Raster_Funcs class_ = \
223
#else /* !_STANDALONE_ */
226
#include FT_INTERNAL_OBJECTS_H
227
#include FT_INTERNAL_DEBUG_H /* for FT_TRACE() and FT_ERROR() */
229
#include "rasterrs.h"
231
#define Raster_Err_None Raster_Err_Ok
232
#define Raster_Err_Not_Ini Raster_Err_Raster_Uninitialized
233
#define Raster_Err_Overflow Raster_Err_Raster_Overflow
234
#define Raster_Err_Neg_Height Raster_Err_Raster_Negative_Height
235
#define Raster_Err_Invalid Raster_Err_Invalid_Outline
236
#define Raster_Err_Unsupported Raster_Err_Cannot_Render_Glyph
239
#endif /* !_STANDALONE_ */
243
#define FT_MEM_SET( d, s, c ) ft_memset( d, s, c )
247
#define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count )
250
/* FMulDiv means `Fast MulDiv'; it is used in case where `b' is */
251
/* typically a small value and the result of a*b is known to fit into */
253
#define FMulDiv( a, b, c ) ( (a) * (b) / (c) )
255
/* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */
256
/* for clipping computations. It simply uses the FT_MulDiv() function */
257
/* defined in `ftcalc.h'. */
258
#define SMulDiv FT_MulDiv
260
/* The rasterizer is a very general purpose component; please leave */
261
/* the following redefinitions there (you never know your target */
273
#define NULL (void*)0
285
#define MaxBezier 32 /* The maximum number of stacked Bezier curves. */
286
/* Setting this constant to more than 32 is a */
287
/* pure waste of space. */
289
#define Pixel_Bits 6 /* fractional bits of *input* coordinates */
292
/*************************************************************************/
293
/*************************************************************************/
295
/** SIMPLE TYPE DECLARATIONS **/
297
/*************************************************************************/
298
/*************************************************************************/
301
typedef unsigned int UInt;
303
typedef unsigned short UShort, *PUShort;
304
typedef long Long, *PLong;
306
typedef unsigned char Byte, *PByte;
310
typedef union Alignment_
316
} Alignment, *PAlignment;
319
typedef struct TPoint_
327
/* values for the `flags' bit field */
329
#define Overshoot_Top 0x10
330
#define Overshoot_Bottom 0x20
333
/* States of each line, arc, and profile */
334
typedef enum TStates_
344
typedef struct TProfile_ TProfile;
345
typedef TProfile* PProfile;
349
FT_F26Dot6 X; /* current coordinate during sweep */
350
PProfile link; /* link to next profile (various purposes) */
351
PLong offset; /* start of profile's data in render pool */
352
unsigned flags; /* Bit 0-2: drop-out mode */
353
/* Bit 3: profile orientation (up/down) */
354
/* Bit 4: is top profile? */
355
/* Bit 5: is bottom profile? */
356
long height; /* profile's height in scanlines */
357
long start; /* profile's starting scanline */
359
unsigned countL; /* number of lines to step before this */
360
/* profile becomes drawable */
362
PProfile next; /* next profile in same contour, used */
363
/* during drop-out control */
366
typedef PProfile TProfileList;
367
typedef PProfile* PProfileList;
370
/* Simple record used to implement a stack of bands, required */
371
/* by the sub-banding mechanism */
372
typedef struct TBand_
374
Short y_min; /* band's minimum */
375
Short y_max; /* band's maximum */
380
#define AlignProfileSize \
381
( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( long ) )
384
#ifdef FT_STATIC_RASTER
387
#define RAS_ARGS /* void */
388
#define RAS_ARG /* void */
390
#define RAS_VARS /* void */
391
#define RAS_VAR /* void */
393
#define FT_UNUSED_RASTER do { } while ( 0 )
396
#else /* !FT_STATIC_RASTER */
399
#define RAS_ARGS PWorker worker,
400
#define RAS_ARG PWorker worker
402
#define RAS_VARS worker,
403
#define RAS_VAR worker
405
#define FT_UNUSED_RASTER FT_UNUSED( worker )
408
#endif /* !FT_STATIC_RASTER */
411
typedef struct TWorker_ TWorker, *PWorker;
414
/* prototypes used for sweep function dispatch */
416
Function_Sweep_Init( RAS_ARGS Short* min,
420
Function_Sweep_Span( RAS_ARGS Short y,
427
Function_Sweep_Step( RAS_ARG );
430
/* NOTE: These operations are only valid on 2's complement processors */
432
#define FLOOR( x ) ( (x) & -ras.precision )
433
#define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision )
434
#define TRUNC( x ) ( (signed long)(x) >> ras.precision_bits )
435
#define FRAC( x ) ( (x) & ( ras.precision - 1 ) )
436
#define SCALED( x ) ( ( (x) << ras.scale_shift ) - ras.precision_half )
438
#define IS_BOTTOM_OVERSHOOT( x ) ( CEILING( x ) - x >= ras.precision_half )
439
#define IS_TOP_OVERSHOOT( x ) ( x - FLOOR( x ) >= ras.precision_half )
441
/* The most used variables are positioned at the top of the structure. */
442
/* Thus, their offset can be coded with less opcodes, resulting in a */
443
/* smaller executable. */
447
Int precision_bits; /* precision related variables */
452
Int precision_jitter;
454
Int scale_shift; /* == precision_shift for bitmaps */
455
/* == precision_shift+1 for pixmaps */
457
PLong buff; /* The profiles buffer */
458
PLong sizeBuff; /* Render pool size */
459
PLong maxBuff; /* Profiles buffer size */
460
PLong top; /* Current cursor in buffer */
464
Int numTurns; /* number of Y-turns in outline */
466
TPoint* arc; /* current Bezier arc pointer */
468
UShort bWidth; /* target bitmap width */
469
PByte bTarget; /* target bitmap buffer */
470
PByte gTarget; /* target pixmap buffer */
475
UShort num_Profs; /* current number of profiles */
477
Bool fresh; /* signals a fresh new profile which */
478
/* `start' field must be completed */
479
Bool joint; /* signals that the last arc ended */
480
/* exactly on a scanline. Allows */
481
/* removal of doublets */
482
PProfile cProfile; /* current profile */
483
PProfile fProfile; /* head of linked list of profiles */
484
PProfile gProfile; /* contour's first profile in case */
487
TStates state; /* rendering state */
489
FT_Bitmap target; /* description of target bit/pixmap */
492
Long traceOfs; /* current offset in target bitmap */
493
Long traceG; /* current offset in target pixmap */
495
Short traceIncr; /* sweep's increment in target bitmap */
497
Short gray_min_x; /* current min x during gray rendering */
498
Short gray_max_x; /* current max x during gray rendering */
500
/* dispatch variables */
502
Function_Sweep_Init* Proc_Sweep_Init;
503
Function_Sweep_Span* Proc_Sweep_Span;
504
Function_Sweep_Span* Proc_Sweep_Drop;
505
Function_Sweep_Step* Proc_Sweep_Step;
507
Byte dropOutControl; /* current drop_out control method */
509
Bool second_pass; /* indicates whether a horizontal pass */
510
/* should be performed to control */
511
/* drop-out accurately when calling */
512
/* Render_Glyph. Note that there is */
513
/* no horizontal pass during gray */
516
TPoint arcs[3 * MaxBezier + 1]; /* The Bezier stack */
518
TBand band_stack[16]; /* band stack used for sub-banding */
519
Int band_top; /* band stack top */
521
#ifdef FT_RASTER_OPTION_ANTI_ALIASING
525
Byte gray_lines[RASTER_GRAY_LINES];
526
/* Intermediate table used to render the */
527
/* graylevels pixmaps. */
528
/* gray_lines is a buffer holding two */
529
/* monochrome scanlines */
531
Short gray_width; /* width in bytes of one monochrome */
532
/* intermediate scanline of gray_lines. */
533
/* Each gray pixel takes 2 bits long there */
535
/* The gray_lines must hold 2 lines, thus with size */
536
/* in bytes of at least `gray_width*2'. */
538
#endif /* FT_RASTER_ANTI_ALIASING */
543
typedef struct TRaster_
554
#ifdef FT_STATIC_RASTER
556
static TWorker cur_ras;
559
#else /* !FT_STATIC_RASTER */
561
#define ras (*worker)
563
#endif /* !FT_STATIC_RASTER */
566
#ifdef FT_RASTER_OPTION_ANTI_ALIASING
568
/* A lookup table used to quickly count set bits in four gray 2x2 */
569
/* cells. The values of the table have been produced with the */
570
/* following code: */
572
/* for ( i = 0; i < 256; i++ ) */
577
/* for ( c = 0; c < 4; c++ ) */
581
/* if ( j & 0x80 ) l++; */
582
/* if ( j & 0x40 ) l++; */
584
/* j = ( j << 2 ) & 0xFF; */
586
/* printf( "0x%04X", l ); */
590
static const short count_table[256] =
592
0x0000, 0x0001, 0x0001, 0x0002, 0x0010, 0x0011, 0x0011, 0x0012,
593
0x0010, 0x0011, 0x0011, 0x0012, 0x0020, 0x0021, 0x0021, 0x0022,
594
0x0100, 0x0101, 0x0101, 0x0102, 0x0110, 0x0111, 0x0111, 0x0112,
595
0x0110, 0x0111, 0x0111, 0x0112, 0x0120, 0x0121, 0x0121, 0x0122,
596
0x0100, 0x0101, 0x0101, 0x0102, 0x0110, 0x0111, 0x0111, 0x0112,
597
0x0110, 0x0111, 0x0111, 0x0112, 0x0120, 0x0121, 0x0121, 0x0122,
598
0x0200, 0x0201, 0x0201, 0x0202, 0x0210, 0x0211, 0x0211, 0x0212,
599
0x0210, 0x0211, 0x0211, 0x0212, 0x0220, 0x0221, 0x0221, 0x0222,
600
0x1000, 0x1001, 0x1001, 0x1002, 0x1010, 0x1011, 0x1011, 0x1012,
601
0x1010, 0x1011, 0x1011, 0x1012, 0x1020, 0x1021, 0x1021, 0x1022,
602
0x1100, 0x1101, 0x1101, 0x1102, 0x1110, 0x1111, 0x1111, 0x1112,
603
0x1110, 0x1111, 0x1111, 0x1112, 0x1120, 0x1121, 0x1121, 0x1122,
604
0x1100, 0x1101, 0x1101, 0x1102, 0x1110, 0x1111, 0x1111, 0x1112,
605
0x1110, 0x1111, 0x1111, 0x1112, 0x1120, 0x1121, 0x1121, 0x1122,
606
0x1200, 0x1201, 0x1201, 0x1202, 0x1210, 0x1211, 0x1211, 0x1212,
607
0x1210, 0x1211, 0x1211, 0x1212, 0x1220, 0x1221, 0x1221, 0x1222,
608
0x1000, 0x1001, 0x1001, 0x1002, 0x1010, 0x1011, 0x1011, 0x1012,
609
0x1010, 0x1011, 0x1011, 0x1012, 0x1020, 0x1021, 0x1021, 0x1022,
610
0x1100, 0x1101, 0x1101, 0x1102, 0x1110, 0x1111, 0x1111, 0x1112,
611
0x1110, 0x1111, 0x1111, 0x1112, 0x1120, 0x1121, 0x1121, 0x1122,
612
0x1100, 0x1101, 0x1101, 0x1102, 0x1110, 0x1111, 0x1111, 0x1112,
613
0x1110, 0x1111, 0x1111, 0x1112, 0x1120, 0x1121, 0x1121, 0x1122,
614
0x1200, 0x1201, 0x1201, 0x1202, 0x1210, 0x1211, 0x1211, 0x1212,
615
0x1210, 0x1211, 0x1211, 0x1212, 0x1220, 0x1221, 0x1221, 0x1222,
616
0x2000, 0x2001, 0x2001, 0x2002, 0x2010, 0x2011, 0x2011, 0x2012,
617
0x2010, 0x2011, 0x2011, 0x2012, 0x2020, 0x2021, 0x2021, 0x2022,
618
0x2100, 0x2101, 0x2101, 0x2102, 0x2110, 0x2111, 0x2111, 0x2112,
619
0x2110, 0x2111, 0x2111, 0x2112, 0x2120, 0x2121, 0x2121, 0x2122,
620
0x2100, 0x2101, 0x2101, 0x2102, 0x2110, 0x2111, 0x2111, 0x2112,
621
0x2110, 0x2111, 0x2111, 0x2112, 0x2120, 0x2121, 0x2121, 0x2122,
622
0x2200, 0x2201, 0x2201, 0x2202, 0x2210, 0x2211, 0x2211, 0x2212,
623
0x2210, 0x2211, 0x2211, 0x2212, 0x2220, 0x2221, 0x2221, 0x2222
626
#endif /* FT_RASTER_OPTION_ANTI_ALIASING */
630
/*************************************************************************/
631
/*************************************************************************/
633
/** PROFILES COMPUTATION **/
635
/*************************************************************************/
636
/*************************************************************************/
639
/*************************************************************************/
642
/* Set_High_Precision */
645
/* Set precision variables according to param flag. */
648
/* High :: Set to True for high precision (typically for ppem < 18), */
649
/* false otherwise. */
652
Set_High_Precision( RAS_ARGS Int High )
656
ras.precision_bits = 12;
657
ras.precision_step = 256;
658
ras.precision_jitter = 50;
662
ras.precision_bits = 6;
663
ras.precision_step = 32;
664
ras.precision_jitter = 2;
667
FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" ));
669
ras.precision = 1 << ras.precision_bits;
670
ras.precision_half = ras.precision / 2;
671
ras.precision_shift = ras.precision_bits - Pixel_Bits;
675
/*************************************************************************/
681
/* Create a new profile in the render pool. */
684
/* aState :: The state/orientation of the new profile. */
686
/* overshoot :: Whether the profile's unrounded start position */
687
/* differs by at least a half pixel. */
690
/* SUCCESS on success. FAILURE in case of overflow or of incoherent */
694
New_Profile( RAS_ARGS TStates aState,
699
ras.cProfile = (PProfile)ras.top;
700
ras.fProfile = ras.cProfile;
701
ras.top += AlignProfileSize;
704
if ( ras.top >= ras.maxBuff )
706
ras.error = Raster_Err_Overflow;
710
ras.cProfile->flags = 0;
711
ras.cProfile->start = 0;
712
ras.cProfile->height = 0;
713
ras.cProfile->offset = ras.top;
714
ras.cProfile->link = (PProfile)0;
715
ras.cProfile->next = (PProfile)0;
716
ras.cProfile->flags = ras.dropOutControl;
720
case Ascending_State:
721
ras.cProfile->flags |= Flow_Up;
723
ras.cProfile->flags |= Overshoot_Bottom;
725
FT_TRACE6(( "New ascending profile = %p\n", ras.cProfile ));
728
case Descending_State:
730
ras.cProfile->flags |= Overshoot_Top;
731
FT_TRACE6(( "New descending profile = %p\n", ras.cProfile ));
735
FT_ERROR(( "New_Profile: invalid profile direction\n" ));
736
ras.error = Raster_Err_Invalid;
741
ras.gProfile = ras.cProfile;
751
/*************************************************************************/
757
/* Finalize the current profile. */
760
/* overshoot :: Whether the profile's unrounded end position differs */
761
/* by at least a half pixel. */
764
/* SUCCESS on success. FAILURE in case of overflow or incoherency. */
767
End_Profile( RAS_ARGS Bool overshoot )
773
h = (Long)( ras.top - ras.cProfile->offset );
777
FT_ERROR(( "End_Profile: negative height encountered\n" ));
778
ras.error = Raster_Err_Neg_Height;
784
FT_TRACE6(( "Ending profile %p, start = %ld, height = %ld\n",
785
ras.cProfile, ras.cProfile->start, h ));
787
ras.cProfile->height = h;
790
if ( ras.cProfile->flags & Flow_Up )
791
ras.cProfile->flags |= Overshoot_Top;
793
ras.cProfile->flags |= Overshoot_Bottom;
796
oldProfile = ras.cProfile;
797
ras.cProfile = (PProfile)ras.top;
799
ras.top += AlignProfileSize;
801
ras.cProfile->height = 0;
802
ras.cProfile->offset = ras.top;
804
oldProfile->next = ras.cProfile;
808
if ( ras.top >= ras.maxBuff )
810
FT_TRACE1(( "overflow in End_Profile\n" ));
811
ras.error = Raster_Err_Overflow;
821
/*************************************************************************/
827
/* Insert a salient into the sorted list placed on top of the render */
831
/* New y scanline position. */
834
/* SUCCESS on success. FAILURE in case of overflow. */
837
Insert_Y_Turn( RAS_ARGS Int y )
843
n = ras.numTurns - 1;
844
y_turns = ras.sizeBuff - ras.numTurns;
846
/* look for first y value that is <= */
847
while ( n >= 0 && y < y_turns[n] )
850
/* if it is <, simply insert it, ignore if == */
851
if ( n >= 0 && y > y_turns[n] )
854
y2 = (Int)y_turns[n];
863
if ( ras.maxBuff <= ras.top )
865
ras.error = Raster_Err_Overflow;
869
ras.sizeBuff[-ras.numTurns] = y;
876
/*************************************************************************/
879
/* Finalize_Profile_Table */
882
/* Adjust all links in the profiles list. */
885
/* SUCCESS on success. FAILURE in case of overflow. */
888
Finalize_Profile_Table( RAS_ARG )
903
p->link = (PProfile)( p->offset + p->height );
907
if ( p->flags & Flow_Up )
909
bottom = (Int)p->start;
910
top = (Int)( p->start + p->height - 1 );
914
bottom = (Int)( p->start - p->height + 1 );
917
p->offset += p->height - 1;
920
if ( Insert_Y_Turn( RAS_VARS bottom ) ||
921
Insert_Y_Turn( RAS_VARS top + 1 ) )
935
/*************************************************************************/
941
/* Subdivide one conic Bezier into two joint sub-arcs in the Bezier */
945
/* None (subdivided Bezier is taken from the top of the stack). */
948
/* This routine is the `beef' of this component. It is _the_ inner */
949
/* loop that should be optimized to hell to get the best performance. */
952
Split_Conic( TPoint* base )
957
base[4].x = base[2].x;
959
a = base[3].x = ( base[2].x + b ) / 2;
960
b = base[1].x = ( base[0].x + b ) / 2;
961
base[2].x = ( a + b ) / 2;
963
base[4].y = base[2].y;
965
a = base[3].y = ( base[2].y + b ) / 2;
966
b = base[1].y = ( base[0].y + b ) / 2;
967
base[2].y = ( a + b ) / 2;
969
/* hand optimized. gcc doesn't seem to be too good at common */
970
/* expression substitution and instruction scheduling ;-) */
974
/*************************************************************************/
980
/* Subdivide a third-order Bezier arc into two joint sub-arcs in the */
984
/* This routine is the `beef' of the component. It is one of _the_ */
985
/* inner loops that should be optimized like hell to get the best */
989
Split_Cubic( TPoint* base )
994
base[6].x = base[3].x;
997
base[1].x = a = ( base[0].x + c + 1 ) >> 1;
998
base[5].x = b = ( base[3].x + d + 1 ) >> 1;
999
c = ( c + d + 1 ) >> 1;
1000
base[2].x = a = ( a + c + 1 ) >> 1;
1001
base[4].x = b = ( b + c + 1 ) >> 1;
1002
base[3].x = ( a + b + 1 ) >> 1;
1004
base[6].y = base[3].y;
1007
base[1].y = a = ( base[0].y + c + 1 ) >> 1;
1008
base[5].y = b = ( base[3].y + d + 1 ) >> 1;
1009
c = ( c + d + 1 ) >> 1;
1010
base[2].y = a = ( a + c + 1 ) >> 1;
1011
base[4].y = b = ( b + c + 1 ) >> 1;
1012
base[3].y = ( a + b + 1 ) >> 1;
1016
/*************************************************************************/
1022
/* Compute the x-coordinates of an ascending line segment and store */
1023
/* them in the render pool. */
1026
/* x1 :: The x-coordinate of the segment's start point. */
1028
/* y1 :: The y-coordinate of the segment's start point. */
1030
/* x2 :: The x-coordinate of the segment's end point. */
1032
/* y2 :: The y-coordinate of the segment's end point. */
1034
/* miny :: A lower vertical clipping bound value. */
1036
/* maxy :: An upper vertical clipping bound value. */
1039
/* SUCCESS on success, FAILURE on render pool overflow. */
1042
Line_Up( RAS_ARGS Long x1,
1050
Int e1, e2, f1, f2, size; /* XXX: is `Short' sufficient? */
1059
if ( Dy <= 0 || y2 < miny || y1 > maxy )
1064
/* Take care: miny-y1 can be a very large value; we use */
1065
/* a slow MulDiv function to avoid clipping bugs */
1066
x1 += SMulDiv( Dx, miny - y1, Dy );
1067
e1 = (Int)TRUNC( miny );
1072
e1 = (Int)TRUNC( y1 );
1073
f1 = (Int)FRAC( y1 );
1078
/* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */
1079
e2 = (Int)TRUNC( maxy );
1084
e2 = (Int)TRUNC( y2 );
1085
f2 = (Int)FRAC( y2 );
1094
x1 += SMulDiv( Dx, ras.precision - f1, Dy );
1105
ras.joint = (char)( f2 == 0 );
1109
ras.cProfile->start = e1;
1114
if ( ras.top + size >= ras.maxBuff )
1116
ras.error = Raster_Err_Overflow;
1122
Ix = SMulDiv( ras.precision, Dx, Dy);
1123
Rx = ( ras.precision * Dx ) % Dy;
1128
Ix = SMulDiv( ras.precision, -Dx, Dy) * -1;
1129
Rx = ( ras.precision * -Dx ) % Dy;
1155
/*************************************************************************/
1161
/* Compute the x-coordinates of an descending line segment and store */
1162
/* them in the render pool. */
1165
/* x1 :: The x-coordinate of the segment's start point. */
1167
/* y1 :: The y-coordinate of the segment's start point. */
1169
/* x2 :: The x-coordinate of the segment's end point. */
1171
/* y2 :: The y-coordinate of the segment's end point. */
1173
/* miny :: A lower vertical clipping bound value. */
1175
/* maxy :: An upper vertical clipping bound value. */
1178
/* SUCCESS on success, FAILURE on render pool overflow. */
1181
Line_Down( RAS_ARGS Long x1,
1193
result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );
1195
if ( fresh && !ras.fresh )
1196
ras.cProfile->start = -ras.cProfile->start;
1202
/* A function type describing the functions used to split Bezier arcs */
1203
typedef void (*TSplitter)( TPoint* base );
1206
/*************************************************************************/
1212
/* Compute the x-coordinates of an ascending Bezier arc and store */
1213
/* them in the render pool. */
1216
/* degree :: The degree of the Bezier arc (either 2 or 3). */
1218
/* splitter :: The function to split Bezier arcs. */
1220
/* miny :: A lower vertical clipping bound value. */
1222
/* maxy :: An upper vertical clipping bound value. */
1225
/* SUCCESS on success, FAILURE on render pool overflow. */
1228
Bezier_Up( RAS_ARGS Int degree,
1233
Long y1, y2, e, e2, e0;
1247
if ( y2 < miny || y1 > maxy )
1262
f1 = (Short)( FRAC( y1 ) );
1273
*top++ = arc[degree].x;
1281
ras.cProfile->start = TRUNC( e0 );
1288
if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff )
1291
ras.error = Raster_Err_Overflow;
1297
while ( arc >= start_arc && e <= e2 )
1306
if ( y2 - y1 >= ras.precision_step )
1313
*top++ = arc[degree].x + FMulDiv( arc[0].x - arc[degree].x,
1339
/*************************************************************************/
1345
/* Compute the x-coordinates of an descending Bezier arc and store */
1346
/* them in the render pool. */
1349
/* degree :: The degree of the Bezier arc (either 2 or 3). */
1351
/* splitter :: The function to split Bezier arcs. */
1353
/* miny :: A lower vertical clipping bound value. */
1355
/* maxy :: An upper vertical clipping bound value. */
1358
/* SUCCESS on success, FAILURE on render pool overflow. */
1361
Bezier_Down( RAS_ARGS Int degree,
1366
TPoint* arc = ras.arc;
1370
arc[0].y = -arc[0].y;
1371
arc[1].y = -arc[1].y;
1372
arc[2].y = -arc[2].y;
1374
arc[3].y = -arc[3].y;
1378
result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny );
1380
if ( fresh && !ras.fresh )
1381
ras.cProfile->start = -ras.cProfile->start;
1383
arc[0].y = -arc[0].y;
1388
/*************************************************************************/
1394
/* Inject a new line segment and adjust the Profiles list. */
1397
/* x :: The x-coordinate of the segment's end point (its start point */
1398
/* is stored in `lastX'). */
1400
/* y :: The y-coordinate of the segment's end point (its start point */
1401
/* is stored in `lastY'). */
1404
/* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1408
Line_To( RAS_ARGS Long x,
1411
/* First, detect a change of direction */
1413
switch ( ras.state )
1416
if ( y > ras.lastY )
1418
if ( New_Profile( RAS_VARS Ascending_State,
1419
IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
1424
if ( y < ras.lastY )
1425
if ( New_Profile( RAS_VARS Descending_State,
1426
IS_TOP_OVERSHOOT( ras.lastY ) ) )
1431
case Ascending_State:
1432
if ( y < ras.lastY )
1434
if ( End_Profile( RAS_VARS IS_TOP_OVERSHOOT( ras.lastY ) ) ||
1435
New_Profile( RAS_VARS Descending_State,
1436
IS_TOP_OVERSHOOT( ras.lastY ) ) )
1441
case Descending_State:
1442
if ( y > ras.lastY )
1444
if ( End_Profile( RAS_VARS IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ||
1445
New_Profile( RAS_VARS Ascending_State,
1446
IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
1455
/* Then compute the lines */
1457
switch ( ras.state )
1459
case Ascending_State:
1460
if ( Line_Up( RAS_VARS ras.lastX, ras.lastY,
1461
x, y, ras.minY, ras.maxY ) )
1465
case Descending_State:
1466
if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
1467
x, y, ras.minY, ras.maxY ) )
1482
/*************************************************************************/
1488
/* Inject a new conic arc and adjust the profile list. */
1491
/* cx :: The x-coordinate of the arc's new control point. */
1493
/* cy :: The y-coordinate of the arc's new control point. */
1495
/* x :: The x-coordinate of the arc's end point (its start point is */
1496
/* stored in `lastX'). */
1498
/* y :: The y-coordinate of the arc's end point (its start point is */
1499
/* stored in `lastY'). */
1502
/* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1506
Conic_To( RAS_ARGS Long cx,
1511
Long y1, y2, y3, x3, ymin, ymax;
1516
ras.arc[2].x = ras.lastX;
1517
ras.arc[2].y = ras.lastY;
1530
/* first, categorize the Bezier arc */
1543
if ( y2 < ymin || y2 > ymax )
1545
/* this arc has no given direction, split it! */
1546
Split_Conic( ras.arc );
1549
else if ( y1 == y3 )
1551
/* this arc is flat, ignore it and pop it from the Bezier stack */
1556
/* the arc is y-monotonous, either ascending or descending */
1557
/* detect a change of direction */
1558
state_bez = y1 < y3 ? Ascending_State : Descending_State;
1559
if ( ras.state != state_bez )
1561
Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 )
1562
: IS_TOP_OVERSHOOT( y1 );
1565
/* finalize current profile if any */
1566
if ( ras.state != Unknown_State &&
1567
End_Profile( RAS_VARS o ) )
1570
/* create a new profile */
1571
if ( New_Profile( RAS_VARS state_bez, o ) )
1575
/* now call the appropriate routine */
1576
if ( state_bez == Ascending_State )
1578
if ( Bezier_Up( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
1582
if ( Bezier_Down( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
1586
} while ( ras.arc >= ras.arcs );
1598
/*************************************************************************/
1604
/* Inject a new cubic arc and adjust the profile list. */
1607
/* cx1 :: The x-coordinate of the arc's first new control point. */
1609
/* cy1 :: The y-coordinate of the arc's first new control point. */
1611
/* cx2 :: The x-coordinate of the arc's second new control point. */
1613
/* cy2 :: The y-coordinate of the arc's second new control point. */
1615
/* x :: The x-coordinate of the arc's end point (its start point is */
1616
/* stored in `lastX'). */
1618
/* y :: The y-coordinate of the arc's end point (its start point is */
1619
/* stored in `lastY'). */
1622
/* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1626
Cubic_To( RAS_ARGS Long cx1,
1633
Long y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
1638
ras.arc[3].x = ras.lastX;
1639
ras.arc[3].y = ras.lastY;
1655
/* first, categorize the Bezier arc */
1679
if ( ymin2 < ymin1 || ymax2 > ymax1 )
1681
/* this arc has no given direction, split it! */
1682
Split_Cubic( ras.arc );
1685
else if ( y1 == y4 )
1687
/* this arc is flat, ignore it and pop it from the Bezier stack */
1692
state_bez = ( y1 <= y4 ) ? Ascending_State : Descending_State;
1694
/* detect a change of direction */
1695
if ( ras.state != state_bez )
1697
Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 )
1698
: IS_TOP_OVERSHOOT( y1 );
1701
/* finalize current profile if any */
1702
if ( ras.state != Unknown_State &&
1703
End_Profile( RAS_VARS o ) )
1706
if ( New_Profile( RAS_VARS state_bez, o ) )
1710
/* compute intersections */
1711
if ( state_bez == Ascending_State )
1713
if ( Bezier_Up( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
1717
if ( Bezier_Down( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
1721
} while ( ras.arc >= ras.arcs );
1734
#define SWAP_( x, y ) do \
1744
/*************************************************************************/
1747
/* Decompose_Curve */
1750
/* Scan the outline arrays in order to emit individual segments and */
1751
/* Beziers by calling Line_To() and Bezier_To(). It handles all */
1752
/* weird cases, like when the first point is off the curve, or when */
1753
/* there are simply no `on' points in the contour! */
1756
/* first :: The index of the first point in the contour. */
1758
/* last :: The index of the last point in the contour. */
1760
/* flipped :: If set, flip the direction of the curve. */
1763
/* SUCCESS on success, FAILURE on error. */
1766
Decompose_Curve( RAS_ARGS UShort first,
1771
FT_Vector v_control;
1779
unsigned tag; /* current point's state */
1782
points = ras.outline.points;
1783
limit = points + last;
1785
v_start.x = SCALED( points[first].x );
1786
v_start.y = SCALED( points[first].y );
1787
v_last.x = SCALED( points[last].x );
1788
v_last.y = SCALED( points[last].y );
1792
SWAP_( v_start.x, v_start.y );
1793
SWAP_( v_last.x, v_last.y );
1796
v_control = v_start;
1798
point = points + first;
1799
tags = ras.outline.tags + first;
1801
/* set scan mode if necessary */
1802
if ( tags[0] & FT_CURVE_TAG_HAS_SCANMODE )
1803
ras.dropOutControl = (Byte)tags[0] >> 5;
1805
tag = FT_CURVE_TAG( tags[0] );
1807
/* A contour cannot start with a cubic control point! */
1808
if ( tag == FT_CURVE_TAG_CUBIC )
1809
goto Invalid_Outline;
1811
/* check first point to determine origin */
1812
if ( tag == FT_CURVE_TAG_CONIC )
1814
/* first point is conic control. Yes, this happens. */
1815
if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_CURVE_TAG_ON )
1817
/* start at last point if it is on the curve */
1823
/* if both first and last points are conic, */
1824
/* start at their middle and record its position */
1826
v_start.x = ( v_start.x + v_last.x ) / 2;
1827
v_start.y = ( v_start.y + v_last.y ) / 2;
1835
ras.lastX = v_start.x;
1836
ras.lastY = v_start.y;
1838
while ( point < limit )
1843
tag = FT_CURVE_TAG( tags[0] );
1847
case FT_CURVE_TAG_ON: /* emit a single line_to */
1852
x = SCALED( point->x );
1853
y = SCALED( point->y );
1857
if ( Line_To( RAS_VARS x, y ) )
1862
case FT_CURVE_TAG_CONIC: /* consume conic arcs */
1863
v_control.x = SCALED( point[0].x );
1864
v_control.y = SCALED( point[0].y );
1867
SWAP_( v_control.x, v_control.y );
1870
if ( point < limit )
1878
tag = FT_CURVE_TAG( tags[0] );
1880
x = SCALED( point[0].x );
1881
y = SCALED( point[0].y );
1886
if ( tag == FT_CURVE_TAG_ON )
1888
if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) )
1893
if ( tag != FT_CURVE_TAG_CONIC )
1894
goto Invalid_Outline;
1896
v_middle.x = ( v_control.x + x ) / 2;
1897
v_middle.y = ( v_control.y + y ) / 2;
1899
if ( Conic_To( RAS_VARS v_control.x, v_control.y,
1900
v_middle.x, v_middle.y ) )
1909
if ( Conic_To( RAS_VARS v_control.x, v_control.y,
1910
v_start.x, v_start.y ) )
1915
default: /* FT_CURVE_TAG_CUBIC */
1917
Long x1, y1, x2, y2, x3, y3;
1920
if ( point + 1 > limit ||
1921
FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
1922
goto Invalid_Outline;
1927
x1 = SCALED( point[-2].x );
1928
y1 = SCALED( point[-2].y );
1929
x2 = SCALED( point[-1].x );
1930
y2 = SCALED( point[-1].y );
1938
if ( point <= limit )
1940
x3 = SCALED( point[0].x );
1941
y3 = SCALED( point[0].y );
1946
if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) )
1951
if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) )
1958
/* close the contour with a line segment */
1959
if ( Line_To( RAS_VARS v_start.x, v_start.y ) )
1966
ras.error = Raster_Err_Invalid;
1973
/*************************************************************************/
1979
/* Convert a glyph into a series of segments and arcs and make a */
1980
/* profiles list with them. */
1983
/* flipped :: If set, flip the direction of curve. */
1986
/* SUCCESS on success, FAILURE if any error was encountered during */
1990
Convert_Glyph( RAS_ARGS int flipped )
1995
PProfile lastProfile;
1998
ras.fProfile = NULL;
2002
ras.maxBuff = ras.sizeBuff - AlignProfileSize;
2006
ras.cProfile = (PProfile)ras.top;
2007
ras.cProfile->offset = ras.top;
2012
for ( i = 0; i < ras.outline.n_contours; i++ )
2017
ras.state = Unknown_State;
2018
ras.gProfile = NULL;
2020
if ( Decompose_Curve( RAS_VARS (unsigned short)start,
2021
ras.outline.contours[i],
2025
start = ras.outline.contours[i] + 1;
2027
/* we must now check whether the extreme arcs join or not */
2028
if ( FRAC( ras.lastY ) == 0 &&
2029
ras.lastY >= ras.minY &&
2030
ras.lastY <= ras.maxY )
2031
if ( ras.gProfile &&
2032
( ras.gProfile->flags & Flow_Up ) ==
2033
( ras.cProfile->flags & Flow_Up ) )
2035
/* Note that ras.gProfile can be nil if the contour was too small */
2038
lastProfile = ras.cProfile;
2039
if ( ras.cProfile->flags & Flow_Up )
2040
o = IS_TOP_OVERSHOOT( ras.lastY );
2042
o = IS_BOTTOM_OVERSHOOT( ras.lastY );
2043
if ( End_Profile( RAS_VARS o ) )
2046
/* close the `next profile in contour' linked list */
2048
lastProfile->next = ras.gProfile;
2051
if ( Finalize_Profile_Table( RAS_VAR ) )
2054
return (Bool)( ras.top < ras.maxBuff ? SUCCESS : FAILURE );
2058
/*************************************************************************/
2059
/*************************************************************************/
2061
/** SCAN-LINE SWEEPS AND DRAWING **/
2063
/*************************************************************************/
2064
/*************************************************************************/
2067
/*************************************************************************/
2071
/* Initializes an empty linked list. */
2074
Init_Linked( TProfileList* l )
2080
/*************************************************************************/
2084
/* Inserts a new profile in a linked list. */
2087
InsNew( PProfileList list,
2090
PProfile *old, current;
2100
if ( x < current->X )
2102
old = ¤t->link;
2106
profile->link = current;
2111
/*************************************************************************/
2115
/* Removes an old profile from a linked list. */
2118
DelOld( PProfileList list,
2121
PProfile *old, current;
2129
if ( current == profile )
2131
*old = current->link;
2135
old = ¤t->link;
2139
/* we should never get there, unless the profile was not part of */
2144
/*************************************************************************/
2148
/* Sorts a trace list. In 95%, the list is already sorted. We need */
2149
/* an algorithm which is fast in this case. Bubble sort is enough */
2153
Sort( PProfileList list )
2155
PProfile *old, current, next;
2158
/* First, set the new X coordinate of each profile */
2162
current->X = *current->offset;
2163
current->offset += current->flags & Flow_Up ? 1 : -1;
2165
current = current->link;
2168
/* Then sort them */
2175
next = current->link;
2179
if ( current->X <= next->X )
2181
old = ¤t->link;
2190
current->link = next->link;
2191
next->link = current;
2197
next = current->link;
2202
/*************************************************************************/
2204
/* Vertical Sweep Procedure Set */
2206
/* These four routines are used during the vertical black/white sweep */
2207
/* phase by the generic Draw_Sweep() function. */
2209
/*************************************************************************/
2212
Vertical_Sweep_Init( RAS_ARGS Short* min,
2215
Long pitch = ras.target.pitch;
2220
ras.traceIncr = (Short)-pitch;
2221
ras.traceOfs = -*min * pitch;
2223
ras.traceOfs += ( ras.target.rows - 1 ) * pitch;
2231
Vertical_Sweep_Span( RAS_ARGS Short y,
2247
/* Drop-out control */
2249
e1 = TRUNC( CEILING( x1 ) );
2251
if ( x2 - x1 - ras.precision <= ras.precision_jitter )
2254
e2 = TRUNC( FLOOR( x2 ) );
2256
if ( e2 >= 0 && e1 < ras.bWidth )
2260
if ( e2 >= ras.bWidth )
2261
e2 = ras.bWidth - 1;
2263
c1 = (Short)( e1 >> 3 );
2264
c2 = (Short)( e2 >> 3 );
2266
f1 = (Byte) ( 0xFF >> ( e1 & 7 ) );
2267
f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) );
2269
if ( ras.gray_min_x > c1 )
2270
ras.gray_min_x = (short)c1;
2271
if ( ras.gray_max_x < c2 )
2272
ras.gray_max_x = (short)c2;
2274
target = ras.bTarget + ras.traceOfs + c1;
2281
/* memset() is slower than the following code on many platforms. */
2282
/* This is due to the fact that, in the vast majority of cases, */
2283
/* the span length in bytes is relatively small. */
2293
*target |= ( f1 & f2 );
2299
Vertical_Sweep_Drop( RAS_ARGS Short y,
2309
/* Drop-out control */
2315
/* +-------------+---------------------+------------+ */
2319
/* pixel contour contour pixel */
2322
/* drop-out mode scan conversion rules (as defined in OpenType) */
2323
/* --------------------------------------------------------------- */
2327
/* 3 same as mode 2 */
2330
/* 6, 7 same as mode 2 */
2338
Int dropOutControl = left->flags & 7;
2341
if ( e1 == e2 + ras.precision )
2343
switch ( dropOutControl )
2345
case 0: /* simple drop-outs including stubs */
2349
case 4: /* smart drop-outs including stubs */
2350
pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2353
case 1: /* simple drop-outs excluding stubs */
2354
case 5: /* smart drop-outs excluding stubs */
2356
/* Drop-out Control Rules #4 and #6 */
2358
/* The specification neither provides an exact definition */
2359
/* of a `stub' nor gives exact rules to exclude them. */
2361
/* Here the constraints we use to recognize a stub. */
2365
/* - P_Left and P_Right are in the same contour */
2366
/* - P_Right is the successor of P_Left in that contour */
2367
/* - y is the top of P_Left and P_Right */
2371
/* - P_Left and P_Right are in the same contour */
2372
/* - P_Left is the successor of P_Right in that contour */
2373
/* - y is the bottom of P_Left */
2375
/* We draw a stub if the following constraints are met. */
2377
/* - for an upper or lower stub, there is top or bottom */
2378
/* overshoot, respectively */
2379
/* - the covered interval is greater or equal to a half */
2382
/* upper stub test */
2383
if ( left->next == right &&
2384
left->height <= 0 &&
2385
!( left->flags & Overshoot_Top &&
2386
x2 - x1 >= ras.precision_half ) )
2389
/* lower stub test */
2390
if ( right->next == left &&
2392
!( left->flags & Overshoot_Bottom &&
2393
x2 - x1 >= ras.precision_half ) )
2396
if ( dropOutControl == 1 )
2399
pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2402
default: /* modes 2, 3, 6, 7 */
2403
return; /* no drop-out control */
2406
/* check that the other pixel isn't set */
2407
e1 = pxl == e1 ? e2 : e1;
2411
c1 = (Short)( e1 >> 3 );
2412
f1 = (Short)( e1 & 7 );
2414
if ( e1 >= 0 && e1 < ras.bWidth &&
2415
ras.bTarget[ras.traceOfs + c1] & ( 0x80 >> f1 ) )
2424
if ( e1 >= 0 && e1 < ras.bWidth )
2426
c1 = (Short)( e1 >> 3 );
2427
f1 = (Short)( e1 & 7 );
2429
if ( ras.gray_min_x > c1 )
2430
ras.gray_min_x = c1;
2431
if ( ras.gray_max_x < c1 )
2432
ras.gray_max_x = c1;
2434
ras.bTarget[ras.traceOfs + c1] |= (char)( 0x80 >> f1 );
2440
Vertical_Sweep_Step( RAS_ARG )
2442
ras.traceOfs += ras.traceIncr;
2446
/***********************************************************************/
2448
/* Horizontal Sweep Procedure Set */
2450
/* These four routines are used during the horizontal black/white */
2451
/* sweep phase by the generic Draw_Sweep() function. */
2453
/***********************************************************************/
2456
Horizontal_Sweep_Init( RAS_ARGS Short* min,
2459
/* nothing, really */
2467
Horizontal_Sweep_Span( RAS_ARGS Short y,
2481
if ( x2 - x1 < ras.precision )
2488
bits = ras.bTarget + ( y >> 3 );
2489
f1 = (Byte)( 0x80 >> ( y & 7 ) );
2493
if ( e1 >= 0 && e1 < ras.target.rows )
2498
p = bits - e1 * ras.target.pitch;
2499
if ( ras.target.pitch > 0 )
2500
p += ( ras.target.rows - 1 ) * ras.target.pitch;
2510
Horizontal_Sweep_Drop( RAS_ARGS Short y,
2521
/* During the horizontal sweep, we only take care of drop-outs */
2523
/* e1 + <-- pixel center */
2525
/* x1 ---+--> <-- contour */
2528
/* x2 <--+--- <-- contour */
2531
/* e2 + <-- pixel center */
2539
Int dropOutControl = left->flags & 7;
2542
if ( e1 == e2 + ras.precision )
2544
switch ( dropOutControl )
2546
case 0: /* simple drop-outs including stubs */
2550
case 4: /* smart drop-outs including stubs */
2551
pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2554
case 1: /* simple drop-outs excluding stubs */
2555
case 5: /* smart drop-outs excluding stubs */
2556
/* see Vertical_Sweep_Drop for details */
2558
/* rightmost stub test */
2559
if ( left->next == right &&
2560
left->height <= 0 &&
2561
!( left->flags & Overshoot_Top &&
2562
x2 - x1 >= ras.precision_half ) )
2565
/* leftmost stub test */
2566
if ( right->next == left &&
2568
!( left->flags & Overshoot_Bottom &&
2569
x2 - x1 >= ras.precision_half ) )
2572
if ( dropOutControl == 1 )
2575
pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2578
default: /* modes 2, 3, 6, 7 */
2579
return; /* no drop-out control */
2582
/* check that the other pixel isn't set */
2583
e1 = pxl == e1 ? e2 : e1;
2587
bits = ras.bTarget + ( y >> 3 );
2588
f1 = (Byte)( 0x80 >> ( y & 7 ) );
2590
bits -= e1 * ras.target.pitch;
2591
if ( ras.target.pitch > 0 )
2592
bits += ( ras.target.rows - 1 ) * ras.target.pitch;
2595
e1 < ras.target.rows &&
2603
bits = ras.bTarget + ( y >> 3 );
2604
f1 = (Byte)( 0x80 >> ( y & 7 ) );
2608
if ( e1 >= 0 && e1 < ras.target.rows )
2610
bits -= e1 * ras.target.pitch;
2611
if ( ras.target.pitch > 0 )
2612
bits += ( ras.target.rows - 1 ) * ras.target.pitch;
2620
Horizontal_Sweep_Step( RAS_ARG )
2622
/* Nothing, really */
2627
#ifdef FT_RASTER_OPTION_ANTI_ALIASING
2630
/*************************************************************************/
2632
/* Vertical Gray Sweep Procedure Set */
2634
/* These two routines are used during the vertical gray-levels sweep */
2635
/* phase by the generic Draw_Sweep() function. */
2639
/* - The target pixmap's width *must* be a multiple of 4. */
2641
/* - You have to use the function Vertical_Sweep_Span() for the gray */
2644
/*************************************************************************/
2647
Vertical_Gray_Sweep_Init( RAS_ARGS Short* min,
2650
Long pitch, byte_len;
2654
*max = ( *max + 3 ) & -2;
2657
pitch = ras.target.pitch;
2659
ras.traceIncr = (Short)byte_len;
2660
ras.traceG = ( *min / 2 ) * byte_len;
2664
ras.traceG += ( ras.target.rows - 1 ) * pitch;
2665
byte_len = -byte_len;
2668
ras.gray_min_x = (Short)byte_len;
2669
ras.gray_max_x = -(Short)byte_len;
2674
Vertical_Gray_Sweep_Step( RAS_ARG )
2677
PByte pix, bit, bit2;
2678
short* count = (short*)count_table;
2682
ras.traceOfs += ras.gray_width;
2684
if ( ras.traceOfs > ras.gray_width )
2686
pix = ras.gTarget + ras.traceG + ras.gray_min_x * 4;
2689
if ( ras.gray_max_x >= 0 )
2691
Long last_pixel = ras.target.width - 1;
2692
Int last_cell = last_pixel >> 2;
2693
Int last_bit = last_pixel & 3;
2697
if ( ras.gray_max_x >= last_cell && last_bit != 3 )
2699
ras.gray_max_x = last_cell - 1;
2703
if ( ras.gray_min_x < 0 )
2706
bit = ras.bTarget + ras.gray_min_x;
2707
bit2 = bit + ras.gray_width;
2709
c1 = ras.gray_max_x - ras.gray_min_x;
2713
c2 = count[*bit] + count[*bit2];
2717
pix[0] = grays[(c2 >> 12) & 0x000F];
2718
pix[1] = grays[(c2 >> 8 ) & 0x000F];
2719
pix[2] = grays[(c2 >> 4 ) & 0x000F];
2720
pix[3] = grays[ c2 & 0x000F];
2734
c2 = count[*bit] + count[*bit2];
2740
pix[2] = grays[(c2 >> 4 ) & 0x000F];
2742
pix[1] = grays[(c2 >> 8 ) & 0x000F];
2744
pix[0] = grays[(c2 >> 12) & 0x000F];
2754
ras.traceG += ras.traceIncr;
2756
ras.gray_min_x = 32000;
2757
ras.gray_max_x = -32000;
2763
Horizontal_Gray_Sweep_Span( RAS_ARGS Short y,
2769
/* nothing, really */
2780
Horizontal_Gray_Sweep_Drop( RAS_ARGS Short y,
2791
/* During the horizontal sweep, we only take care of drop-outs */
2798
Int dropOutControl = left->flags & 7;
2801
if ( e1 == e2 + ras.precision )
2803
switch ( dropOutControl )
2805
case 0: /* simple drop-outs including stubs */
2809
case 4: /* smart drop-outs including stubs */
2810
e1 = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2813
case 1: /* simple drop-outs excluding stubs */
2814
case 5: /* smart drop-outs excluding stubs */
2815
/* see Vertical_Sweep_Drop for details */
2817
/* rightmost stub test */
2818
if ( left->next == right && left->height <= 0 )
2821
/* leftmost stub test */
2822
if ( right->next == left && left->start == y )
2825
if ( dropOutControl == 1 )
2828
e1 = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2832
default: /* modes 2, 3, 6, 7 */
2833
return; /* no drop-out control */
2842
if ( x2 - x1 >= ras.precision_half )
2843
color = ras.grays[2];
2845
color = ras.grays[1];
2847
e1 = TRUNC( e1 ) / 2;
2848
if ( e1 < ras.target.rows )
2850
pixel = ras.gTarget - e1 * ras.target.pitch + y / 2;
2851
if ( ras.target.pitch > 0 )
2852
pixel += ( ras.target.rows - 1 ) * ras.target.pitch;
2854
if ( pixel[0] == ras.grays[0] )
2861
#endif /* FT_RASTER_OPTION_ANTI_ALIASING */
2864
/*************************************************************************/
2866
/* Generic Sweep Drawing routine */
2868
/*************************************************************************/
2871
Draw_Sweep( RAS_ARG )
2873
Short y, y_change, y_height;
2875
PProfile P, Q, P_Left, P_Right;
2877
Short min_Y, max_Y, top, bottom, dropouts;
2879
Long x1, x2, xs, e1, e2;
2881
TProfileList waiting;
2882
TProfileList draw_left, draw_right;
2885
/* initialize empty linked lists */
2887
Init_Linked( &waiting );
2889
Init_Linked( &draw_left );
2890
Init_Linked( &draw_right );
2892
/* first, compute min and max Y */
2895
max_Y = (Short)TRUNC( ras.minY );
2896
min_Y = (Short)TRUNC( ras.maxY );
2902
bottom = (Short)P->start;
2903
top = (Short)( P->start + P->height - 1 );
2905
if ( min_Y > bottom )
2911
InsNew( &waiting, P );
2916
/* check the Y-turns */
2917
if ( ras.numTurns == 0 )
2919
ras.error = Raster_Err_Invalid;
2923
/* now initialize the sweep */
2925
ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y );
2927
/* then compute the distance of each profile from min_Y */
2933
P->countL = (UShort)( P->start - min_Y );
2942
if ( ras.numTurns > 0 &&
2943
ras.sizeBuff[-ras.numTurns] == min_Y )
2946
while ( ras.numTurns > 0 )
2948
/* check waiting list for new activations */
2955
P->countL -= y_height;
2956
if ( P->countL == 0 )
2958
DelOld( &waiting, P );
2960
if ( P->flags & Flow_Up )
2961
InsNew( &draw_left, P );
2963
InsNew( &draw_right, P );
2969
/* sort the drawing lists */
2972
Sort( &draw_right );
2974
y_change = (Short)ras.sizeBuff[-ras.numTurns--];
2975
y_height = (Short)( y_change - y );
2977
while ( y < y_change )
2984
P_Right = draw_right;
3001
if ( x2 - x1 <= ras.precision &&
3002
e1 != x1 && e2 != x2 )
3004
if ( e1 > e2 || e2 == e1 + ras.precision )
3006
Int dropOutControl = P_Left->flags & 7;
3009
if ( dropOutControl != 2 )
3011
/* a drop-out was detected */
3016
/* mark profile for drop-out processing */
3025
ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right );
3029
P_Left = P_Left->link;
3030
P_Right = P_Right->link;
3033
/* handle drop-outs _after_ the span drawing -- */
3034
/* drop-out processing has been moved out of the loop */
3035
/* for performance tuning */
3041
ras.Proc_Sweep_Step( RAS_VAR );
3048
Sort( &draw_right );
3052
/* now finalize the profiles that need it */
3058
if ( P->height == 0 )
3059
DelOld( &draw_left, P );
3067
if ( P->height == 0 )
3068
DelOld( &draw_right, P );
3073
/* for gray-scaling, flush the bitmap scanline cache */
3074
while ( y <= max_Y )
3076
ras.Proc_Sweep_Step( RAS_VAR );
3085
P_Right = draw_right;
3089
if ( P_Left->countL )
3093
dropouts--; /* -- this is useful when debugging only */
3095
ras.Proc_Sweep_Drop( RAS_VARS y,
3102
P_Left = P_Left->link;
3103
P_Right = P_Right->link;
3110
/*************************************************************************/
3113
/* Render_Single_Pass */
3116
/* Perform one sweep with sub-banding. */
3119
/* flipped :: If set, flip the direction of the outline. */
3122
/* Renderer error code. */
3125
Render_Single_Pass( RAS_ARGS Bool flipped )
3130
while ( ras.band_top >= 0 )
3132
ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision;
3133
ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision;
3137
ras.error = Raster_Err_None;
3139
if ( Convert_Glyph( RAS_VARS flipped ) )
3141
if ( ras.error != Raster_Err_Overflow )
3144
ras.error = Raster_Err_None;
3149
ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) );
3152
i = ras.band_stack[ras.band_top].y_min;
3153
j = ras.band_stack[ras.band_top].y_max;
3155
k = (Short)( ( i + j ) / 2 );
3157
if ( ras.band_top >= 7 || k < i )
3160
ras.error = Raster_Err_Invalid;
3165
ras.band_stack[ras.band_top + 1].y_min = k;
3166
ras.band_stack[ras.band_top + 1].y_max = j;
3168
ras.band_stack[ras.band_top].y_max = (Short)( k - 1 );
3175
if ( Draw_Sweep( RAS_VAR ) )
3185
/*************************************************************************/
3191
/* Render a glyph in a bitmap. Sub-banding if needed. */
3194
/* FreeType error code. 0 means success. */
3196
FT_LOCAL_DEF( FT_Error )
3197
Render_Glyph( RAS_ARG )
3202
Set_High_Precision( RAS_VARS ras.outline.flags &
3203
FT_OUTLINE_HIGH_PRECISION );
3204
ras.scale_shift = ras.precision_shift;
3206
if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )
3207
ras.dropOutControl = 2;
3210
if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS )
3211
ras.dropOutControl = 4;
3213
ras.dropOutControl = 0;
3215
if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) )
3216
ras.dropOutControl += 1;
3219
ras.second_pass = (FT_Byte)( !( ras.outline.flags &
3220
FT_OUTLINE_SINGLE_PASS ) );
3222
/* Vertical Sweep */
3223
ras.Proc_Sweep_Init = Vertical_Sweep_Init;
3224
ras.Proc_Sweep_Span = Vertical_Sweep_Span;
3225
ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
3226
ras.Proc_Sweep_Step = Vertical_Sweep_Step;
3229
ras.band_stack[0].y_min = 0;
3230
ras.band_stack[0].y_max = (short)( ras.target.rows - 1 );
3232
ras.bWidth = (unsigned short)ras.target.width;
3233
ras.bTarget = (Byte*)ras.target.buffer;
3235
if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 )
3238
/* Horizontal Sweep */
3239
if ( ras.second_pass && ras.dropOutControl != 2 )
3241
ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
3242
ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
3243
ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;
3244
ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
3247
ras.band_stack[0].y_min = 0;
3248
ras.band_stack[0].y_max = (short)( ras.target.width - 1 );
3250
if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 )
3254
return Raster_Err_None;
3258
#ifdef FT_RASTER_OPTION_ANTI_ALIASING
3260
/*************************************************************************/
3263
/* Render_Gray_Glyph */
3266
/* Render a glyph with grayscaling. Sub-banding if needed. */
3269
/* FreeType error code. 0 means success. */
3271
FT_LOCAL_DEF( FT_Error )
3272
Render_Gray_Glyph( RAS_ARG )
3278
Set_High_Precision( RAS_VARS ras.outline.flags &
3279
FT_OUTLINE_HIGH_PRECISION );
3280
ras.scale_shift = ras.precision_shift + 1;
3282
if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )
3283
ras.dropOutControl = 2;
3286
if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS )
3287
ras.dropOutControl = 4;
3289
ras.dropOutControl = 0;
3291
if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) )
3292
ras.dropOutControl += 1;
3295
ras.second_pass = !( ras.outline.flags & FT_OUTLINE_SINGLE_PASS );
3297
/* Vertical Sweep */
3300
ras.band_stack[0].y_min = 0;
3301
ras.band_stack[0].y_max = 2 * ras.target.rows - 1;
3303
ras.bWidth = ras.gray_width;
3304
pixel_width = 2 * ( ( ras.target.width + 3 ) >> 2 );
3306
if ( ras.bWidth > pixel_width )
3307
ras.bWidth = pixel_width;
3309
ras.bWidth = ras.bWidth * 8;
3310
ras.bTarget = (Byte*)ras.gray_lines;
3311
ras.gTarget = (Byte*)ras.target.buffer;
3313
ras.Proc_Sweep_Init = Vertical_Gray_Sweep_Init;
3314
ras.Proc_Sweep_Span = Vertical_Sweep_Span;
3315
ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
3316
ras.Proc_Sweep_Step = Vertical_Gray_Sweep_Step;
3318
error = Render_Single_Pass( RAS_VARS 0 );
3322
/* Horizontal Sweep */
3323
if ( ras.second_pass && ras.dropOutControl != 2 )
3325
ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
3326
ras.Proc_Sweep_Span = Horizontal_Gray_Sweep_Span;
3327
ras.Proc_Sweep_Drop = Horizontal_Gray_Sweep_Drop;
3328
ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
3331
ras.band_stack[0].y_min = 0;
3332
ras.band_stack[0].y_max = ras.target.width * 2 - 1;
3334
error = Render_Single_Pass( RAS_VARS 1 );
3339
return Raster_Err_None;
3342
#else /* !FT_RASTER_OPTION_ANTI_ALIASING */
3344
FT_LOCAL_DEF( FT_Error )
3345
Render_Gray_Glyph( RAS_ARG )
3349
return Raster_Err_Unsupported;
3352
#endif /* !FT_RASTER_OPTION_ANTI_ALIASING */
3356
ft_black_init( PRaster raster )
3358
#ifdef FT_RASTER_OPTION_ANTI_ALIASING
3362
/* set default 5-levels gray palette */
3363
for ( n = 0; n < 5; n++ )
3364
raster->grays[n] = n * 255 / 4;
3366
raster->gray_width = RASTER_GRAY_LINES / 2;
3368
FT_UNUSED( raster );
3373
/**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
3374
/**** a static object. *****/
3381
ft_black_new( void* memory,
3382
FT_Raster *araster )
3384
static TRaster the_raster;
3385
FT_UNUSED( memory );
3388
*araster = (FT_Raster)&the_raster;
3389
FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) );
3390
ft_black_init( &the_raster );
3397
ft_black_done( FT_Raster raster )
3400
FT_UNUSED( raster );
3404
#else /* !_STANDALONE_ */
3408
ft_black_new( FT_Memory memory,
3412
PRaster raster = NULL;
3416
if ( !FT_NEW( raster ) )
3418
raster->memory = memory;
3419
ft_black_init( raster );
3429
ft_black_done( PRaster raster )
3431
FT_Memory memory = (FT_Memory)raster->memory;
3436
#endif /* !_STANDALONE_ */
3440
ft_black_reset( PRaster raster,
3446
if ( pool_base && pool_size >= (long)sizeof(TWorker) + 2048 )
3448
PWorker worker = (PWorker)pool_base;
3451
raster->buffer = pool_base + ( ( sizeof ( *worker ) + 7 ) & ~7 );
3452
raster->buffer_size = pool_base + pool_size - (char*)raster->buffer;
3453
raster->worker = worker;
3457
raster->buffer = NULL;
3458
raster->buffer_size = 0;
3459
raster->worker = NULL;
3465
static int /* XXX EMSCRIPTEN */
3466
ft_black_set_mode( PRaster raster,
3468
const char* palette )
3470
#ifdef FT_RASTER_OPTION_ANTI_ALIASING
3472
if ( mode == FT_MAKE_TAG( 'p', 'a', 'l', '5' ) )
3474
/* set 5-levels gray palette */
3475
raster->grays[0] = palette[0];
3476
raster->grays[1] = palette[1];
3477
raster->grays[2] = palette[2];
3478
raster->grays[3] = palette[3];
3479
raster->grays[4] = palette[4];
3484
FT_UNUSED( raster );
3486
FT_UNUSED( palette );
3489
return 0; /* XXX EMSCRIPTEN */
3494
ft_black_render( PRaster raster,
3495
const FT_Raster_Params* params )
3497
const FT_Outline* outline = (const FT_Outline*)params->source;
3498
const FT_Bitmap* target_map = params->target;
3502
if ( !raster || !raster->buffer || !raster->buffer_size )
3503
return Raster_Err_Not_Ini;
3506
return Raster_Err_Invalid;
3508
/* return immediately if the outline is empty */
3509
if ( outline->n_points == 0 || outline->n_contours <= 0 )
3510
return Raster_Err_None;
3512
if ( !outline->contours || !outline->points )
3513
return Raster_Err_Invalid;
3515
if ( outline->n_points !=
3516
outline->contours[outline->n_contours - 1] + 1 )
3517
return Raster_Err_Invalid;
3519
worker = raster->worker;
3521
/* this version of the raster does not support direct rendering, sorry */
3522
if ( params->flags & FT_RASTER_FLAG_DIRECT )
3523
return Raster_Err_Unsupported;
3526
return Raster_Err_Invalid;
3529
if ( !target_map->width || !target_map->rows )
3530
return Raster_Err_None;
3532
if ( !target_map->buffer )
3533
return Raster_Err_Invalid;
3535
ras.outline = *outline;
3536
ras.target = *target_map;
3538
worker->buff = (PLong) raster->buffer;
3539
worker->sizeBuff = worker->buff +
3540
raster->buffer_size / sizeof ( Long );
3541
#ifdef FT_RASTER_OPTION_ANTI_ALIASING
3542
worker->grays = raster->grays;
3543
worker->gray_width = raster->gray_width;
3545
FT_MEM_ZERO( worker->gray_lines, worker->gray_width * 2 );
3548
return ( params->flags & FT_RASTER_FLAG_AA )
3549
? Render_Gray_Glyph( RAS_VAR )
3550
: Render_Glyph( RAS_VAR );
3554
FT_DEFINE_RASTER_FUNCS( ft_standard_raster,
3555
FT_GLYPH_FORMAT_OUTLINE,
3556
(FT_Raster_New_Func) ft_black_new,
3557
(FT_Raster_Reset_Func) ft_black_reset,
3558
(FT_Raster_Set_Mode_Func)ft_black_set_mode,
3559
(FT_Raster_Render_Func) ft_black_render,
3560
(FT_Raster_Done_Func) ft_black_done