1 /***************************************************************************/
5 /* The FreeType glyph rasterizer (body). */
7 /* Copyright 1996-2001, 2002, 2003, 2005, 2007, 2008 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 /*************************************************************************/
55 #else /* !_STANDALONE_ */
59 #include FT_INTERNAL_CALC_H /* for FT_MulDiv only */
61 #endif /* !_STANDALONE_ */
64 /*************************************************************************/
66 /* A simple technical note on how the raster works */
67 /* ----------------------------------------------- */
69 /* Converting an outline into a bitmap is achieved in several steps: */
71 /* 1 - Decomposing the outline into successive `profiles'. Each */
72 /* profile is simply an array of scanline intersections on a given */
73 /* dimension. A profile's main attributes are */
75 /* o its scanline position boundaries, i.e. `Ymin' and `Ymax'. */
77 /* o an array of intersection coordinates for each scanline */
78 /* between `Ymin' and `Ymax'. */
80 /* o a direction, indicating whether it was built going `up' or */
81 /* `down', as this is very important for filling rules. */
83 /* 2 - Sweeping the target map's scanlines in order to compute segment */
84 /* `spans' which are then filled. Additionally, this pass */
85 /* performs drop-out control. */
87 /* The outline data is parsed during step 1 only. The profiles are */
88 /* built from the bottom of the render pool, used as a stack. The */
89 /* following graphics shows the profile list under construction: */
91 /* ____________________________________________________________ _ _ */
93 /* | profile | coordinates for | profile | coordinates for |--> */
94 /* | 1 | profile 1 | 2 | profile 2 |--> */
95 /* |_________|___________________|_________|_________________|__ _ _ */
99 /* start of render pool top */
101 /* The top of the profile stack is kept in the `top' variable. */
103 /* As you can see, a profile record is pushed on top of the render */
104 /* pool, which is then followed by its coordinates/intersections. If */
105 /* a change of direction is detected in the outline, a new profile is */
106 /* generated until the end of the outline. */
108 /* Note that when all profiles have been generated, the function */
109 /* Finalize_Profile_Table() is used to record, for each profile, its */
110 /* bottom-most scanline as well as the scanline above its upmost */
111 /* boundary. These positions are called `y-turns' because they (sort */
112 /* of) correspond to local extrema. They are stored in a sorted list */
113 /* built from the top of the render pool as a downwards stack: */
115 /* _ _ _______________________________________ */
117 /* <--| sorted list of | */
118 /* <--| extrema scanlines | */
119 /* _ _ __________________|____________________| */
123 /* maxBuff sizeBuff = end of pool */
125 /* This list is later used during the sweep phase in order to */
126 /* optimize performance (see technical note on the sweep below). */
128 /* Of course, the raster detects whether the two stacks collide and */
129 /* handles the situation properly. */
131 /*************************************************************************/
134 /*************************************************************************/
135 /*************************************************************************/
137 /** CONFIGURATION MACROS **/
139 /*************************************************************************/
140 /*************************************************************************/
142 /* define DEBUG_RASTER if you want to compile a debugging version */
143 #define xxxDEBUG_RASTER
145 /* undefine FT_RASTER_OPTION_ANTI_ALIASING if you do not want to support */
146 /* 5-levels anti-aliasing */
147 #undef FT_RASTER_OPTION_ANTI_ALIASING
149 /* The size of the two-lines intermediate bitmap used */
150 /* for anti-aliasing, in bytes. */
151 #define RASTER_GRAY_LINES 2048
154 /*************************************************************************/
155 /*************************************************************************/
157 /** OTHER MACROS (do not change) **/
159 /*************************************************************************/
160 /*************************************************************************/
162 /*************************************************************************/
164 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
165 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
166 /* messages during execution. */
169 #define FT_COMPONENT trace_raster
175 /* This macro is used to indicate that a function parameter is unused. */
176 /* Its purpose is simply to reduce compiler warnings. Note also that */
177 /* simply defining it as `(void)x' doesn't avoid warnings with certain */
178 /* ANSI compilers (e.g. LCC). */
179 #define FT_UNUSED( x ) (x) = (x)
181 /* Disable the tracing mechanism for simplicity -- developers can */
182 /* activate it easily by redefining these two macros. */
184 #define FT_ERROR( x ) do ; while ( 0 ) /* nothing */
188 #define FT_TRACE( x ) do ; while ( 0 ) /* nothing */
189 #define FT_TRACE1( x ) do ; while ( 0 ) /* nothing */
190 #define FT_TRACE6( x ) do ; while ( 0 ) /* nothing */
193 #define Raster_Err_None 0
194 #define Raster_Err_Not_Ini -1
195 #define Raster_Err_Overflow -2
196 #define Raster_Err_Neg_Height -3
197 #define Raster_Err_Invalid -4
198 #define Raster_Err_Unsupported -5
200 #define ft_memset memset
202 #else /* _STANDALONE_ */
205 #include FT_INTERNAL_OBJECTS_H
206 #include FT_INTERNAL_DEBUG_H /* for FT_TRACE() and FT_ERROR() */
208 #include "rasterrs.h"
210 #define Raster_Err_None Raster_Err_Ok
211 #define Raster_Err_Not_Ini Raster_Err_Raster_Uninitialized
212 #define Raster_Err_Overflow Raster_Err_Raster_Overflow
213 #define Raster_Err_Neg_Height Raster_Err_Raster_Negative_Height
214 #define Raster_Err_Invalid Raster_Err_Invalid_Outline
215 #define Raster_Err_Unsupported Raster_Err_Cannot_Render_Glyph
218 #endif /* _STANDALONE_ */
222 #define FT_MEM_SET( d, s, c ) ft_memset( d, s, c )
226 #define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count )
229 /* FMulDiv means `Fast MulDiv'; it is used in case where `b' is */
230 /* typically a small value and the result of a*b is known to fit into */
232 #define FMulDiv( a, b, c ) ( (a) * (b) / (c) )
234 /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */
235 /* for clipping computations. It simply uses the FT_MulDiv() function */
236 /* defined in `ftcalc.h'. */
237 #define SMulDiv FT_MulDiv
239 /* The rasterizer is a very general purpose component; please leave */
240 /* the following redefinitions there (you never know your target */
252 #define NULL (void*)0
264 #define MaxBezier 32 /* The maximum number of stacked Bezier curves. */
265 /* Setting this constant to more than 32 is a */
266 /* pure waste of space. */
268 #define Pixel_Bits 6 /* fractional bits of *input* coordinates */
271 /*************************************************************************/
272 /*************************************************************************/
274 /** SIMPLE TYPE DECLARATIONS **/
276 /*************************************************************************/
277 /*************************************************************************/
280 typedef unsigned int UInt
;
282 typedef unsigned short UShort
, *PUShort
;
283 typedef long Long
, *PLong
;
284 typedef unsigned long ULong
;
286 typedef unsigned char Byte
, *PByte
;
290 typedef union Alignment_
296 } Alignment
, *PAlignment
;
299 typedef struct TPoint_
316 /* States of each line, arc, and profile */
317 typedef enum TStates_
327 typedef struct TProfile_ TProfile
;
328 typedef TProfile
* PProfile
;
332 FT_F26Dot6 X
; /* current coordinate during sweep */
333 PProfile link
; /* link to next profile - various purpose */
334 PLong offset
; /* start of profile's data in render pool */
335 int flow
; /* Profile orientation: Asc/Descending */
336 long height
; /* profile's height in scanlines */
337 long start
; /* profile's starting scanline */
339 unsigned countL
; /* number of lines to step before this */
340 /* profile becomes drawable */
342 PProfile next
; /* next profile in same contour, used */
343 /* during drop-out control */
346 typedef PProfile TProfileList
;
347 typedef PProfile
* PProfileList
;
350 /* Simple record used to implement a stack of bands, required */
351 /* by the sub-banding mechanism */
352 typedef struct TBand_
354 Short y_min
; /* band's minimum */
355 Short y_max
; /* band's maximum */
360 #define AlignProfileSize \
361 ( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( long ) )
364 #ifdef FT_STATIC_RASTER
367 #define RAS_ARGS /* void */
368 #define RAS_ARG /* void */
370 #define RAS_VARS /* void */
371 #define RAS_VAR /* void */
373 #define FT_UNUSED_RASTER do ; while ( 0 )
376 #else /* FT_STATIC_RASTER */
379 #define RAS_ARGS PWorker worker,
380 #define RAS_ARG PWorker worker
382 #define RAS_VARS worker,
383 #define RAS_VAR worker
385 #define FT_UNUSED_RASTER FT_UNUSED( worker )
388 #endif /* FT_STATIC_RASTER */
391 typedef struct TWorker_ TWorker
, *PWorker
;
394 /* prototypes used for sweep function dispatch */
396 Function_Sweep_Init( RAS_ARGS Short
* min
,
400 Function_Sweep_Span( RAS_ARGS Short y
,
407 Function_Sweep_Step( RAS_ARG
);
410 /* NOTE: These operations are only valid on 2's complement processors */
412 #define FLOOR( x ) ( (x) & -ras.precision )
413 #define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision )
414 #define TRUNC( x ) ( (signed long)(x) >> ras.precision_bits )
415 #define FRAC( x ) ( (x) & ( ras.precision - 1 ) )
416 #define SCALED( x ) ( ( (x) << ras.scale_shift ) - ras.precision_half )
418 /* Note that I have moved the location of some fields in the */
419 /* structure to ensure that the most used variables are used */
420 /* at the top. Thus, their offset can be coded with less */
421 /* opcodes, and it results in a smaller executable. */
425 Int precision_bits
; /* precision related variables */
431 Int precision_jitter
;
433 Int scale_shift
; /* == precision_shift for bitmaps */
434 /* == precision_shift+1 for pixmaps */
436 PLong buff
; /* The profiles buffer */
437 PLong sizeBuff
; /* Render pool size */
438 PLong maxBuff
; /* Profiles buffer size */
439 PLong top
; /* Current cursor in buffer */
443 Int numTurns
; /* number of Y-turns in outline */
445 TPoint
* arc
; /* current Bezier arc pointer */
447 UShort bWidth
; /* target bitmap width */
448 PByte bTarget
; /* target bitmap buffer */
449 PByte gTarget
; /* target pixmap buffer */
451 Long lastX
, lastY
, minY
, maxY
;
453 UShort num_Profs
; /* current number of profiles */
455 Bool fresh
; /* signals a fresh new profile which */
456 /* 'start' field must be completed */
457 Bool joint
; /* signals that the last arc ended */
458 /* exactly on a scanline. Allows */
459 /* removal of doublets */
460 PProfile cProfile
; /* current profile */
461 PProfile fProfile
; /* head of linked list of profiles */
462 PProfile gProfile
; /* contour's first profile in case */
465 TStates state
; /* rendering state */
467 FT_Bitmap target
; /* description of target bit/pixmap */
470 Long traceOfs
; /* current offset in target bitmap */
471 Long traceG
; /* current offset in target pixmap */
473 Short traceIncr
; /* sweep's increment in target bitmap */
475 Short gray_min_x
; /* current min x during gray rendering */
476 Short gray_max_x
; /* current max x during gray rendering */
478 /* dispatch variables */
480 Function_Sweep_Init
* Proc_Sweep_Init
;
481 Function_Sweep_Span
* Proc_Sweep_Span
;
482 Function_Sweep_Span
* Proc_Sweep_Drop
;
483 Function_Sweep_Step
* Proc_Sweep_Step
;
485 Byte dropOutControl
; /* current drop_out control method */
487 Bool second_pass
; /* indicates whether a horizontal pass */
488 /* should be performed to control */
489 /* drop-out accurately when calling */
490 /* Render_Glyph. Note that there is */
491 /* no horizontal pass during gray */
494 TPoint arcs
[3 * MaxBezier
+ 1]; /* The Bezier stack */
496 TBand band_stack
[16]; /* band stack used for sub-banding */
497 Int band_top
; /* band stack top */
499 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
503 Byte gray_lines
[RASTER_GRAY_LINES
];
504 /* Intermediate table used to render the */
505 /* graylevels pixmaps. */
506 /* gray_lines is a buffer holding two */
507 /* monochrome scanlines */
509 Short gray_width
; /* width in bytes of one monochrome */
510 /* intermediate scanline of gray_lines. */
511 /* Each gray pixel takes 2 bits long there */
513 /* The gray_lines must hold 2 lines, thus with size */
514 /* in bytes of at least `gray_width*2'. */
516 #endif /* FT_RASTER_ANTI_ALIASING */
521 typedef struct TRaster_
532 #ifdef FT_STATIC_RASTER
534 static TWorker cur_ras
;
539 #define ras (*worker)
541 #endif /* FT_STATIC_RASTER */
544 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
546 static const char count_table
[256] =
548 0 , 1 , 1 , 2 , 1 , 2 , 2 , 3 , 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4,
549 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,
550 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,
551 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
552 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,
553 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
554 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
555 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,
556 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,
557 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
558 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
559 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,
560 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
561 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,
562 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,
563 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 , 5 , 6 , 6 , 7 , 6 , 7 , 7 , 8 };
565 #endif /* FT_RASTER_OPTION_ANTI_ALIASING */
569 /*************************************************************************/
570 /*************************************************************************/
572 /** PROFILES COMPUTATION **/
574 /*************************************************************************/
575 /*************************************************************************/
578 /*************************************************************************/
581 /* Set_High_Precision */
584 /* Sets precision variables according to param flag. */
587 /* High :: Set to True for high precision (typically for ppem < 18), */
588 /* false otherwise. */
591 Set_High_Precision( RAS_ARGS Int High
)
595 ras
.precision_bits
= 10;
596 ras
.precision_step
= 128;
597 ras
.precision_jitter
= 24;
601 ras
.precision_bits
= 6;
602 ras
.precision_step
= 32;
603 ras
.precision_jitter
= 2;
606 FT_TRACE6(( "Set_High_Precision(%s)\n", High
? "true" : "false" ));
608 ras
.precision
= 1 << ras
.precision_bits
;
609 ras
.precision_half
= ras
.precision
/ 2;
610 ras
.precision_shift
= ras
.precision_bits
- Pixel_Bits
;
611 ras
.precision_mask
= -ras
.precision
;
615 /*************************************************************************/
621 /* Creates a new profile in the render pool. */
624 /* aState :: The state/orientation of the new profile. */
627 /* SUCCESS on success. FAILURE in case of overflow or of incoherent */
631 New_Profile( RAS_ARGS TStates aState
)
635 ras
.cProfile
= (PProfile
)ras
.top
;
636 ras
.fProfile
= ras
.cProfile
;
637 ras
.top
+= AlignProfileSize
;
640 if ( ras
.top
>= ras
.maxBuff
)
642 ras
.error
= Raster_Err_Overflow
;
648 case Ascending_State
:
649 ras
.cProfile
->flow
= Flow_Up
;
650 FT_TRACE6(( "New ascending profile = %lx\n", (long)ras
.cProfile
));
653 case Descending_State
:
654 ras
.cProfile
->flow
= Flow_Down
;
655 FT_TRACE6(( "New descending profile = %lx\n", (long)ras
.cProfile
));
659 FT_ERROR(( "New_Profile: invalid profile direction!\n" ));
660 ras
.error
= Raster_Err_Invalid
;
664 ras
.cProfile
->start
= 0;
665 ras
.cProfile
->height
= 0;
666 ras
.cProfile
->offset
= ras
.top
;
667 ras
.cProfile
->link
= (PProfile
)0;
668 ras
.cProfile
->next
= (PProfile
)0;
671 ras
.gProfile
= ras
.cProfile
;
681 /*************************************************************************/
687 /* Finalizes the current profile. */
690 /* SUCCESS on success. FAILURE in case of overflow or incoherency. */
693 End_Profile( RAS_ARG
)
699 h
= (Long
)( ras
.top
- ras
.cProfile
->offset
);
703 FT_ERROR(( "End_Profile: negative height encountered!\n" ));
704 ras
.error
= Raster_Err_Neg_Height
;
710 FT_TRACE6(( "Ending profile %lx, start = %ld, height = %ld\n",
711 (long)ras
.cProfile
, ras
.cProfile
->start
, h
));
713 oldProfile
= ras
.cProfile
;
714 ras
.cProfile
->height
= h
;
715 ras
.cProfile
= (PProfile
)ras
.top
;
717 ras
.top
+= AlignProfileSize
;
719 ras
.cProfile
->height
= 0;
720 ras
.cProfile
->offset
= ras
.top
;
721 oldProfile
->next
= ras
.cProfile
;
725 if ( ras
.top
>= ras
.maxBuff
)
727 FT_TRACE1(( "overflow in End_Profile\n" ));
728 ras
.error
= Raster_Err_Overflow
;
738 /*************************************************************************/
744 /* Inserts a salient into the sorted list placed on top of the render */
748 /* New y scanline position. */
751 /* SUCCESS on success. FAILURE in case of overflow. */
754 Insert_Y_Turn( RAS_ARGS Int y
)
760 n
= ras
.numTurns
- 1;
761 y_turns
= ras
.sizeBuff
- ras
.numTurns
;
763 /* look for first y value that is <= */
764 while ( n
>= 0 && y
< y_turns
[n
] )
767 /* if it is <, simply insert it, ignore if == */
768 if ( n
>= 0 && y
> y_turns
[n
] )
771 y2
= (Int
)y_turns
[n
];
780 if ( ras
.maxBuff
<= ras
.top
)
782 ras
.error
= Raster_Err_Overflow
;
786 ras
.sizeBuff
[-ras
.numTurns
] = y
;
793 /*************************************************************************/
796 /* Finalize_Profile_Table */
799 /* Adjusts all links in the profiles list. */
802 /* SUCCESS on success. FAILURE in case of overflow. */
805 Finalize_Profile_Table( RAS_ARG
)
820 p
->link
= (PProfile
)( p
->offset
+ p
->height
);
827 bottom
= (Int
)( p
->start
- p
->height
+ 1 );
830 p
->offset
+= p
->height
- 1;
835 bottom
= (Int
)p
->start
;
836 top
= (Int
)( p
->start
+ p
->height
- 1 );
839 if ( Insert_Y_Turn( RAS_VARS bottom
) ||
840 Insert_Y_Turn( RAS_VARS top
+ 1 ) )
854 /*************************************************************************/
860 /* Subdivides one conic Bezier into two joint sub-arcs in the Bezier */
864 /* None (subdivided Bezier is taken from the top of the stack). */
867 /* This routine is the `beef' of this component. It is _the_ inner */
868 /* loop that should be optimized to hell to get the best performance. */
871 Split_Conic( TPoint
* base
)
876 base
[4].x
= base
[2].x
;
878 a
= base
[3].x
= ( base
[2].x
+ b
) / 2;
879 b
= base
[1].x
= ( base
[0].x
+ b
) / 2;
880 base
[2].x
= ( a
+ b
) / 2;
882 base
[4].y
= base
[2].y
;
884 a
= base
[3].y
= ( base
[2].y
+ b
) / 2;
885 b
= base
[1].y
= ( base
[0].y
+ b
) / 2;
886 base
[2].y
= ( a
+ b
) / 2;
888 /* hand optimized. gcc doesn't seem to be too good at common */
889 /* expression substitution and instruction scheduling ;-) */
893 /*************************************************************************/
899 /* Subdivides a third-order Bezier arc into two joint sub-arcs in the */
903 /* This routine is the `beef' of the component. It is one of _the_ */
904 /* inner loops that should be optimized like hell to get the best */
908 Split_Cubic( TPoint
* base
)
913 base
[6].x
= base
[3].x
;
916 base
[1].x
= a
= ( base
[0].x
+ c
+ 1 ) >> 1;
917 base
[5].x
= b
= ( base
[3].x
+ d
+ 1 ) >> 1;
918 c
= ( c
+ d
+ 1 ) >> 1;
919 base
[2].x
= a
= ( a
+ c
+ 1 ) >> 1;
920 base
[4].x
= b
= ( b
+ c
+ 1 ) >> 1;
921 base
[3].x
= ( a
+ b
+ 1 ) >> 1;
923 base
[6].y
= base
[3].y
;
926 base
[1].y
= a
= ( base
[0].y
+ c
+ 1 ) >> 1;
927 base
[5].y
= b
= ( base
[3].y
+ d
+ 1 ) >> 1;
928 c
= ( c
+ d
+ 1 ) >> 1;
929 base
[2].y
= a
= ( a
+ c
+ 1 ) >> 1;
930 base
[4].y
= b
= ( b
+ c
+ 1 ) >> 1;
931 base
[3].y
= ( a
+ b
+ 1 ) >> 1;
935 /*************************************************************************/
941 /* Computes the x-coordinates of an ascending line segment and stores */
942 /* them in the render pool. */
945 /* x1 :: The x-coordinate of the segment's start point. */
947 /* y1 :: The y-coordinate of the segment's start point. */
949 /* x2 :: The x-coordinate of the segment's end point. */
951 /* y2 :: The y-coordinate of the segment's end point. */
953 /* miny :: A lower vertical clipping bound value. */
955 /* maxy :: An upper vertical clipping bound value. */
958 /* SUCCESS on success, FAILURE on render pool overflow. */
961 Line_Up( RAS_ARGS Long x1
,
969 Int e1
, e2
, f1
, f2
, size
; /* XXX: is `Short' sufficient? */
978 if ( Dy
<= 0 || y2
< miny
|| y1
> maxy
)
983 /* Take care: miny-y1 can be a very large value; we use */
984 /* a slow MulDiv function to avoid clipping bugs */
985 x1
+= SMulDiv( Dx
, miny
- y1
, Dy
);
986 e1
= (Int
)TRUNC( miny
);
991 e1
= (Int
)TRUNC( y1
);
992 f1
= (Int
)FRAC( y1
);
997 /* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */
998 e2
= (Int
)TRUNC( maxy
);
1003 e2
= (Int
)TRUNC( y2
);
1004 f2
= (Int
)FRAC( y2
);
1013 x1
+= FMulDiv( Dx
, ras
.precision
- f1
, Dy
);
1024 ras
.joint
= (char)( f2
== 0 );
1028 ras
.cProfile
->start
= e1
;
1033 if ( ras
.top
+ size
>= ras
.maxBuff
)
1035 ras
.error
= Raster_Err_Overflow
;
1041 Ix
= ( ras
.precision
* Dx
) / Dy
;
1042 Rx
= ( ras
.precision
* Dx
) % Dy
;
1047 Ix
= -( ( ras
.precision
* -Dx
) / Dy
);
1048 Rx
= ( ras
.precision
* -Dx
) % Dy
;
1074 /*************************************************************************/
1080 /* Computes the x-coordinates of an descending line segment and */
1081 /* stores them in the render pool. */
1084 /* x1 :: The x-coordinate of the segment's start point. */
1086 /* y1 :: The y-coordinate of the segment's start point. */
1088 /* x2 :: The x-coordinate of the segment's end point. */
1090 /* y2 :: The y-coordinate of the segment's end point. */
1092 /* miny :: A lower vertical clipping bound value. */
1094 /* maxy :: An upper vertical clipping bound value. */
1097 /* SUCCESS on success, FAILURE on render pool overflow. */
1100 Line_Down( RAS_ARGS Long x1
,
1112 result
= Line_Up( RAS_VARS x1
, -y1
, x2
, -y2
, -maxy
, -miny
);
1114 if ( fresh
&& !ras
.fresh
)
1115 ras
.cProfile
->start
= -ras
.cProfile
->start
;
1121 /* A function type describing the functions used to split Bezier arcs */
1122 typedef void (*TSplitter
)( TPoint
* base
);
1125 /*************************************************************************/
1131 /* Computes the x-coordinates of an ascending Bezier arc and stores */
1132 /* them in the render pool. */
1135 /* degree :: The degree of the Bezier arc (either 2 or 3). */
1137 /* splitter :: The function to split Bezier arcs. */
1139 /* miny :: A lower vertical clipping bound value. */
1141 /* maxy :: An upper vertical clipping bound value. */
1144 /* SUCCESS on success, FAILURE on render pool overflow. */
1147 Bezier_Up( RAS_ARGS Int degree
,
1152 Long y1
, y2
, e
, e2
, e0
;
1166 if ( y2
< miny
|| y1
> maxy
)
1181 f1
= (Short
)( FRAC( y1
) );
1192 *top
++ = arc
[degree
].x
;
1200 ras
.cProfile
->start
= TRUNC( e0
);
1207 if ( ( top
+ TRUNC( e2
- e
) + 1 ) >= ras
.maxBuff
)
1210 ras
.error
= Raster_Err_Overflow
;
1216 while ( arc
>= start_arc
&& e
<= e2
)
1225 if ( y2
- y1
>= ras
.precision_step
)
1232 *top
++ = arc
[degree
].x
+ FMulDiv( arc
[0].x
-arc
[degree
].x
,
1258 /*************************************************************************/
1264 /* Computes the x-coordinates of an descending Bezier arc and stores */
1265 /* them in the render pool. */
1268 /* degree :: The degree of the Bezier arc (either 2 or 3). */
1270 /* splitter :: The function to split Bezier arcs. */
1272 /* miny :: A lower vertical clipping bound value. */
1274 /* maxy :: An upper vertical clipping bound value. */
1277 /* SUCCESS on success, FAILURE on render pool overflow. */
1280 Bezier_Down( RAS_ARGS Int degree
,
1285 TPoint
* arc
= ras
.arc
;
1289 arc
[0].y
= -arc
[0].y
;
1290 arc
[1].y
= -arc
[1].y
;
1291 arc
[2].y
= -arc
[2].y
;
1293 arc
[3].y
= -arc
[3].y
;
1297 result
= Bezier_Up( RAS_VARS degree
, splitter
, -maxy
, -miny
);
1299 if ( fresh
&& !ras
.fresh
)
1300 ras
.cProfile
->start
= -ras
.cProfile
->start
;
1302 arc
[0].y
= -arc
[0].y
;
1307 /*************************************************************************/
1313 /* Injects a new line segment and adjusts Profiles list. */
1316 /* x :: The x-coordinate of the segment's end point (its start point */
1317 /* is stored in `lastX'). */
1319 /* y :: The y-coordinate of the segment's end point (its start point */
1320 /* is stored in `lastY'). */
1323 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1327 Line_To( RAS_ARGS Long x
,
1330 /* First, detect a change of direction */
1332 switch ( ras
.state
)
1335 if ( y
> ras
.lastY
)
1337 if ( New_Profile( RAS_VARS Ascending_State
) )
1342 if ( y
< ras
.lastY
)
1343 if ( New_Profile( RAS_VARS Descending_State
) )
1348 case Ascending_State
:
1349 if ( y
< ras
.lastY
)
1351 if ( End_Profile( RAS_VAR
) ||
1352 New_Profile( RAS_VARS Descending_State
) )
1357 case Descending_State
:
1358 if ( y
> ras
.lastY
)
1360 if ( End_Profile( RAS_VAR
) ||
1361 New_Profile( RAS_VARS Ascending_State
) )
1370 /* Then compute the lines */
1372 switch ( ras
.state
)
1374 case Ascending_State
:
1375 if ( Line_Up( RAS_VARS ras
.lastX
, ras
.lastY
,
1376 x
, y
, ras
.minY
, ras
.maxY
) )
1380 case Descending_State
:
1381 if ( Line_Down( RAS_VARS ras
.lastX
, ras
.lastY
,
1382 x
, y
, ras
.minY
, ras
.maxY
) )
1397 /*************************************************************************/
1403 /* Injects a new conic arc and adjusts the profile list. */
1406 /* cx :: The x-coordinate of the arc's new control point. */
1408 /* cy :: The y-coordinate of the arc's new control point. */
1410 /* x :: The x-coordinate of the arc's end point (its start point is */
1411 /* stored in `lastX'). */
1413 /* y :: The y-coordinate of the arc's end point (its start point is */
1414 /* stored in `lastY'). */
1417 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1421 Conic_To( RAS_ARGS Long cx
,
1426 Long y1
, y2
, y3
, x3
, ymin
, ymax
;
1431 ras
.arc
[2].x
= ras
.lastX
;
1432 ras
.arc
[2].y
= ras
.lastY
;
1433 ras
.arc
[1].x
= cx
; ras
.arc
[1].y
= cy
;
1434 ras
.arc
[0].x
= x
; ras
.arc
[0].y
= y
;
1443 /* first, categorize the Bezier arc */
1456 if ( y2
< ymin
|| y2
> ymax
)
1458 /* this arc has no given direction, split it! */
1459 Split_Conic( ras
.arc
);
1462 else if ( y1
== y3
)
1464 /* this arc is flat, ignore it and pop it from the Bezier stack */
1469 /* the arc is y-monotonous, either ascending or descending */
1470 /* detect a change of direction */
1471 state_bez
= y1
< y3
? Ascending_State
: Descending_State
;
1472 if ( ras
.state
!= state_bez
)
1474 /* finalize current profile if any */
1475 if ( ras
.state
!= Unknown_State
&&
1476 End_Profile( RAS_VAR
) )
1479 /* create a new profile */
1480 if ( New_Profile( RAS_VARS state_bez
) )
1484 /* now call the appropriate routine */
1485 if ( state_bez
== Ascending_State
)
1487 if ( Bezier_Up( RAS_VARS
2, Split_Conic
, ras
.minY
, ras
.maxY
) )
1491 if ( Bezier_Down( RAS_VARS
2, Split_Conic
, ras
.minY
, ras
.maxY
) )
1495 } while ( ras
.arc
>= ras
.arcs
);
1507 /*************************************************************************/
1513 /* Injects a new cubic arc and adjusts the profile list. */
1516 /* cx1 :: The x-coordinate of the arc's first new control point. */
1518 /* cy1 :: The y-coordinate of the arc's first new control point. */
1520 /* cx2 :: The x-coordinate of the arc's second new control point. */
1522 /* cy2 :: The y-coordinate of the arc's second new control point. */
1524 /* x :: The x-coordinate of the arc's end point (its start point is */
1525 /* stored in `lastX'). */
1527 /* y :: The y-coordinate of the arc's end point (its start point is */
1528 /* stored in `lastY'). */
1531 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1535 Cubic_To( RAS_ARGS Long cx1
,
1542 Long y1
, y2
, y3
, y4
, x4
, ymin1
, ymax1
, ymin2
, ymax2
;
1547 ras
.arc
[3].x
= ras
.lastX
;
1548 ras
.arc
[3].y
= ras
.lastY
;
1549 ras
.arc
[2].x
= cx1
; ras
.arc
[2].y
= cy1
;
1550 ras
.arc
[1].x
= cx2
; ras
.arc
[1].y
= cy2
;
1551 ras
.arc
[0].x
= x
; ras
.arc
[0].y
= y
;
1561 /* first, categorize the Bezier arc */
1585 if ( ymin2
< ymin1
|| ymax2
> ymax1
)
1587 /* this arc has no given direction, split it! */
1588 Split_Cubic( ras
.arc
);
1591 else if ( y1
== y4
)
1593 /* this arc is flat, ignore it and pop it from the Bezier stack */
1598 state_bez
= ( y1
<= y4
) ? Ascending_State
: Descending_State
;
1600 /* detect a change of direction */
1601 if ( ras
.state
!= state_bez
)
1603 if ( ras
.state
!= Unknown_State
&&
1604 End_Profile( RAS_VAR
) )
1607 if ( New_Profile( RAS_VARS state_bez
) )
1611 /* compute intersections */
1612 if ( state_bez
== Ascending_State
)
1614 if ( Bezier_Up( RAS_VARS
3, Split_Cubic
, ras
.minY
, ras
.maxY
) )
1618 if ( Bezier_Down( RAS_VARS
3, Split_Cubic
, ras
.minY
, ras
.maxY
) )
1622 } while ( ras
.arc
>= ras
.arcs
);
1635 #define SWAP_( x, y ) do \
1645 /*************************************************************************/
1648 /* Decompose_Curve */
1651 /* Scans the outline arrays in order to emit individual segments and */
1652 /* Beziers by calling Line_To() and Bezier_To(). It handles all */
1653 /* weird cases, like when the first point is off the curve, or when */
1654 /* there are simply no `on' points in the contour! */
1657 /* first :: The index of the first point in the contour. */
1659 /* last :: The index of the last point in the contour. */
1661 /* flipped :: If set, flip the direction of the curve. */
1664 /* SUCCESS on success, FAILURE on error. */
1667 Decompose_Curve( RAS_ARGS UShort first
,
1672 FT_Vector v_control
;
1680 unsigned tag
; /* current point's state */
1683 points
= ras
.outline
.points
;
1684 limit
= points
+ last
;
1686 v_start
.x
= SCALED( points
[first
].x
);
1687 v_start
.y
= SCALED( points
[first
].y
);
1688 v_last
.x
= SCALED( points
[last
].x
);
1689 v_last
.y
= SCALED( points
[last
].y
);
1693 SWAP_( v_start
.x
, v_start
.y
);
1694 SWAP_( v_last
.x
, v_last
.y
);
1697 v_control
= v_start
;
1699 point
= points
+ first
;
1700 tags
= ras
.outline
.tags
+ first
;
1701 tag
= FT_CURVE_TAG( tags
[0] );
1703 /* A contour cannot start with a cubic control point! */
1704 if ( tag
== FT_CURVE_TAG_CUBIC
)
1705 goto Invalid_Outline
;
1707 /* check first point to determine origin */
1708 if ( tag
== FT_CURVE_TAG_CONIC
)
1710 /* first point is conic control. Yes, this happens. */
1711 if ( FT_CURVE_TAG( ras
.outline
.tags
[last
] ) == FT_CURVE_TAG_ON
)
1713 /* start at last point if it is on the curve */
1719 /* if both first and last points are conic, */
1720 /* start at their middle and record its position */
1722 v_start
.x
= ( v_start
.x
+ v_last
.x
) / 2;
1723 v_start
.y
= ( v_start
.y
+ v_last
.y
) / 2;
1731 ras
.lastX
= v_start
.x
;
1732 ras
.lastY
= v_start
.y
;
1734 while ( point
< limit
)
1739 tag
= FT_CURVE_TAG( tags
[0] );
1743 case FT_CURVE_TAG_ON
: /* emit a single line_to */
1748 x
= SCALED( point
->x
);
1749 y
= SCALED( point
->y
);
1753 if ( Line_To( RAS_VARS x
, y
) )
1758 case FT_CURVE_TAG_CONIC
: /* consume conic arcs */
1759 v_control
.x
= SCALED( point
[0].x
);
1760 v_control
.y
= SCALED( point
[0].y
);
1763 SWAP_( v_control
.x
, v_control
.y
);
1766 if ( point
< limit
)
1774 tag
= FT_CURVE_TAG( tags
[0] );
1776 x
= SCALED( point
[0].x
);
1777 y
= SCALED( point
[0].y
);
1782 if ( tag
== FT_CURVE_TAG_ON
)
1784 if ( Conic_To( RAS_VARS v_control
.x
, v_control
.y
, x
, y
) )
1789 if ( tag
!= FT_CURVE_TAG_CONIC
)
1790 goto Invalid_Outline
;
1792 v_middle
.x
= ( v_control
.x
+ x
) / 2;
1793 v_middle
.y
= ( v_control
.y
+ y
) / 2;
1795 if ( Conic_To( RAS_VARS v_control
.x
, v_control
.y
,
1796 v_middle
.x
, v_middle
.y
) )
1805 if ( Conic_To( RAS_VARS v_control
.x
, v_control
.y
,
1806 v_start
.x
, v_start
.y
) )
1811 default: /* FT_CURVE_TAG_CUBIC */
1813 Long x1
, y1
, x2
, y2
, x3
, y3
;
1816 if ( point
+ 1 > limit
||
1817 FT_CURVE_TAG( tags
[1] ) != FT_CURVE_TAG_CUBIC
)
1818 goto Invalid_Outline
;
1823 x1
= SCALED( point
[-2].x
);
1824 y1
= SCALED( point
[-2].y
);
1825 x2
= SCALED( point
[-1].x
);
1826 y2
= SCALED( point
[-1].y
);
1827 x3
= SCALED( point
[ 0].x
);
1828 y3
= SCALED( point
[ 0].y
);
1837 if ( point
<= limit
)
1839 if ( Cubic_To( RAS_VARS x1
, y1
, x2
, y2
, x3
, y3
) )
1844 if ( Cubic_To( RAS_VARS x1
, y1
, x2
, y2
, v_start
.x
, v_start
.y
) )
1851 /* close the contour with a line segment */
1852 if ( Line_To( RAS_VARS v_start
.x
, v_start
.y
) )
1859 ras
.error
= Raster_Err_Invalid
;
1866 /*************************************************************************/
1872 /* Converts a glyph into a series of segments and arcs and makes a */
1873 /* profiles list with them. */
1876 /* flipped :: If set, flip the direction of curve. */
1879 /* SUCCESS on success, FAILURE if any error was encountered during */
1883 Convert_Glyph( RAS_ARGS
int flipped
)
1888 PProfile lastProfile
;
1891 ras
.fProfile
= NULL
;
1895 ras
.maxBuff
= ras
.sizeBuff
- AlignProfileSize
;
1899 ras
.cProfile
= (PProfile
)ras
.top
;
1900 ras
.cProfile
->offset
= ras
.top
;
1905 for ( i
= 0; i
< ras
.outline
.n_contours
; i
++ )
1907 ras
.state
= Unknown_State
;
1908 ras
.gProfile
= NULL
;
1910 if ( Decompose_Curve( RAS_VARS (unsigned short)start
,
1911 ras
.outline
.contours
[i
],
1915 start
= ras
.outline
.contours
[i
] + 1;
1917 /* We must now see whether the extreme arcs join or not */
1918 if ( FRAC( ras
.lastY
) == 0 &&
1919 ras
.lastY
>= ras
.minY
&&
1920 ras
.lastY
<= ras
.maxY
)
1921 if ( ras
.gProfile
&& ras
.gProfile
->flow
== ras
.cProfile
->flow
)
1923 /* Note that ras.gProfile can be nil if the contour was too small */
1926 lastProfile
= ras
.cProfile
;
1927 if ( End_Profile( RAS_VAR
) )
1930 /* close the `next profile in contour' linked list */
1932 lastProfile
->next
= ras
.gProfile
;
1935 if ( Finalize_Profile_Table( RAS_VAR
) )
1938 return (Bool
)( ras
.top
< ras
.maxBuff
? SUCCESS
: FAILURE
);
1942 /*************************************************************************/
1943 /*************************************************************************/
1945 /** SCAN-LINE SWEEPS AND DRAWING **/
1947 /*************************************************************************/
1948 /*************************************************************************/
1951 /*************************************************************************/
1955 /* Initializes an empty linked list. */
1958 Init_Linked( TProfileList
* l
)
1964 /*************************************************************************/
1968 /* Inserts a new profile in a linked list. */
1971 InsNew( PProfileList list
,
1974 PProfile
*old
, current
;
1984 if ( x
< current
->X
)
1986 old
= ¤t
->link
;
1990 profile
->link
= current
;
1995 /*************************************************************************/
1999 /* Removes an old profile from a linked list. */
2002 DelOld( PProfileList list
,
2005 PProfile
*old
, current
;
2013 if ( current
== profile
)
2015 *old
= current
->link
;
2019 old
= ¤t
->link
;
2023 /* we should never get there, unless the profile was not part of */
2028 /*************************************************************************/
2032 /* Sorts a trace list. In 95%, the list is already sorted. We need */
2033 /* an algorithm which is fast in this case. Bubble sort is enough */
2037 Sort( PProfileList list
)
2039 PProfile
*old
, current
, next
;
2042 /* First, set the new X coordinate of each profile */
2046 current
->X
= *current
->offset
;
2047 current
->offset
+= current
->flow
;
2049 current
= current
->link
;
2052 /* Then sort them */
2059 next
= current
->link
;
2063 if ( current
->X
<= next
->X
)
2065 old
= ¤t
->link
;
2074 current
->link
= next
->link
;
2075 next
->link
= current
;
2081 next
= current
->link
;
2086 /*************************************************************************/
2088 /* Vertical Sweep Procedure Set */
2090 /* These four routines are used during the vertical black/white sweep */
2091 /* phase by the generic Draw_Sweep() function. */
2093 /*************************************************************************/
2096 Vertical_Sweep_Init( RAS_ARGS Short
* min
,
2099 Long pitch
= ras
.target
.pitch
;
2104 ras
.traceIncr
= (Short
)-pitch
;
2105 ras
.traceOfs
= -*min
* pitch
;
2107 ras
.traceOfs
+= ( ras
.target
.rows
- 1 ) * pitch
;
2115 Vertical_Sweep_Span( RAS_ARGS Short y
,
2131 /* Drop-out control */
2133 e1
= TRUNC( CEILING( x1
) );
2135 if ( x2
- x1
- ras
.precision
<= ras
.precision_jitter
)
2138 e2
= TRUNC( FLOOR( x2
) );
2140 if ( e2
>= 0 && e1
< ras
.bWidth
)
2144 if ( e2
>= ras
.bWidth
)
2145 e2
= ras
.bWidth
- 1;
2147 c1
= (Short
)( e1
>> 3 );
2148 c2
= (Short
)( e2
>> 3 );
2150 f1
= (Byte
) ( 0xFF >> ( e1
& 7 ) );
2151 f2
= (Byte
) ~( 0x7F >> ( e2
& 7 ) );
2153 if ( ras
.gray_min_x
> c1
)
2154 ras
.gray_min_x
= (short)c1
;
2155 if ( ras
.gray_max_x
< c2
)
2156 ras
.gray_max_x
= (short)c2
;
2158 target
= ras
.bTarget
+ ras
.traceOfs
+ c1
;
2165 /* memset() is slower than the following code on many platforms. */
2166 /* This is due to the fact that, in the vast majority of cases, */
2167 /* the span length in bytes is relatively small. */
2177 *target
|= ( f1
& f2
);
2183 Vertical_Sweep_Drop( RAS_ARGS Short y
,
2193 /* Drop-out control */
2199 /* +-------------+---------------------+------------+ */
2203 /* pixel contour contour pixel */
2206 /* drop-out mode scan conversion rules (as defined in OpenType) */
2207 /* --------------------------------------------------------------- */
2211 /* 3 same as mode 2 */
2214 /* 6, 7 same as mode 2 */
2222 if ( e1
== e2
+ ras
.precision
)
2224 switch ( ras
.dropOutControl
)
2226 case 0: /* simple drop-outs including stubs */
2230 case 4: /* smart drop-outs including stubs */
2231 pxl
= FLOOR( ( x1
+ x2
+ 1 ) / 2 + ras
.precision_half
);
2234 case 1: /* simple drop-outs excluding stubs */
2235 case 5: /* smart drop-outs excluding stubs */
2237 /* Drop-out Control Rules #4 and #6 */
2239 /* The spec is not very clear regarding those rules. It */
2240 /* presents a method that is way too costly to implement */
2241 /* while the general idea seems to get rid of `stubs'. */
2243 /* Here, we only get rid of stubs recognized if: */
2247 /* - P_Left and P_Right are in the same contour */
2248 /* - P_Right is the successor of P_Left in that contour */
2249 /* - y is the top of P_Left and P_Right */
2253 /* - P_Left and P_Right are in the same contour */
2254 /* - P_Left is the successor of P_Right in that contour */
2255 /* - y is the bottom of P_Left */
2258 /* FIXXXME: uncommenting this line solves the disappearing */
2259 /* bit problem in the `7' of verdana 10pts, but */
2260 /* makes a new one in the `C' of arial 14pts */
2262 if ( x2
- x1
< ras
.precision_half
)
2265 /* upper stub test */
2266 if ( left
->next
== right
&& left
->height
<= 0 )
2269 /* lower stub test */
2270 if ( right
->next
== left
&& left
->start
== y
)
2274 if ( ras
.dropOutControl
== 1 )
2277 pxl
= FLOOR( ( x1
+ x2
+ 1 ) / 2 + ras
.precision_half
);
2280 default: /* modes 2, 3, 6, 7 */
2281 return; /* no drop-out control */
2284 /* check that the other pixel isn't set */
2285 e1
= pxl
== e1
? e2
: e1
;
2289 c1
= (Short
)( e1
>> 3 );
2290 f1
= (Short
)( e1
& 7 );
2292 if ( e1
>= 0 && e1
< ras
.bWidth
&&
2293 ras
.bTarget
[ras
.traceOfs
+ c1
] & ( 0x80 >> f1
) )
2302 if ( e1
>= 0 && e1
< ras
.bWidth
)
2304 c1
= (Short
)( e1
>> 3 );
2305 f1
= (Short
)( e1
& 7 );
2307 if ( ras
.gray_min_x
> c1
)
2308 ras
.gray_min_x
= c1
;
2309 if ( ras
.gray_max_x
< c1
)
2310 ras
.gray_max_x
= c1
;
2312 ras
.bTarget
[ras
.traceOfs
+ c1
] |= (char)( 0x80 >> f1
);
2318 Vertical_Sweep_Step( RAS_ARG
)
2320 ras
.traceOfs
+= ras
.traceIncr
;
2324 /***********************************************************************/
2326 /* Horizontal Sweep Procedure Set */
2328 /* These four routines are used during the horizontal black/white */
2329 /* sweep phase by the generic Draw_Sweep() function. */
2331 /***********************************************************************/
2334 Horizontal_Sweep_Init( RAS_ARGS Short
* min
,
2337 /* nothing, really */
2345 Horizontal_Sweep_Span( RAS_ARGS Short y
,
2359 if ( x2
- x1
< ras
.precision
)
2366 bits
= ras
.bTarget
+ ( y
>> 3 );
2367 f1
= (Byte
)( 0x80 >> ( y
& 7 ) );
2371 if ( e1
>= 0 && e1
< ras
.target
.rows
)
2376 p
= bits
- e1
*ras
.target
.pitch
;
2377 if ( ras
.target
.pitch
> 0 )
2378 p
+= ( ras
.target
.rows
- 1 ) * ras
.target
.pitch
;
2388 Horizontal_Sweep_Drop( RAS_ARGS Short y
,
2399 /* During the horizontal sweep, we only take care of drop-outs */
2401 /* e1 + <-- pixel center */
2403 /* x1 ---+--> <-- contour */
2406 /* x2 <--+--- <-- contour */
2409 /* e2 + <-- pixel center */
2417 if ( e1
== e2
+ ras
.precision
)
2419 switch ( ras
.dropOutControl
)
2421 case 0: /* simple drop-outs including stubs */
2425 case 4: /* smart drop-outs including stubs */
2426 pxl
= FLOOR( ( x1
+ x2
+ 1 ) / 2 + ras
.precision_half
);
2429 case 1: /* simple drop-outs excluding stubs */
2430 case 5: /* smart drop-outs excluding stubs */
2431 /* see Vertical_Sweep_Drop for details */
2433 /* rightmost stub test */
2434 if ( left
->next
== right
&& left
->height
<= 0 )
2437 /* leftmost stub test */
2438 if ( right
->next
== left
&& left
->start
== y
)
2441 if ( ras
.dropOutControl
== 1 )
2444 pxl
= FLOOR( ( x1
+ x2
+ 1 ) / 2 + ras
.precision_half
);
2447 default: /* modes 2, 3, 6, 7 */
2448 return; /* no drop-out control */
2451 /* check that the other pixel isn't set */
2452 e1
= pxl
== e1
? e2
: e1
;
2456 bits
= ras
.bTarget
+ ( y
>> 3 );
2457 f1
= (Byte
)( 0x80 >> ( y
& 7 ) );
2459 bits
-= e1
* ras
.target
.pitch
;
2460 if ( ras
.target
.pitch
> 0 )
2461 bits
+= ( ras
.target
.rows
- 1 ) * ras
.target
.pitch
;
2464 e1
< ras
.target
.rows
&&
2472 bits
= ras
.bTarget
+ ( y
>> 3 );
2473 f1
= (Byte
)( 0x80 >> ( y
& 7 ) );
2477 if ( e1
>= 0 && e1
< ras
.target
.rows
)
2479 bits
-= e1
* ras
.target
.pitch
;
2480 if ( ras
.target
.pitch
> 0 )
2481 bits
+= ( ras
.target
.rows
- 1 ) * ras
.target
.pitch
;
2489 Horizontal_Sweep_Step( RAS_ARG
)
2491 /* Nothing, really */
2496 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
2499 /*************************************************************************/
2501 /* Vertical Gray Sweep Procedure Set */
2503 /* These two routines are used during the vertical gray-levels sweep */
2504 /* phase by the generic Draw_Sweep() function. */
2508 /* - The target pixmap's width *must* be a multiple of 4. */
2510 /* - You have to use the function Vertical_Sweep_Span() for the gray */
2513 /*************************************************************************/
2516 Vertical_Gray_Sweep_Init( RAS_ARGS Short
* min
,
2519 Long pitch
, byte_len
;
2523 *max
= ( *max
+ 3 ) & -2;
2526 pitch
= ras
.target
.pitch
;
2528 ras
.traceIncr
= (Short
)byte_len
;
2529 ras
.traceG
= ( *min
/ 2 ) * byte_len
;
2533 ras
.traceG
+= ( ras
.target
.rows
- 1 ) * pitch
;
2534 byte_len
= -byte_len
;
2537 ras
.gray_min_x
= (Short
)byte_len
;
2538 ras
.gray_max_x
= -(Short
)byte_len
;
2543 Vertical_Gray_Sweep_Step( RAS_ARG
)
2546 PByte pix
, bit
, bit2
;
2547 char* count
= (char*)count_table
;
2551 ras
.traceOfs
+= ras
.gray_width
;
2553 if ( ras
.traceOfs
> ras
.gray_width
)
2555 pix
= ras
.gTarget
+ ras
.traceG
+ ras
.gray_min_x
* 4;
2558 if ( ras
.gray_max_x
>= 0 )
2560 Long last_pixel
= ras
.target
.width
- 1;
2561 Int last_cell
= last_pixel
>> 2;
2562 Int last_bit
= last_pixel
& 3;
2566 if ( ras
.gray_max_x
>= last_cell
&& last_bit
!= 3 )
2568 ras
.gray_max_x
= last_cell
- 1;
2572 if ( ras
.gray_min_x
< 0 )
2575 bit
= ras
.bTarget
+ ras
.gray_min_x
;
2576 bit2
= bit
+ ras
.gray_width
;
2578 c1
= ras
.gray_max_x
- ras
.gray_min_x
;
2582 c2
= count
[*bit
] + count
[*bit2
];
2586 pix
[0] = grays
[(c2
>> 12) & 0x000F];
2587 pix
[1] = grays
[(c2
>> 8 ) & 0x000F];
2588 pix
[2] = grays
[(c2
>> 4 ) & 0x000F];
2589 pix
[3] = grays
[ c2
& 0x000F];
2603 c2
= count
[*bit
] + count
[*bit2
];
2609 pix
[2] = grays
[(c2
>> 4 ) & 0x000F];
2611 pix
[1] = grays
[(c2
>> 8 ) & 0x000F];
2613 pix
[0] = grays
[(c2
>> 12) & 0x000F];
2623 ras
.traceG
+= ras
.traceIncr
;
2625 ras
.gray_min_x
= 32000;
2626 ras
.gray_max_x
= -32000;
2632 Horizontal_Gray_Sweep_Span( RAS_ARGS Short y
,
2638 /* nothing, really */
2649 Horizontal_Gray_Sweep_Drop( RAS_ARGS Short y
,
2660 /* During the horizontal sweep, we only take care of drop-outs */
2667 if ( e1
== e2
+ ras
.precision
)
2669 switch ( ras
.dropOutControl
)
2671 case 0: /* simple drop-outs including stubs */
2675 case 4: /* smart drop-outs including stubs */
2676 e1
= FLOOR( ( x1
+ x2
+ 1 ) / 2 + ras
.precision_half
);
2679 case 1: /* simple drop-outs excluding stubs */
2680 case 5: /* smart drop-outs excluding stubs */
2681 /* see Vertical_Sweep_Drop for details */
2683 /* rightmost stub test */
2684 if ( left
->next
== right
&& left
->height
<= 0 )
2687 /* leftmost stub test */
2688 if ( right
->next
== left
&& left
->start
== y
)
2691 if ( ras
.dropOutControl
== 1 )
2694 e1
= FLOOR( ( x1
+ x2
+ 1 ) / 2 + ras
.precision_half
);
2698 default: /* modes 2, 3, 6, 7 */
2699 return; /* no drop-out control */
2708 if ( x2
- x1
>= ras
.precision_half
)
2709 color
= ras
.grays
[2];
2711 color
= ras
.grays
[1];
2713 e1
= TRUNC( e1
) / 2;
2714 if ( e1
< ras
.target
.rows
)
2716 pixel
= ras
.gTarget
- e1
* ras
.target
.pitch
+ y
/ 2;
2717 if ( ras
.target
.pitch
> 0 )
2718 pixel
+= ( ras
.target
.rows
- 1 ) * ras
.target
.pitch
;
2720 if ( pixel
[0] == ras
.grays
[0] )
2727 #endif /* FT_RASTER_OPTION_ANTI_ALIASING */
2730 /*************************************************************************/
2732 /* Generic Sweep Drawing routine */
2734 /*************************************************************************/
2737 Draw_Sweep( RAS_ARG
)
2739 Short y
, y_change
, y_height
;
2741 PProfile P
, Q
, P_Left
, P_Right
;
2743 Short min_Y
, max_Y
, top
, bottom
, dropouts
;
2745 Long x1
, x2
, xs
, e1
, e2
;
2747 TProfileList waiting
;
2748 TProfileList draw_left
, draw_right
;
2751 /* Init empty linked lists */
2753 Init_Linked( &waiting
);
2755 Init_Linked( &draw_left
);
2756 Init_Linked( &draw_right
);
2758 /* first, compute min and max Y */
2761 max_Y
= (Short
)TRUNC( ras
.minY
);
2762 min_Y
= (Short
)TRUNC( ras
.maxY
);
2768 bottom
= (Short
)P
->start
;
2769 top
= (Short
)( P
->start
+ P
->height
- 1 );
2771 if ( min_Y
> bottom
)
2777 InsNew( &waiting
, P
);
2782 /* Check the Y-turns */
2783 if ( ras
.numTurns
== 0 )
2785 ras
.error
= Raster_Err_Invalid
;
2789 /* Now inits the sweep */
2791 ras
.Proc_Sweep_Init( RAS_VARS
&min_Y
, &max_Y
);
2793 /* Then compute the distance of each profile from min_Y */
2799 P
->countL
= (UShort
)( P
->start
- min_Y
);
2808 if ( ras
.numTurns
> 0 &&
2809 ras
.sizeBuff
[-ras
.numTurns
] == min_Y
)
2812 while ( ras
.numTurns
> 0 )
2814 /* look in the waiting list for new activations */
2821 P
->countL
-= y_height
;
2822 if ( P
->countL
== 0 )
2824 DelOld( &waiting
, P
);
2829 InsNew( &draw_left
, P
);
2833 InsNew( &draw_right
, P
);
2841 /* Sort the drawing lists */
2844 Sort( &draw_right
);
2846 y_change
= (Short
)ras
.sizeBuff
[-ras
.numTurns
--];
2847 y_height
= (Short
)( y_change
- y
);
2849 while ( y
< y_change
)
2856 P_Right
= draw_right
;
2870 if ( x2
- x1
<= ras
.precision
)
2875 if ( e1
> e2
|| e2
== e1
+ ras
.precision
)
2877 if ( ras
.dropOutControl
!= 2 )
2879 /* a drop out was detected */
2884 /* mark profile for drop-out processing */
2893 ras
.Proc_Sweep_Span( RAS_VARS y
, x1
, x2
, P_Left
, P_Right
);
2897 P_Left
= P_Left
->link
;
2898 P_Right
= P_Right
->link
;
2901 /* now perform the dropouts _after_ the span drawing -- */
2902 /* drop-outs processing has been moved out of the loop */
2903 /* for performance tuning */
2909 ras
.Proc_Sweep_Step( RAS_VAR
);
2916 Sort( &draw_right
);
2920 /* Now finalize the profiles that needs it */
2926 if ( P
->height
== 0 )
2927 DelOld( &draw_left
, P
);
2935 if ( P
->height
== 0 )
2936 DelOld( &draw_right
, P
);
2941 /* for gray-scaling, flushes the bitmap scanline cache */
2942 while ( y
<= max_Y
)
2944 ras
.Proc_Sweep_Step( RAS_VAR
);
2953 P_Right
= draw_right
;
2957 if ( P_Left
->countL
)
2961 dropouts
--; /* -- this is useful when debugging only */
2963 ras
.Proc_Sweep_Drop( RAS_VARS y
,
2970 P_Left
= P_Left
->link
;
2971 P_Right
= P_Right
->link
;
2978 /*************************************************************************/
2981 /* Render_Single_Pass */
2984 /* Performs one sweep with sub-banding. */
2987 /* flipped :: If set, flip the direction of the outline. */
2990 /* Renderer error code. */
2993 Render_Single_Pass( RAS_ARGS Bool flipped
)
2998 while ( ras
.band_top
>= 0 )
3000 ras
.maxY
= (Long
)ras
.band_stack
[ras
.band_top
].y_max
* ras
.precision
;
3001 ras
.minY
= (Long
)ras
.band_stack
[ras
.band_top
].y_min
* ras
.precision
;
3005 ras
.error
= Raster_Err_None
;
3007 if ( Convert_Glyph( RAS_VARS flipped
) )
3009 if ( ras
.error
!= Raster_Err_Overflow
)
3012 ras
.error
= Raster_Err_None
;
3017 ClearBand( RAS_VARS
TRUNC( ras
.minY
), TRUNC( ras
.maxY
) );
3020 i
= ras
.band_stack
[ras
.band_top
].y_min
;
3021 j
= ras
.band_stack
[ras
.band_top
].y_max
;
3023 k
= (Short
)( ( i
+ j
) / 2 );
3025 if ( ras
.band_top
>= 7 || k
< i
)
3028 ras
.error
= Raster_Err_Invalid
;
3033 ras
.band_stack
[ras
.band_top
+ 1].y_min
= k
;
3034 ras
.band_stack
[ras
.band_top
+ 1].y_max
= j
;
3036 ras
.band_stack
[ras
.band_top
].y_max
= (Short
)( k
- 1 );
3043 if ( Draw_Sweep( RAS_VAR
) )
3053 /*************************************************************************/
3059 /* Renders a glyph in a bitmap. Sub-banding if needed. */
3062 /* FreeType error code. 0 means success. */
3064 FT_LOCAL_DEF( FT_Error
)
3065 Render_Glyph( RAS_ARG
)
3070 Set_High_Precision( RAS_VARS ras
.outline
.flags
&
3071 FT_OUTLINE_HIGH_PRECISION
);
3072 ras
.scale_shift
= ras
.precision_shift
;
3074 if ( ras
.outline
.flags
& FT_OUTLINE_IGNORE_DROPOUTS
)
3075 ras
.dropOutControl
= 2;
3078 if ( ras
.outline
.flags
& FT_OUTLINE_SMART_DROPOUTS
)
3079 ras
.dropOutControl
= 4;
3081 ras
.dropOutControl
= 0;
3083 if ( !( ras
.outline
.flags
& FT_OUTLINE_INCLUDE_STUBS
) )
3084 ras
.dropOutControl
+= 1;
3087 ras
.second_pass
= (FT_Byte
)( !( ras
.outline
.flags
&
3088 FT_OUTLINE_SINGLE_PASS
) );
3090 /* Vertical Sweep */
3091 ras
.Proc_Sweep_Init
= Vertical_Sweep_Init
;
3092 ras
.Proc_Sweep_Span
= Vertical_Sweep_Span
;
3093 ras
.Proc_Sweep_Drop
= Vertical_Sweep_Drop
;
3094 ras
.Proc_Sweep_Step
= Vertical_Sweep_Step
;
3097 ras
.band_stack
[0].y_min
= 0;
3098 ras
.band_stack
[0].y_max
= (short)( ras
.target
.rows
- 1 );
3100 ras
.bWidth
= (unsigned short)ras
.target
.width
;
3101 ras
.bTarget
= (Byte
*)ras
.target
.buffer
;
3103 if ( ( error
= Render_Single_Pass( RAS_VARS
0 ) ) != 0 )
3106 /* Horizontal Sweep */
3107 if ( ras
.second_pass
&& ras
.dropOutControl
!= 2 )
3109 ras
.Proc_Sweep_Init
= Horizontal_Sweep_Init
;
3110 ras
.Proc_Sweep_Span
= Horizontal_Sweep_Span
;
3111 ras
.Proc_Sweep_Drop
= Horizontal_Sweep_Drop
;
3112 ras
.Proc_Sweep_Step
= Horizontal_Sweep_Step
;
3115 ras
.band_stack
[0].y_min
= 0;
3116 ras
.band_stack
[0].y_max
= (short)( ras
.target
.width
- 1 );
3118 if ( ( error
= Render_Single_Pass( RAS_VARS
1 ) ) != 0 )
3122 return Raster_Err_None
;
3126 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3129 /*************************************************************************/
3132 /* Render_Gray_Glyph */
3135 /* Renders a glyph with grayscaling. Sub-banding if needed. */
3138 /* FreeType error code. 0 means success. */
3140 FT_LOCAL_DEF( FT_Error
)
3141 Render_Gray_Glyph( RAS_ARG
)
3147 Set_High_Precision( RAS_VARS ras
.outline
.flags
&
3148 FT_OUTLINE_HIGH_PRECISION
);
3149 ras
.scale_shift
= ras
.precision_shift
+ 1;
3151 if ( ras
.outline
.flags
& FT_OUTLINE_IGNORE_DROPOUTS
)
3152 ras
.dropOutControl
= 2;
3155 if ( ras
.outline
.flags
& FT_OUTLINE_SMART_DROPOUTS
)
3156 ras
.dropOutControl
= 4;
3158 ras
.dropOutControl
= 0;
3160 if ( !( ras
.outline
.flags
& FT_OUTLINE_INCLUDE_STUBS
) )
3161 ras
.dropOutControl
+= 1;
3164 ras
.second_pass
= !( ras
.outline
.flags
& FT_OUTLINE_SINGLE_PASS
);
3166 /* Vertical Sweep */
3169 ras
.band_stack
[0].y_min
= 0;
3170 ras
.band_stack
[0].y_max
= 2 * ras
.target
.rows
- 1;
3172 ras
.bWidth
= ras
.gray_width
;
3173 pixel_width
= 2 * ( ( ras
.target
.width
+ 3 ) >> 2 );
3175 if ( ras
.bWidth
> pixel_width
)
3176 ras
.bWidth
= pixel_width
;
3178 ras
.bWidth
= ras
.bWidth
* 8;
3179 ras
.bTarget
= (Byte
*)ras
.gray_lines
;
3180 ras
.gTarget
= (Byte
*)ras
.target
.buffer
;
3182 ras
.Proc_Sweep_Init
= Vertical_Gray_Sweep_Init
;
3183 ras
.Proc_Sweep_Span
= Vertical_Sweep_Span
;
3184 ras
.Proc_Sweep_Drop
= Vertical_Sweep_Drop
;
3185 ras
.Proc_Sweep_Step
= Vertical_Gray_Sweep_Step
;
3187 error
= Render_Single_Pass( RAS_VARS
0 );
3191 /* Horizontal Sweep */
3192 if ( ras
.second_pass
&& ras
.dropOutControl
!= 2 )
3194 ras
.Proc_Sweep_Init
= Horizontal_Sweep_Init
;
3195 ras
.Proc_Sweep_Span
= Horizontal_Gray_Sweep_Span
;
3196 ras
.Proc_Sweep_Drop
= Horizontal_Gray_Sweep_Drop
;
3197 ras
.Proc_Sweep_Step
= Horizontal_Sweep_Step
;
3200 ras
.band_stack
[0].y_min
= 0;
3201 ras
.band_stack
[0].y_max
= ras
.target
.width
* 2 - 1;
3203 error
= Render_Single_Pass( RAS_VARS
1 );
3208 return Raster_Err_None
;
3211 #else /* !FT_RASTER_OPTION_ANTI_ALIASING */
3213 FT_LOCAL_DEF( FT_Error
)
3214 Render_Gray_Glyph( RAS_ARG
)
3218 return Raster_Err_Unsupported
;
3221 #endif /* !FT_RASTER_OPTION_ANTI_ALIASING */
3225 ft_black_init( PRaster raster
)
3227 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3231 /* set default 5-levels gray palette */
3232 for ( n
= 0; n
< 5; n
++ )
3233 raster
->grays
[n
] = n
* 255 / 4;
3235 raster
->gray_width
= RASTER_GRAY_LINES
/ 2;
3238 FT_UNUSED( raster
);
3243 /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
3244 /**** a static object. *****/
3251 ft_black_new( void* memory
,
3252 FT_Raster
*araster
)
3254 static TRaster the_raster
;
3257 *araster
= (FT_Raster
)&the_raster
;
3258 FT_MEM_ZERO( &the_raster
, sizeof ( the_raster
) );
3259 ft_black_init( &the_raster
);
3266 ft_black_done( FT_Raster raster
)
3269 FT_UNUSED( raster
);
3273 #else /* _STANDALONE_ */
3277 ft_black_new( FT_Memory memory
,
3285 if ( !FT_NEW( raster
) )
3287 raster
->memory
= memory
;
3288 ft_black_init( raster
);
3298 ft_black_done( PRaster raster
)
3300 FT_Memory memory
= (FT_Memory
)raster
->memory
;
3305 #endif /* _STANDALONE_ */
3309 ft_black_reset( PRaster raster
,
3315 if ( pool_base
&& pool_size
>= (long)sizeof(TWorker
) + 2048 )
3317 PWorker worker
= (PWorker
)pool_base
;
3320 raster
->buffer
= pool_base
+ ( (sizeof ( *worker
) + 7 ) & ~7 );
3321 raster
->buffer_size
= ( ( pool_base
+ pool_size
) -
3322 (char*)raster
->buffer
) / sizeof ( Long
);
3323 raster
->worker
= worker
;
3327 raster
->buffer
= NULL
;
3328 raster
->buffer_size
= 0;
3329 raster
->worker
= NULL
;
3336 ft_black_set_mode( PRaster raster
,
3338 const char* palette
)
3340 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3342 if ( mode
== FT_MAKE_TAG( 'p', 'a', 'l', '5' ) )
3344 /* set 5-levels gray palette */
3345 raster
->grays
[0] = palette
[0];
3346 raster
->grays
[1] = palette
[1];
3347 raster
->grays
[2] = palette
[2];
3348 raster
->grays
[3] = palette
[3];
3349 raster
->grays
[4] = palette
[4];
3354 FT_UNUSED( raster
);
3356 FT_UNUSED( palette
);
3363 ft_black_render( PRaster raster
,
3364 const FT_Raster_Params
* params
)
3366 const FT_Outline
* outline
= (const FT_Outline
*)params
->source
;
3367 const FT_Bitmap
* target_map
= params
->target
;
3371 if ( !raster
|| !raster
->buffer
|| !raster
->buffer_size
)
3372 return Raster_Err_Not_Ini
;
3375 return Raster_Err_Invalid
;
3377 /* return immediately if the outline is empty */
3378 if ( outline
->n_points
== 0 || outline
->n_contours
<= 0 )
3379 return Raster_Err_None
;
3381 if ( !outline
->contours
|| !outline
->points
)
3382 return Raster_Err_Invalid
;
3384 if ( outline
->n_points
!=
3385 outline
->contours
[outline
->n_contours
- 1] + 1 )
3386 return Raster_Err_Invalid
;
3388 worker
= raster
->worker
;
3390 /* this version of the raster does not support direct rendering, sorry */
3391 if ( params
->flags
& FT_RASTER_FLAG_DIRECT
)
3392 return Raster_Err_Unsupported
;
3395 return Raster_Err_Invalid
;
3398 if ( !target_map
->width
|| !target_map
->rows
)
3399 return Raster_Err_None
;
3401 if ( !target_map
->buffer
)
3402 return Raster_Err_Invalid
;
3404 ras
.outline
= *outline
;
3405 ras
.target
= *target_map
;
3407 worker
->buff
= (PLong
) raster
->buffer
;
3408 worker
->sizeBuff
= worker
->buff
+
3409 raster
->buffer_size
/ sizeof ( Long
);
3410 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3411 worker
->grays
= raster
->grays
;
3412 worker
->gray_width
= raster
->gray_width
;
3415 return ( ( params
->flags
& FT_RASTER_FLAG_AA
)
3416 ? Render_Gray_Glyph( RAS_VAR
)
3417 : Render_Glyph( RAS_VAR
) );
3421 const FT_Raster_Funcs ft_standard_raster
=
3423 FT_GLYPH_FORMAT_OUTLINE
,
3424 (FT_Raster_New_Func
) ft_black_new
,
3425 (FT_Raster_Reset_Func
) ft_black_reset
,
3426 (FT_Raster_Set_Mode_Func
)ft_black_set_mode
,
3427 (FT_Raster_Render_Func
) ft_black_render
,
3428 (FT_Raster_Done_Func
) ft_black_done