1
/***************************************************************************/
5
/* The FreeType glyph rasterizer (body). */
7
/* Copyright 1996-2001, 2002, 2003, 2005, 2007, 2008, 2009, 2010, 2011 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 )
655
* `precision_step' is used in `Bezier_Up' to decide when to split a
656
* given y-monotonous Bezier arc that crosses a scanline before
657
* approximating it as a straight segment. The default value of 32 (for
658
* low accuracy) corresponds to
660
* 32 / 64 == 0.5 pixels ,
662
* while for the high accuracy case we have
664
* 256/ (1 << 12) = 0.0625 pixels .
666
* `precision_jitter' is an epsilon threshold used in
667
* `Vertical_Sweep_Span' to deal with small imperfections in the Bezier
668
* decomposition (after all, we are working with approximations only);
669
* it avoids switching on additional pixels which would cause artifacts
672
* The value of `precision_jitter' has been determined heuristically.
678
ras.precision_bits = 12;
679
ras.precision_step = 256;
680
ras.precision_jitter = 30;
684
ras.precision_bits = 6;
685
ras.precision_step = 32;
686
ras.precision_jitter = 2;
689
FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" ));
691
ras.precision = 1 << ras.precision_bits;
692
ras.precision_half = ras.precision / 2;
693
ras.precision_shift = ras.precision_bits - Pixel_Bits;
697
/*************************************************************************/
703
/* Create a new profile in the render pool. */
706
/* aState :: The state/orientation of the new profile. */
708
/* overshoot :: Whether the profile's unrounded start position */
709
/* differs by at least a half pixel. */
712
/* SUCCESS on success. FAILURE in case of overflow or of incoherent */
716
New_Profile( RAS_ARGS TStates aState,
721
ras.cProfile = (PProfile)ras.top;
722
ras.fProfile = ras.cProfile;
723
ras.top += AlignProfileSize;
726
if ( ras.top >= ras.maxBuff )
728
ras.error = Raster_Err_Overflow;
732
ras.cProfile->flags = 0;
733
ras.cProfile->start = 0;
734
ras.cProfile->height = 0;
735
ras.cProfile->offset = ras.top;
736
ras.cProfile->link = (PProfile)0;
737
ras.cProfile->next = (PProfile)0;
738
ras.cProfile->flags = ras.dropOutControl;
742
case Ascending_State:
743
ras.cProfile->flags |= Flow_Up;
745
ras.cProfile->flags |= Overshoot_Bottom;
747
FT_TRACE6(( "New ascending profile = %p\n", ras.cProfile ));
750
case Descending_State:
752
ras.cProfile->flags |= Overshoot_Top;
753
FT_TRACE6(( "New descending profile = %p\n", ras.cProfile ));
757
FT_ERROR(( "New_Profile: invalid profile direction\n" ));
758
ras.error = Raster_Err_Invalid;
763
ras.gProfile = ras.cProfile;
773
/*************************************************************************/
779
/* Finalize the current profile. */
782
/* overshoot :: Whether the profile's unrounded end position differs */
783
/* by at least a half pixel. */
786
/* SUCCESS on success. FAILURE in case of overflow or incoherency. */
789
End_Profile( RAS_ARGS Bool overshoot )
795
h = (Long)( ras.top - ras.cProfile->offset );
799
FT_ERROR(( "End_Profile: negative height encountered\n" ));
800
ras.error = Raster_Err_Neg_Height;
806
FT_TRACE6(( "Ending profile %p, start = %ld, height = %ld\n",
807
ras.cProfile, ras.cProfile->start, h ));
809
ras.cProfile->height = h;
812
if ( ras.cProfile->flags & Flow_Up )
813
ras.cProfile->flags |= Overshoot_Top;
815
ras.cProfile->flags |= Overshoot_Bottom;
818
oldProfile = ras.cProfile;
819
ras.cProfile = (PProfile)ras.top;
821
ras.top += AlignProfileSize;
823
ras.cProfile->height = 0;
824
ras.cProfile->offset = ras.top;
826
oldProfile->next = ras.cProfile;
830
if ( ras.top >= ras.maxBuff )
832
FT_TRACE1(( "overflow in End_Profile\n" ));
833
ras.error = Raster_Err_Overflow;
843
/*************************************************************************/
849
/* Insert a salient into the sorted list placed on top of the render */
853
/* New y scanline position. */
856
/* SUCCESS on success. FAILURE in case of overflow. */
859
Insert_Y_Turn( RAS_ARGS Int y )
865
n = ras.numTurns - 1;
866
y_turns = ras.sizeBuff - ras.numTurns;
868
/* look for first y value that is <= */
869
while ( n >= 0 && y < y_turns[n] )
872
/* if it is <, simply insert it, ignore if == */
873
if ( n >= 0 && y > y_turns[n] )
876
y2 = (Int)y_turns[n];
885
if ( ras.maxBuff <= ras.top )
887
ras.error = Raster_Err_Overflow;
891
ras.sizeBuff[-ras.numTurns] = y;
898
/*************************************************************************/
901
/* Finalize_Profile_Table */
904
/* Adjust all links in the profiles list. */
907
/* SUCCESS on success. FAILURE in case of overflow. */
910
Finalize_Profile_Table( RAS_ARG )
925
p->link = (PProfile)( p->offset + p->height );
929
if ( p->flags & Flow_Up )
931
bottom = (Int)p->start;
932
top = (Int)( p->start + p->height - 1 );
936
bottom = (Int)( p->start - p->height + 1 );
939
p->offset += p->height - 1;
942
if ( Insert_Y_Turn( RAS_VARS bottom ) ||
943
Insert_Y_Turn( RAS_VARS top + 1 ) )
957
/*************************************************************************/
963
/* Subdivide one conic Bezier into two joint sub-arcs in the Bezier */
967
/* None (subdivided Bezier is taken from the top of the stack). */
970
/* This routine is the `beef' of this component. It is _the_ inner */
971
/* loop that should be optimized to hell to get the best performance. */
974
Split_Conic( TPoint* base )
979
base[4].x = base[2].x;
981
a = base[3].x = ( base[2].x + b ) / 2;
982
b = base[1].x = ( base[0].x + b ) / 2;
983
base[2].x = ( a + b ) / 2;
985
base[4].y = base[2].y;
987
a = base[3].y = ( base[2].y + b ) / 2;
988
b = base[1].y = ( base[0].y + b ) / 2;
989
base[2].y = ( a + b ) / 2;
991
/* hand optimized. gcc doesn't seem to be too good at common */
992
/* expression substitution and instruction scheduling ;-) */
996
/*************************************************************************/
1002
/* Subdivide a third-order Bezier arc into two joint sub-arcs in the */
1006
/* This routine is the `beef' of the component. It is one of _the_ */
1007
/* inner loops that should be optimized like hell to get the best */
1011
Split_Cubic( TPoint* base )
1016
base[6].x = base[3].x;
1019
base[1].x = a = ( base[0].x + c + 1 ) >> 1;
1020
base[5].x = b = ( base[3].x + d + 1 ) >> 1;
1021
c = ( c + d + 1 ) >> 1;
1022
base[2].x = a = ( a + c + 1 ) >> 1;
1023
base[4].x = b = ( b + c + 1 ) >> 1;
1024
base[3].x = ( a + b + 1 ) >> 1;
1026
base[6].y = base[3].y;
1029
base[1].y = a = ( base[0].y + c + 1 ) >> 1;
1030
base[5].y = b = ( base[3].y + d + 1 ) >> 1;
1031
c = ( c + d + 1 ) >> 1;
1032
base[2].y = a = ( a + c + 1 ) >> 1;
1033
base[4].y = b = ( b + c + 1 ) >> 1;
1034
base[3].y = ( a + b + 1 ) >> 1;
1038
/*************************************************************************/
1044
/* Compute the x-coordinates of an ascending line segment and store */
1045
/* them in the render pool. */
1048
/* x1 :: The x-coordinate of the segment's start point. */
1050
/* y1 :: The y-coordinate of the segment's start point. */
1052
/* x2 :: The x-coordinate of the segment's end point. */
1054
/* y2 :: The y-coordinate of the segment's end point. */
1056
/* miny :: A lower vertical clipping bound value. */
1058
/* maxy :: An upper vertical clipping bound value. */
1061
/* SUCCESS on success, FAILURE on render pool overflow. */
1064
Line_Up( RAS_ARGS Long x1,
1072
Int e1, e2, f1, f2, size; /* XXX: is `Short' sufficient? */
1081
if ( Dy <= 0 || y2 < miny || y1 > maxy )
1086
/* Take care: miny-y1 can be a very large value; we use */
1087
/* a slow MulDiv function to avoid clipping bugs */
1088
x1 += SMulDiv( Dx, miny - y1, Dy );
1089
e1 = (Int)TRUNC( miny );
1094
e1 = (Int)TRUNC( y1 );
1095
f1 = (Int)FRAC( y1 );
1100
/* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */
1101
e2 = (Int)TRUNC( maxy );
1106
e2 = (Int)TRUNC( y2 );
1107
f2 = (Int)FRAC( y2 );
1116
x1 += SMulDiv( Dx, ras.precision - f1, Dy );
1127
ras.joint = (char)( f2 == 0 );
1131
ras.cProfile->start = e1;
1136
if ( ras.top + size >= ras.maxBuff )
1138
ras.error = Raster_Err_Overflow;
1144
Ix = SMulDiv( ras.precision, Dx, Dy);
1145
Rx = ( ras.precision * Dx ) % Dy;
1150
Ix = SMulDiv( ras.precision, -Dx, Dy) * -1;
1151
Rx = ( ras.precision * -Dx ) % Dy;
1177
/*************************************************************************/
1183
/* Compute the x-coordinates of an descending line segment and store */
1184
/* them in the render pool. */
1187
/* x1 :: The x-coordinate of the segment's start point. */
1189
/* y1 :: The y-coordinate of the segment's start point. */
1191
/* x2 :: The x-coordinate of the segment's end point. */
1193
/* y2 :: The y-coordinate of the segment's end point. */
1195
/* miny :: A lower vertical clipping bound value. */
1197
/* maxy :: An upper vertical clipping bound value. */
1200
/* SUCCESS on success, FAILURE on render pool overflow. */
1203
Line_Down( RAS_ARGS Long x1,
1215
result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );
1217
if ( fresh && !ras.fresh )
1218
ras.cProfile->start = -ras.cProfile->start;
1224
/* A function type describing the functions used to split Bezier arcs */
1225
typedef void (*TSplitter)( TPoint* base );
1228
/*************************************************************************/
1234
/* Compute the x-coordinates of an ascending Bezier arc and store */
1235
/* them in the render pool. */
1238
/* degree :: The degree of the Bezier arc (either 2 or 3). */
1240
/* splitter :: The function to split Bezier arcs. */
1242
/* miny :: A lower vertical clipping bound value. */
1244
/* maxy :: An upper vertical clipping bound value. */
1247
/* SUCCESS on success, FAILURE on render pool overflow. */
1250
Bezier_Up( RAS_ARGS Int degree,
1255
Long y1, y2, e, e2, e0;
1269
if ( y2 < miny || y1 > maxy )
1284
f1 = (Short)( FRAC( y1 ) );
1295
*top++ = arc[degree].x;
1303
ras.cProfile->start = TRUNC( e0 );
1310
if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff )
1313
ras.error = Raster_Err_Overflow;
1319
while ( arc >= start_arc && e <= e2 )
1328
if ( y2 - y1 >= ras.precision_step )
1335
*top++ = arc[degree].x + FMulDiv( arc[0].x - arc[degree].x,
1361
/*************************************************************************/
1367
/* Compute the x-coordinates of an descending Bezier arc and store */
1368
/* them in the render pool. */
1371
/* degree :: The degree of the Bezier arc (either 2 or 3). */
1373
/* splitter :: The function to split Bezier arcs. */
1375
/* miny :: A lower vertical clipping bound value. */
1377
/* maxy :: An upper vertical clipping bound value. */
1380
/* SUCCESS on success, FAILURE on render pool overflow. */
1383
Bezier_Down( RAS_ARGS Int degree,
1388
TPoint* arc = ras.arc;
1392
arc[0].y = -arc[0].y;
1393
arc[1].y = -arc[1].y;
1394
arc[2].y = -arc[2].y;
1396
arc[3].y = -arc[3].y;
1400
result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny );
1402
if ( fresh && !ras.fresh )
1403
ras.cProfile->start = -ras.cProfile->start;
1405
arc[0].y = -arc[0].y;
1410
/*************************************************************************/
1416
/* Inject a new line segment and adjust the Profiles list. */
1419
/* x :: The x-coordinate of the segment's end point (its start point */
1420
/* is stored in `lastX'). */
1422
/* y :: The y-coordinate of the segment's end point (its start point */
1423
/* is stored in `lastY'). */
1426
/* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1430
Line_To( RAS_ARGS Long x,
1433
/* First, detect a change of direction */
1435
switch ( ras.state )
1438
if ( y > ras.lastY )
1440
if ( New_Profile( RAS_VARS Ascending_State,
1441
IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
1446
if ( y < ras.lastY )
1447
if ( New_Profile( RAS_VARS Descending_State,
1448
IS_TOP_OVERSHOOT( ras.lastY ) ) )
1453
case Ascending_State:
1454
if ( y < ras.lastY )
1456
if ( End_Profile( RAS_VARS IS_TOP_OVERSHOOT( ras.lastY ) ) ||
1457
New_Profile( RAS_VARS Descending_State,
1458
IS_TOP_OVERSHOOT( ras.lastY ) ) )
1463
case Descending_State:
1464
if ( y > ras.lastY )
1466
if ( End_Profile( RAS_VARS IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ||
1467
New_Profile( RAS_VARS Ascending_State,
1468
IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
1477
/* Then compute the lines */
1479
switch ( ras.state )
1481
case Ascending_State:
1482
if ( Line_Up( RAS_VARS ras.lastX, ras.lastY,
1483
x, y, ras.minY, ras.maxY ) )
1487
case Descending_State:
1488
if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
1489
x, y, ras.minY, ras.maxY ) )
1504
/*************************************************************************/
1510
/* Inject a new conic arc and adjust the profile list. */
1513
/* cx :: The x-coordinate of the arc's new control point. */
1515
/* cy :: The y-coordinate of the arc's new control point. */
1517
/* x :: The x-coordinate of the arc's end point (its start point is */
1518
/* stored in `lastX'). */
1520
/* y :: The y-coordinate of the arc's end point (its start point is */
1521
/* stored in `lastY'). */
1524
/* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1528
Conic_To( RAS_ARGS Long cx,
1533
Long y1, y2, y3, x3, ymin, ymax;
1538
ras.arc[2].x = ras.lastX;
1539
ras.arc[2].y = ras.lastY;
1552
/* first, categorize the Bezier arc */
1565
if ( y2 < ymin || y2 > ymax )
1567
/* this arc has no given direction, split it! */
1568
Split_Conic( ras.arc );
1571
else if ( y1 == y3 )
1573
/* this arc is flat, ignore it and pop it from the Bezier stack */
1578
/* the arc is y-monotonous, either ascending or descending */
1579
/* detect a change of direction */
1580
state_bez = y1 < y3 ? Ascending_State : Descending_State;
1581
if ( ras.state != state_bez )
1583
Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 )
1584
: IS_TOP_OVERSHOOT( y1 );
1587
/* finalize current profile if any */
1588
if ( ras.state != Unknown_State &&
1589
End_Profile( RAS_VARS o ) )
1592
/* create a new profile */
1593
if ( New_Profile( RAS_VARS state_bez, o ) )
1597
/* now call the appropriate routine */
1598
if ( state_bez == Ascending_State )
1600
if ( Bezier_Up( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
1604
if ( Bezier_Down( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
1608
} while ( ras.arc >= ras.arcs );
1620
/*************************************************************************/
1626
/* Inject a new cubic arc and adjust the profile list. */
1629
/* cx1 :: The x-coordinate of the arc's first new control point. */
1631
/* cy1 :: The y-coordinate of the arc's first new control point. */
1633
/* cx2 :: The x-coordinate of the arc's second new control point. */
1635
/* cy2 :: The y-coordinate of the arc's second new control point. */
1637
/* x :: The x-coordinate of the arc's end point (its start point is */
1638
/* stored in `lastX'). */
1640
/* y :: The y-coordinate of the arc's end point (its start point is */
1641
/* stored in `lastY'). */
1644
/* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1648
Cubic_To( RAS_ARGS Long cx1,
1655
Long y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
1660
ras.arc[3].x = ras.lastX;
1661
ras.arc[3].y = ras.lastY;
1677
/* first, categorize the Bezier arc */
1701
if ( ymin2 < ymin1 || ymax2 > ymax1 )
1703
/* this arc has no given direction, split it! */
1704
Split_Cubic( ras.arc );
1707
else if ( y1 == y4 )
1709
/* this arc is flat, ignore it and pop it from the Bezier stack */
1714
state_bez = ( y1 <= y4 ) ? Ascending_State : Descending_State;
1716
/* detect a change of direction */
1717
if ( ras.state != state_bez )
1719
Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 )
1720
: IS_TOP_OVERSHOOT( y1 );
1723
/* finalize current profile if any */
1724
if ( ras.state != Unknown_State &&
1725
End_Profile( RAS_VARS o ) )
1728
if ( New_Profile( RAS_VARS state_bez, o ) )
1732
/* compute intersections */
1733
if ( state_bez == Ascending_State )
1735
if ( Bezier_Up( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
1739
if ( Bezier_Down( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
1743
} while ( ras.arc >= ras.arcs );
1756
#define SWAP_( x, y ) do \
1766
/*************************************************************************/
1769
/* Decompose_Curve */
1772
/* Scan the outline arrays in order to emit individual segments and */
1773
/* Beziers by calling Line_To() and Bezier_To(). It handles all */
1774
/* weird cases, like when the first point is off the curve, or when */
1775
/* there are simply no `on' points in the contour! */
1778
/* first :: The index of the first point in the contour. */
1780
/* last :: The index of the last point in the contour. */
1782
/* flipped :: If set, flip the direction of the curve. */
1785
/* SUCCESS on success, FAILURE on error. */
1788
Decompose_Curve( RAS_ARGS UShort first,
1793
FT_Vector v_control;
1801
unsigned tag; /* current point's state */
1804
points = ras.outline.points;
1805
limit = points + last;
1807
v_start.x = SCALED( points[first].x );
1808
v_start.y = SCALED( points[first].y );
1809
v_last.x = SCALED( points[last].x );
1810
v_last.y = SCALED( points[last].y );
1814
SWAP_( v_start.x, v_start.y );
1815
SWAP_( v_last.x, v_last.y );
1818
v_control = v_start;
1820
point = points + first;
1821
tags = ras.outline.tags + first;
1823
/* set scan mode if necessary */
1824
if ( tags[0] & FT_CURVE_TAG_HAS_SCANMODE )
1825
ras.dropOutControl = (Byte)tags[0] >> 5;
1827
tag = FT_CURVE_TAG( tags[0] );
1829
/* A contour cannot start with a cubic control point! */
1830
if ( tag == FT_CURVE_TAG_CUBIC )
1831
goto Invalid_Outline;
1833
/* check first point to determine origin */
1834
if ( tag == FT_CURVE_TAG_CONIC )
1836
/* first point is conic control. Yes, this happens. */
1837
if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_CURVE_TAG_ON )
1839
/* start at last point if it is on the curve */
1845
/* if both first and last points are conic, */
1846
/* start at their middle and record its position */
1848
v_start.x = ( v_start.x + v_last.x ) / 2;
1849
v_start.y = ( v_start.y + v_last.y ) / 2;
1857
ras.lastX = v_start.x;
1858
ras.lastY = v_start.y;
1860
while ( point < limit )
1865
tag = FT_CURVE_TAG( tags[0] );
1869
case FT_CURVE_TAG_ON: /* emit a single line_to */
1874
x = SCALED( point->x );
1875
y = SCALED( point->y );
1879
if ( Line_To( RAS_VARS x, y ) )
1884
case FT_CURVE_TAG_CONIC: /* consume conic arcs */
1885
v_control.x = SCALED( point[0].x );
1886
v_control.y = SCALED( point[0].y );
1889
SWAP_( v_control.x, v_control.y );
1892
if ( point < limit )
1900
tag = FT_CURVE_TAG( tags[0] );
1902
x = SCALED( point[0].x );
1903
y = SCALED( point[0].y );
1908
if ( tag == FT_CURVE_TAG_ON )
1910
if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) )
1915
if ( tag != FT_CURVE_TAG_CONIC )
1916
goto Invalid_Outline;
1918
v_middle.x = ( v_control.x + x ) / 2;
1919
v_middle.y = ( v_control.y + y ) / 2;
1921
if ( Conic_To( RAS_VARS v_control.x, v_control.y,
1922
v_middle.x, v_middle.y ) )
1931
if ( Conic_To( RAS_VARS v_control.x, v_control.y,
1932
v_start.x, v_start.y ) )
1937
default: /* FT_CURVE_TAG_CUBIC */
1939
Long x1, y1, x2, y2, x3, y3;
1942
if ( point + 1 > limit ||
1943
FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
1944
goto Invalid_Outline;
1949
x1 = SCALED( point[-2].x );
1950
y1 = SCALED( point[-2].y );
1951
x2 = SCALED( point[-1].x );
1952
y2 = SCALED( point[-1].y );
1960
if ( point <= limit )
1962
x3 = SCALED( point[0].x );
1963
y3 = SCALED( point[0].y );
1968
if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) )
1973
if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) )
1980
/* close the contour with a line segment */
1981
if ( Line_To( RAS_VARS v_start.x, v_start.y ) )
1988
ras.error = Raster_Err_Invalid;
1995
/*************************************************************************/
2001
/* Convert a glyph into a series of segments and arcs and make a */
2002
/* profiles list with them. */
2005
/* flipped :: If set, flip the direction of curve. */
2008
/* SUCCESS on success, FAILURE if any error was encountered during */
2012
Convert_Glyph( RAS_ARGS int flipped )
2017
PProfile lastProfile;
2020
ras.fProfile = NULL;
2024
ras.maxBuff = ras.sizeBuff - AlignProfileSize;
2028
ras.cProfile = (PProfile)ras.top;
2029
ras.cProfile->offset = ras.top;
2034
for ( i = 0; i < ras.outline.n_contours; i++ )
2039
ras.state = Unknown_State;
2040
ras.gProfile = NULL;
2042
if ( Decompose_Curve( RAS_VARS (unsigned short)start,
2043
ras.outline.contours[i],
2047
start = ras.outline.contours[i] + 1;
2049
/* we must now check whether the extreme arcs join or not */
2050
if ( FRAC( ras.lastY ) == 0 &&
2051
ras.lastY >= ras.minY &&
2052
ras.lastY <= ras.maxY )
2053
if ( ras.gProfile &&
2054
( ras.gProfile->flags & Flow_Up ) ==
2055
( ras.cProfile->flags & Flow_Up ) )
2057
/* Note that ras.gProfile can be nil if the contour was too small */
2060
lastProfile = ras.cProfile;
2061
if ( ras.cProfile->flags & Flow_Up )
2062
o = IS_TOP_OVERSHOOT( ras.lastY );
2064
o = IS_BOTTOM_OVERSHOOT( ras.lastY );
2065
if ( End_Profile( RAS_VARS o ) )
2068
/* close the `next profile in contour' linked list */
2070
lastProfile->next = ras.gProfile;
2073
if ( Finalize_Profile_Table( RAS_VAR ) )
2076
return (Bool)( ras.top < ras.maxBuff ? SUCCESS : FAILURE );
2080
/*************************************************************************/
2081
/*************************************************************************/
2083
/** SCAN-LINE SWEEPS AND DRAWING **/
2085
/*************************************************************************/
2086
/*************************************************************************/
2089
/*************************************************************************/
2093
/* Initializes an empty linked list. */
2096
Init_Linked( TProfileList* l )
2102
/*************************************************************************/
2106
/* Inserts a new profile in a linked list. */
2109
InsNew( PProfileList list,
2112
PProfile *old, current;
2122
if ( x < current->X )
2124
old = ¤t->link;
2128
profile->link = current;
2133
/*************************************************************************/
2137
/* Removes an old profile from a linked list. */
2140
DelOld( PProfileList list,
2143
PProfile *old, current;
2151
if ( current == profile )
2153
*old = current->link;
2157
old = ¤t->link;
2161
/* we should never get there, unless the profile was not part of */
2166
/*************************************************************************/
2170
/* Sorts a trace list. In 95%, the list is already sorted. We need */
2171
/* an algorithm which is fast in this case. Bubble sort is enough */
2175
Sort( PProfileList list )
2177
PProfile *old, current, next;
2180
/* First, set the new X coordinate of each profile */
2184
current->X = *current->offset;
2185
current->offset += current->flags & Flow_Up ? 1 : -1;
2187
current = current->link;
2190
/* Then sort them */
2197
next = current->link;
2201
if ( current->X <= next->X )
2203
old = ¤t->link;
2212
current->link = next->link;
2213
next->link = current;
2219
next = current->link;
2224
/*************************************************************************/
2226
/* Vertical Sweep Procedure Set */
2228
/* These four routines are used during the vertical black/white sweep */
2229
/* phase by the generic Draw_Sweep() function. */
2231
/*************************************************************************/
2234
Vertical_Sweep_Init( RAS_ARGS Short* min,
2237
Long pitch = ras.target.pitch;
2242
ras.traceIncr = (Short)-pitch;
2243
ras.traceOfs = -*min * pitch;
2245
ras.traceOfs += ( ras.target.rows - 1 ) * pitch;
2253
Vertical_Sweep_Span( RAS_ARGS Short y,
2269
/* Drop-out control */
2271
e1 = TRUNC( CEILING( x1 ) );
2273
if ( x2 - x1 - ras.precision <= ras.precision_jitter )
2276
e2 = TRUNC( FLOOR( x2 ) );
2278
if ( e2 >= 0 && e1 < ras.bWidth )
2282
if ( e2 >= ras.bWidth )
2283
e2 = ras.bWidth - 1;
2285
c1 = (Short)( e1 >> 3 );
2286
c2 = (Short)( e2 >> 3 );
2288
f1 = (Byte) ( 0xFF >> ( e1 & 7 ) );
2289
f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) );
2291
if ( ras.gray_min_x > c1 )
2292
ras.gray_min_x = (short)c1;
2293
if ( ras.gray_max_x < c2 )
2294
ras.gray_max_x = (short)c2;
2296
target = ras.bTarget + ras.traceOfs + c1;
2303
/* memset() is slower than the following code on many platforms. */
2304
/* This is due to the fact that, in the vast majority of cases, */
2305
/* the span length in bytes is relatively small. */
2315
*target |= ( f1 & f2 );
2321
Vertical_Sweep_Drop( RAS_ARGS Short y,
2331
/* Drop-out control */
2337
/* +-------------+---------------------+------------+ */
2341
/* pixel contour contour pixel */
2344
/* drop-out mode scan conversion rules (as defined in OpenType) */
2345
/* --------------------------------------------------------------- */
2349
/* 3 same as mode 2 */
2352
/* 6, 7 same as mode 2 */
2360
Int dropOutControl = left->flags & 7;
2363
if ( e1 == e2 + ras.precision )
2365
switch ( dropOutControl )
2367
case 0: /* simple drop-outs including stubs */
2371
case 4: /* smart drop-outs including stubs */
2372
pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2375
case 1: /* simple drop-outs excluding stubs */
2376
case 5: /* smart drop-outs excluding stubs */
2378
/* Drop-out Control Rules #4 and #6 */
2380
/* The specification neither provides an exact definition */
2381
/* of a `stub' nor gives exact rules to exclude them. */
2383
/* Here the constraints we use to recognize a stub. */
2387
/* - P_Left and P_Right are in the same contour */
2388
/* - P_Right is the successor of P_Left in that contour */
2389
/* - y is the top of P_Left and P_Right */
2393
/* - P_Left and P_Right are in the same contour */
2394
/* - P_Left is the successor of P_Right in that contour */
2395
/* - y is the bottom of P_Left */
2397
/* We draw a stub if the following constraints are met. */
2399
/* - for an upper or lower stub, there is top or bottom */
2400
/* overshoot, respectively */
2401
/* - the covered interval is greater or equal to a half */
2404
/* upper stub test */
2405
if ( left->next == right &&
2406
left->height <= 0 &&
2407
!( left->flags & Overshoot_Top &&
2408
x2 - x1 >= ras.precision_half ) )
2411
/* lower stub test */
2412
if ( right->next == left &&
2414
!( left->flags & Overshoot_Bottom &&
2415
x2 - x1 >= ras.precision_half ) )
2418
if ( dropOutControl == 1 )
2421
pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2424
default: /* modes 2, 3, 6, 7 */
2425
return; /* no drop-out control */
2428
/* undocumented but confirmed: If the drop-out would result in a */
2429
/* pixel outside of the bounding box, use the pixel inside of the */
2430
/* bounding box instead */
2433
else if ( TRUNC( pxl ) >= ras.bWidth )
2436
/* check that the other pixel isn't set */
2437
e1 = pxl == e1 ? e2 : e1;
2441
c1 = (Short)( e1 >> 3 );
2442
f1 = (Short)( e1 & 7 );
2444
if ( e1 >= 0 && e1 < ras.bWidth &&
2445
ras.bTarget[ras.traceOfs + c1] & ( 0x80 >> f1 ) )
2454
if ( e1 >= 0 && e1 < ras.bWidth )
2456
c1 = (Short)( e1 >> 3 );
2457
f1 = (Short)( e1 & 7 );
2459
if ( ras.gray_min_x > c1 )
2460
ras.gray_min_x = c1;
2461
if ( ras.gray_max_x < c1 )
2462
ras.gray_max_x = c1;
2464
ras.bTarget[ras.traceOfs + c1] |= (char)( 0x80 >> f1 );
2470
Vertical_Sweep_Step( RAS_ARG )
2472
ras.traceOfs += ras.traceIncr;
2476
/***********************************************************************/
2478
/* Horizontal Sweep Procedure Set */
2480
/* These four routines are used during the horizontal black/white */
2481
/* sweep phase by the generic Draw_Sweep() function. */
2483
/***********************************************************************/
2486
Horizontal_Sweep_Init( RAS_ARGS Short* min,
2489
/* nothing, really */
2497
Horizontal_Sweep_Span( RAS_ARGS Short y,
2511
if ( x2 - x1 < ras.precision )
2518
bits = ras.bTarget + ( y >> 3 );
2519
f1 = (Byte)( 0x80 >> ( y & 7 ) );
2523
if ( e1 >= 0 && e1 < ras.target.rows )
2528
p = bits - e1 * ras.target.pitch;
2529
if ( ras.target.pitch > 0 )
2530
p += ( ras.target.rows - 1 ) * ras.target.pitch;
2540
Horizontal_Sweep_Drop( RAS_ARGS Short y,
2551
/* During the horizontal sweep, we only take care of drop-outs */
2553
/* e1 + <-- pixel center */
2555
/* x1 ---+--> <-- contour */
2558
/* x2 <--+--- <-- contour */
2561
/* e2 + <-- pixel center */
2569
Int dropOutControl = left->flags & 7;
2572
if ( e1 == e2 + ras.precision )
2574
switch ( dropOutControl )
2576
case 0: /* simple drop-outs including stubs */
2580
case 4: /* smart drop-outs including stubs */
2581
pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2584
case 1: /* simple drop-outs excluding stubs */
2585
case 5: /* smart drop-outs excluding stubs */
2586
/* see Vertical_Sweep_Drop for details */
2588
/* rightmost stub test */
2589
if ( left->next == right &&
2590
left->height <= 0 &&
2591
!( left->flags & Overshoot_Top &&
2592
x2 - x1 >= ras.precision_half ) )
2595
/* leftmost stub test */
2596
if ( right->next == left &&
2598
!( left->flags & Overshoot_Bottom &&
2599
x2 - x1 >= ras.precision_half ) )
2602
if ( dropOutControl == 1 )
2605
pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2608
default: /* modes 2, 3, 6, 7 */
2609
return; /* no drop-out control */
2612
/* undocumented but confirmed: If the drop-out would result in a */
2613
/* pixel outside of the bounding box, use the pixel inside of the */
2614
/* bounding box instead */
2617
else if ( TRUNC( pxl ) >= ras.target.rows )
2620
/* check that the other pixel isn't set */
2621
e1 = pxl == e1 ? e2 : e1;
2625
bits = ras.bTarget + ( y >> 3 );
2626
f1 = (Byte)( 0x80 >> ( y & 7 ) );
2628
bits -= e1 * ras.target.pitch;
2629
if ( ras.target.pitch > 0 )
2630
bits += ( ras.target.rows - 1 ) * ras.target.pitch;
2633
e1 < ras.target.rows &&
2641
bits = ras.bTarget + ( y >> 3 );
2642
f1 = (Byte)( 0x80 >> ( y & 7 ) );
2646
if ( e1 >= 0 && e1 < ras.target.rows )
2648
bits -= e1 * ras.target.pitch;
2649
if ( ras.target.pitch > 0 )
2650
bits += ( ras.target.rows - 1 ) * ras.target.pitch;
2658
Horizontal_Sweep_Step( RAS_ARG )
2660
/* Nothing, really */
2665
#ifdef FT_RASTER_OPTION_ANTI_ALIASING
2668
/*************************************************************************/
2670
/* Vertical Gray Sweep Procedure Set */
2672
/* These two routines are used during the vertical gray-levels sweep */
2673
/* phase by the generic Draw_Sweep() function. */
2677
/* - The target pixmap's width *must* be a multiple of 4. */
2679
/* - You have to use the function Vertical_Sweep_Span() for the gray */
2682
/*************************************************************************/
2685
Vertical_Gray_Sweep_Init( RAS_ARGS Short* min,
2688
Long pitch, byte_len;
2692
*max = ( *max + 3 ) & -2;
2695
pitch = ras.target.pitch;
2697
ras.traceIncr = (Short)byte_len;
2698
ras.traceG = ( *min / 2 ) * byte_len;
2702
ras.traceG += ( ras.target.rows - 1 ) * pitch;
2703
byte_len = -byte_len;
2706
ras.gray_min_x = (Short)byte_len;
2707
ras.gray_max_x = -(Short)byte_len;
2712
Vertical_Gray_Sweep_Step( RAS_ARG )
2715
PByte pix, bit, bit2;
2716
short* count = (short*)count_table;
2720
ras.traceOfs += ras.gray_width;
2722
if ( ras.traceOfs > ras.gray_width )
2724
pix = ras.gTarget + ras.traceG + ras.gray_min_x * 4;
2727
if ( ras.gray_max_x >= 0 )
2729
Long last_pixel = ras.target.width - 1;
2730
Int last_cell = last_pixel >> 2;
2731
Int last_bit = last_pixel & 3;
2735
if ( ras.gray_max_x >= last_cell && last_bit != 3 )
2737
ras.gray_max_x = last_cell - 1;
2741
if ( ras.gray_min_x < 0 )
2744
bit = ras.bTarget + ras.gray_min_x;
2745
bit2 = bit + ras.gray_width;
2747
c1 = ras.gray_max_x - ras.gray_min_x;
2751
c2 = count[*bit] + count[*bit2];
2755
pix[0] = grays[(c2 >> 12) & 0x000F];
2756
pix[1] = grays[(c2 >> 8 ) & 0x000F];
2757
pix[2] = grays[(c2 >> 4 ) & 0x000F];
2758
pix[3] = grays[ c2 & 0x000F];
2772
c2 = count[*bit] + count[*bit2];
2778
pix[2] = grays[(c2 >> 4 ) & 0x000F];
2780
pix[1] = grays[(c2 >> 8 ) & 0x000F];
2782
pix[0] = grays[(c2 >> 12) & 0x000F];
2792
ras.traceG += ras.traceIncr;
2794
ras.gray_min_x = 32000;
2795
ras.gray_max_x = -32000;
2801
Horizontal_Gray_Sweep_Span( RAS_ARGS Short y,
2807
/* nothing, really */
2818
Horizontal_Gray_Sweep_Drop( RAS_ARGS Short y,
2829
/* During the horizontal sweep, we only take care of drop-outs */
2836
Int dropOutControl = left->flags & 7;
2839
if ( e1 == e2 + ras.precision )
2841
switch ( dropOutControl )
2843
case 0: /* simple drop-outs including stubs */
2847
case 4: /* smart drop-outs including stubs */
2848
e1 = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2851
case 1: /* simple drop-outs excluding stubs */
2852
case 5: /* smart drop-outs excluding stubs */
2853
/* see Vertical_Sweep_Drop for details */
2855
/* rightmost stub test */
2856
if ( left->next == right && left->height <= 0 )
2859
/* leftmost stub test */
2860
if ( right->next == left && left->start == y )
2863
if ( dropOutControl == 1 )
2866
e1 = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2870
default: /* modes 2, 3, 6, 7 */
2871
return; /* no drop-out control */
2880
if ( x2 - x1 >= ras.precision_half )
2881
color = ras.grays[2];
2883
color = ras.grays[1];
2885
e1 = TRUNC( e1 ) / 2;
2886
if ( e1 < ras.target.rows )
2888
pixel = ras.gTarget - e1 * ras.target.pitch + y / 2;
2889
if ( ras.target.pitch > 0 )
2890
pixel += ( ras.target.rows - 1 ) * ras.target.pitch;
2892
if ( pixel[0] == ras.grays[0] )
2899
#endif /* FT_RASTER_OPTION_ANTI_ALIASING */
2902
/*************************************************************************/
2904
/* Generic Sweep Drawing routine */
2906
/*************************************************************************/
2909
Draw_Sweep( RAS_ARG )
2911
Short y, y_change, y_height;
2913
PProfile P, Q, P_Left, P_Right;
2915
Short min_Y, max_Y, top, bottom, dropouts;
2917
Long x1, x2, xs, e1, e2;
2919
TProfileList waiting;
2920
TProfileList draw_left, draw_right;
2923
/* initialize empty linked lists */
2925
Init_Linked( &waiting );
2927
Init_Linked( &draw_left );
2928
Init_Linked( &draw_right );
2930
/* first, compute min and max Y */
2933
max_Y = (Short)TRUNC( ras.minY );
2934
min_Y = (Short)TRUNC( ras.maxY );
2940
bottom = (Short)P->start;
2941
top = (Short)( P->start + P->height - 1 );
2943
if ( min_Y > bottom )
2949
InsNew( &waiting, P );
2954
/* check the Y-turns */
2955
if ( ras.numTurns == 0 )
2957
ras.error = Raster_Err_Invalid;
2961
/* now initialize the sweep */
2963
ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y );
2965
/* then compute the distance of each profile from min_Y */
2971
P->countL = (UShort)( P->start - min_Y );
2980
if ( ras.numTurns > 0 &&
2981
ras.sizeBuff[-ras.numTurns] == min_Y )
2984
while ( ras.numTurns > 0 )
2986
/* check waiting list for new activations */
2993
P->countL -= y_height;
2994
if ( P->countL == 0 )
2996
DelOld( &waiting, P );
2998
if ( P->flags & Flow_Up )
2999
InsNew( &draw_left, P );
3001
InsNew( &draw_right, P );
3007
/* sort the drawing lists */
3010
Sort( &draw_right );
3012
y_change = (Short)ras.sizeBuff[-ras.numTurns--];
3013
y_height = (Short)( y_change - y );
3015
while ( y < y_change )
3022
P_Right = draw_right;
3039
if ( x2 - x1 <= ras.precision &&
3040
e1 != x1 && e2 != x2 )
3042
if ( e1 > e2 || e2 == e1 + ras.precision )
3044
Int dropOutControl = P_Left->flags & 7;
3047
if ( dropOutControl != 2 )
3049
/* a drop-out was detected */
3054
/* mark profile for drop-out processing */
3063
ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right );
3067
P_Left = P_Left->link;
3068
P_Right = P_Right->link;
3071
/* handle drop-outs _after_ the span drawing -- */
3072
/* drop-out processing has been moved out of the loop */
3073
/* for performance tuning */
3079
ras.Proc_Sweep_Step( RAS_VAR );
3086
Sort( &draw_right );
3090
/* now finalize the profiles that need it */
3096
if ( P->height == 0 )
3097
DelOld( &draw_left, P );
3105
if ( P->height == 0 )
3106
DelOld( &draw_right, P );
3111
/* for gray-scaling, flush the bitmap scanline cache */
3112
while ( y <= max_Y )
3114
ras.Proc_Sweep_Step( RAS_VAR );
3123
P_Right = draw_right;
3127
if ( P_Left->countL )
3131
dropouts--; /* -- this is useful when debugging only */
3133
ras.Proc_Sweep_Drop( RAS_VARS y,
3140
P_Left = P_Left->link;
3141
P_Right = P_Right->link;
3148
/*************************************************************************/
3151
/* Render_Single_Pass */
3154
/* Perform one sweep with sub-banding. */
3157
/* flipped :: If set, flip the direction of the outline. */
3160
/* Renderer error code. */
3163
Render_Single_Pass( RAS_ARGS Bool flipped )
3168
while ( ras.band_top >= 0 )
3170
ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision;
3171
ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision;
3175
ras.error = Raster_Err_None;
3177
if ( Convert_Glyph( RAS_VARS flipped ) )
3179
if ( ras.error != Raster_Err_Overflow )
3182
ras.error = Raster_Err_None;
3187
ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) );
3190
i = ras.band_stack[ras.band_top].y_min;
3191
j = ras.band_stack[ras.band_top].y_max;
3193
k = (Short)( ( i + j ) / 2 );
3195
if ( ras.band_top >= 7 || k < i )
3198
ras.error = Raster_Err_Invalid;
3203
ras.band_stack[ras.band_top + 1].y_min = k;
3204
ras.band_stack[ras.band_top + 1].y_max = j;
3206
ras.band_stack[ras.band_top].y_max = (Short)( k - 1 );
3213
if ( Draw_Sweep( RAS_VAR ) )
3223
/*************************************************************************/
3229
/* Render a glyph in a bitmap. Sub-banding if needed. */
3232
/* FreeType error code. 0 means success. */
3234
FT_LOCAL_DEF( FT_Error )
3235
Render_Glyph( RAS_ARG )
3240
Set_High_Precision( RAS_VARS ras.outline.flags &
3241
FT_OUTLINE_HIGH_PRECISION );
3242
ras.scale_shift = ras.precision_shift;
3244
if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )
3245
ras.dropOutControl = 2;
3248
if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS )
3249
ras.dropOutControl = 4;
3251
ras.dropOutControl = 0;
3253
if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) )
3254
ras.dropOutControl += 1;
3257
ras.second_pass = (FT_Byte)( !( ras.outline.flags &
3258
FT_OUTLINE_SINGLE_PASS ) );
3260
/* Vertical Sweep */
3261
ras.Proc_Sweep_Init = Vertical_Sweep_Init;
3262
ras.Proc_Sweep_Span = Vertical_Sweep_Span;
3263
ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
3264
ras.Proc_Sweep_Step = Vertical_Sweep_Step;
3267
ras.band_stack[0].y_min = 0;
3268
ras.band_stack[0].y_max = (short)( ras.target.rows - 1 );
3270
ras.bWidth = (unsigned short)ras.target.width;
3271
ras.bTarget = (Byte*)ras.target.buffer;
3273
if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 )
3276
/* Horizontal Sweep */
3277
if ( ras.second_pass && ras.dropOutControl != 2 )
3279
ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
3280
ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
3281
ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;
3282
ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
3285
ras.band_stack[0].y_min = 0;
3286
ras.band_stack[0].y_max = (short)( ras.target.width - 1 );
3288
if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 )
3292
return Raster_Err_None;
3296
#ifdef FT_RASTER_OPTION_ANTI_ALIASING
3298
/*************************************************************************/
3301
/* Render_Gray_Glyph */
3304
/* Render a glyph with grayscaling. Sub-banding if needed. */
3307
/* FreeType error code. 0 means success. */
3309
FT_LOCAL_DEF( FT_Error )
3310
Render_Gray_Glyph( RAS_ARG )
3316
Set_High_Precision( RAS_VARS ras.outline.flags &
3317
FT_OUTLINE_HIGH_PRECISION );
3318
ras.scale_shift = ras.precision_shift + 1;
3320
if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )
3321
ras.dropOutControl = 2;
3324
if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS )
3325
ras.dropOutControl = 4;
3327
ras.dropOutControl = 0;
3329
if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) )
3330
ras.dropOutControl += 1;
3333
ras.second_pass = !( ras.outline.flags & FT_OUTLINE_SINGLE_PASS );
3335
/* Vertical Sweep */
3338
ras.band_stack[0].y_min = 0;
3339
ras.band_stack[0].y_max = 2 * ras.target.rows - 1;
3341
ras.bWidth = ras.gray_width;
3342
pixel_width = 2 * ( ( ras.target.width + 3 ) >> 2 );
3344
if ( ras.bWidth > pixel_width )
3345
ras.bWidth = pixel_width;
3347
ras.bWidth = ras.bWidth * 8;
3348
ras.bTarget = (Byte*)ras.gray_lines;
3349
ras.gTarget = (Byte*)ras.target.buffer;
3351
ras.Proc_Sweep_Init = Vertical_Gray_Sweep_Init;
3352
ras.Proc_Sweep_Span = Vertical_Sweep_Span;
3353
ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
3354
ras.Proc_Sweep_Step = Vertical_Gray_Sweep_Step;
3356
error = Render_Single_Pass( RAS_VARS 0 );
3360
/* Horizontal Sweep */
3361
if ( ras.second_pass && ras.dropOutControl != 2 )
3363
ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
3364
ras.Proc_Sweep_Span = Horizontal_Gray_Sweep_Span;
3365
ras.Proc_Sweep_Drop = Horizontal_Gray_Sweep_Drop;
3366
ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
3369
ras.band_stack[0].y_min = 0;
3370
ras.band_stack[0].y_max = ras.target.width * 2 - 1;
3372
error = Render_Single_Pass( RAS_VARS 1 );
3377
return Raster_Err_None;
3380
#else /* !FT_RASTER_OPTION_ANTI_ALIASING */
3382
FT_LOCAL_DEF( FT_Error )
3383
Render_Gray_Glyph( RAS_ARG )
3387
return Raster_Err_Unsupported;
3390
#endif /* !FT_RASTER_OPTION_ANTI_ALIASING */
3394
ft_black_init( PRaster raster )
3396
#ifdef FT_RASTER_OPTION_ANTI_ALIASING
3400
/* set default 5-levels gray palette */
3401
for ( n = 0; n < 5; n++ )
3402
raster->grays[n] = n * 255 / 4;
3404
raster->gray_width = RASTER_GRAY_LINES / 2;
3406
FT_UNUSED( raster );
3411
/**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
3412
/**** a static object. *****/
3419
ft_black_new( void* memory,
3420
FT_Raster *araster )
3422
static TRaster the_raster;
3423
FT_UNUSED( memory );
3426
*araster = (FT_Raster)&the_raster;
3427
FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) );
3428
ft_black_init( &the_raster );
3435
ft_black_done( FT_Raster raster )
3438
FT_UNUSED( raster );
3442
#else /* !_STANDALONE_ */
3446
ft_black_new( FT_Memory memory,
3450
PRaster raster = NULL;
3454
if ( !FT_NEW( raster ) )
3456
raster->memory = memory;
3457
ft_black_init( raster );
3467
ft_black_done( PRaster raster )
3469
FT_Memory memory = (FT_Memory)raster->memory;
3474
#endif /* !_STANDALONE_ */
3478
ft_black_reset( PRaster raster,
3484
if ( pool_base && pool_size >= (long)sizeof(TWorker) + 2048 )
3486
PWorker worker = (PWorker)pool_base;
3489
raster->buffer = pool_base + ( ( sizeof ( *worker ) + 7 ) & ~7 );
3490
raster->buffer_size = pool_base + pool_size - (char*)raster->buffer;
3491
raster->worker = worker;
3495
raster->buffer = NULL;
3496
raster->buffer_size = 0;
3497
raster->worker = NULL;
3504
ft_black_set_mode( PRaster raster,
3506
const char* palette )
3508
#ifdef FT_RASTER_OPTION_ANTI_ALIASING
3510
if ( mode == FT_MAKE_TAG( 'p', 'a', 'l', '5' ) )
3512
/* set 5-levels gray palette */
3513
raster->grays[0] = palette[0];
3514
raster->grays[1] = palette[1];
3515
raster->grays[2] = palette[2];
3516
raster->grays[3] = palette[3];
3517
raster->grays[4] = palette[4];
3522
FT_UNUSED( raster );
3524
FT_UNUSED( palette );
3531
ft_black_render( PRaster raster,
3532
const FT_Raster_Params* params )
3534
const FT_Outline* outline = (const FT_Outline*)params->source;
3535
const FT_Bitmap* target_map = params->target;
3539
if ( !raster || !raster->buffer || !raster->buffer_size )
3540
return Raster_Err_Not_Ini;
3543
return Raster_Err_Invalid;
3545
/* return immediately if the outline is empty */
3546
if ( outline->n_points == 0 || outline->n_contours <= 0 )
3547
return Raster_Err_None;
3549
if ( !outline->contours || !outline->points )
3550
return Raster_Err_Invalid;
3552
if ( outline->n_points !=
3553
outline->contours[outline->n_contours - 1] + 1 )
3554
return Raster_Err_Invalid;
3556
worker = raster->worker;
3558
/* this version of the raster does not support direct rendering, sorry */
3559
if ( params->flags & FT_RASTER_FLAG_DIRECT )
3560
return Raster_Err_Unsupported;
3563
return Raster_Err_Invalid;
3566
if ( !target_map->width || !target_map->rows )
3567
return Raster_Err_None;
3569
if ( !target_map->buffer )
3570
return Raster_Err_Invalid;
3572
ras.outline = *outline;
3573
ras.target = *target_map;
3575
worker->buff = (PLong) raster->buffer;
3576
worker->sizeBuff = worker->buff +
3577
raster->buffer_size / sizeof ( Long );
3578
#ifdef FT_RASTER_OPTION_ANTI_ALIASING
3579
worker->grays = raster->grays;
3580
worker->gray_width = raster->gray_width;
3582
FT_MEM_ZERO( worker->gray_lines, worker->gray_width * 2 );
3585
return ( params->flags & FT_RASTER_FLAG_AA )
3586
? Render_Gray_Glyph( RAS_VAR )
3587
: Render_Glyph( RAS_VAR );
3591
FT_DEFINE_RASTER_FUNCS( ft_standard_raster,
3592
FT_GLYPH_FORMAT_OUTLINE,
3593
(FT_Raster_New_Func) ft_black_new,
3594
(FT_Raster_Reset_Func) ft_black_reset,
3595
(FT_Raster_Set_Mode_Func)ft_black_set_mode,
3596
(FT_Raster_Render_Func) ft_black_render,
3597
(FT_Raster_Done_Func) ft_black_done