1/****************************************************************************
2 *
3 * ftraster.c
4 *
5 * The FreeType glyph rasterizer (body).
6 *
7 * Copyright (C) 1996-2019 by
8 * David Turner, Robert Wilhelm, and Werner Lemberg.
9 *
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.
15 *
16 */
17
18 /**************************************************************************
19 *
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
24 *
25 * - copy `src/raster/ftraster.c' (this file) to your current directory
26 *
27 * - copy `include/freetype/ftimage.h' and `src/raster/ftmisc.h' to your
28 * current directory
29 *
30 * - compile `ftraster' with the STANDALONE_ macro defined, as in
31 *
32 * cc -c -DSTANDALONE_ ftraster.c
33 *
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'.
37 *
38 * See the comments and documentation in the file `ftimage.h' for more
39 * details on how the raster works.
40 *
41 */
42
43
44 /**************************************************************************
45 *
46 * This is a rewrite of the FreeType 1.x scan-line converter
47 *
48 */
49
50#ifdef STANDALONE_
51
52 /* The size in bytes of the render pool used by the scan-line converter */
53 /* to do all of its work. */
54#define FT_RENDER_POOL_SIZE 16384L
55
56#define FT_CONFIG_STANDARD_LIBRARY_H <stdlib.h>
57
58#include <string.h> /* for memset */
59
60#include "ftmisc.h"
61#include "ftimage.h"
62
63#else /* !STANDALONE_ */
64
65#include <ft2build.h>
66#include "ftraster.h"
67#include FT_INTERNAL_CALC_H /* for FT_MulDiv and FT_MulDiv_No_Round */
68#include FT_OUTLINE_H /* for FT_Outline_Get_CBox */
69
70#endif /* !STANDALONE_ */
71
72
73 /**************************************************************************
74 *
75 * A simple technical note on how the raster works
76 * -----------------------------------------------
77 *
78 * Converting an outline into a bitmap is achieved in several steps:
79 *
80 * 1 - Decomposing the outline into successive `profiles'. Each
81 * profile is simply an array of scanline intersections on a given
82 * dimension. A profile's main attributes are
83 *
84 * o its scanline position boundaries, i.e. `Ymin' and `Ymax'
85 *
86 * o an array of intersection coordinates for each scanline
87 * between `Ymin' and `Ymax'
88 *
89 * o a direction, indicating whether it was built going `up' or
90 * `down', as this is very important for filling rules
91 *
92 * o its drop-out mode
93 *
94 * 2 - Sweeping the target map's scanlines in order to compute segment
95 * `spans' which are then filled. Additionally, this pass
96 * performs drop-out control.
97 *
98 * The outline data is parsed during step 1 only. The profiles are
99 * built from the bottom of the render pool, used as a stack. The
100 * following graphics shows the profile list under construction:
101 *
102 * __________________________________________________________ _ _
103 * | | | | |
104 * | profile | coordinates for | profile | coordinates for |-->
105 * | 1 | profile 1 | 2 | profile 2 |-->
106 * |_________|_________________|_________|_________________|__ _ _
107 *
108 * ^ ^
109 * | |
110 * start of render pool top
111 *
112 * The top of the profile stack is kept in the `top' variable.
113 *
114 * As you can see, a profile record is pushed on top of the render
115 * pool, which is then followed by its coordinates/intersections. If
116 * a change of direction is detected in the outline, a new profile is
117 * generated until the end of the outline.
118 *
119 * Note that when all profiles have been generated, the function
120 * Finalize_Profile_Table() is used to record, for each profile, its
121 * bottom-most scanline as well as the scanline above its upmost
122 * boundary. These positions are called `y-turns' because they (sort
123 * of) correspond to local extrema. They are stored in a sorted list
124 * built from the top of the render pool as a downwards stack:
125 *
126 * _ _ _______________________________________
127 * | |
128 * <--| sorted list of |
129 * <--| extrema scanlines |
130 * _ _ __________________|____________________|
131 *
132 * ^ ^
133 * | |
134 * maxBuff sizeBuff = end of pool
135 *
136 * This list is later used during the sweep phase in order to
137 * optimize performance (see technical note on the sweep below).
138 *
139 * Of course, the raster detects whether the two stacks collide and
140 * handles the situation properly.
141 *
142 */
143
144
145 /*************************************************************************/
146 /*************************************************************************/
147 /** **/
148 /** CONFIGURATION MACROS **/
149 /** **/
150 /*************************************************************************/
151 /*************************************************************************/
152
153 /* define DEBUG_RASTER if you want to compile a debugging version */
154/* #define DEBUG_RASTER */
155
156
157 /*************************************************************************/
158 /*************************************************************************/
159 /** **/
160 /** OTHER MACROS (do not change) **/
161 /** **/
162 /*************************************************************************/
163 /*************************************************************************/
164
165 /**************************************************************************
166 *
167 * The macro FT_COMPONENT is used in trace mode. It is an implicit
168 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
169 * messages during execution.
170 */
171#undef FT_COMPONENT
172#define FT_COMPONENT raster
173
174
175#ifdef STANDALONE_
176
177 /* Auxiliary macros for token concatenation. */
178#define FT_ERR_XCAT( x, y ) x ## y
179#define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y )
180
181 /* This macro is used to indicate that a function parameter is unused. */
182 /* Its purpose is simply to reduce compiler warnings. Note also that */
183 /* simply defining it as `(void)x' doesn't avoid warnings with certain */
184 /* ANSI compilers (e.g. LCC). */
185#define FT_UNUSED( x ) (x) = (x)
186
187 /* Disable the tracing mechanism for simplicity -- developers can */
188 /* activate it easily by redefining these macros. */
189#ifndef FT_ERROR
190#define FT_ERROR( x ) do { } while ( 0 ) /* nothing */
191#endif
192
193#ifndef FT_TRACE
194#define FT_TRACE( x ) do { } while ( 0 ) /* nothing */
195#define FT_TRACE1( x ) do { } while ( 0 ) /* nothing */
196#define FT_TRACE6( x ) do { } while ( 0 ) /* nothing */
197#define FT_TRACE7( x ) do { } while ( 0 ) /* nothing */
198#endif
199
200#ifndef FT_THROW
201#define FT_THROW( e ) FT_ERR_CAT( Raster_Err_, e )
202#endif
203
204#define Raster_Err_None 0
205#define Raster_Err_Not_Ini -1
206#define Raster_Err_Overflow -2
207#define Raster_Err_Neg_Height -3
208#define Raster_Err_Invalid -4
209#define Raster_Err_Unsupported -5
210
211#define ft_memset memset
212
213#define FT_DEFINE_RASTER_FUNCS( class_, glyph_format_, raster_new_, \
214 raster_reset_, raster_set_mode_, \
215 raster_render_, raster_done_ ) \
216 const FT_Raster_Funcs class_ = \
217 { \
218 glyph_format_, \
219 raster_new_, \
220 raster_reset_, \
221 raster_set_mode_, \
222 raster_render_, \
223 raster_done_ \
224 };
225
226#else /* !STANDALONE_ */
227
228
229#include FT_INTERNAL_OBJECTS_H
230#include FT_INTERNAL_DEBUG_H /* for FT_TRACE, FT_ERROR, and FT_THROW */
231
232#include "rasterrs.h"
233
234#define Raster_Err_None FT_Err_Ok
235#define Raster_Err_Not_Ini Raster_Err_Raster_Uninitialized
236#define Raster_Err_Overflow Raster_Err_Raster_Overflow
237#define Raster_Err_Neg_Height Raster_Err_Raster_Negative_Height
238#define Raster_Err_Invalid Raster_Err_Invalid_Outline
239#define Raster_Err_Unsupported Raster_Err_Cannot_Render_Glyph
240
241
242#endif /* !STANDALONE_ */
243
244
245#ifndef FT_MEM_SET
246#define FT_MEM_SET( d, s, c ) ft_memset( d, s, c )
247#endif
248
249#ifndef FT_MEM_ZERO
250#define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count )
251#endif
252
253#ifndef FT_ZERO
254#define FT_ZERO( p ) FT_MEM_ZERO( p, sizeof ( *(p) ) )
255#endif
256
257 /* FMulDiv means `Fast MulDiv'; it is used in case where `b' is */
258 /* typically a small value and the result of a*b is known to fit into */
259 /* 32 bits. */
260#define FMulDiv( a, b, c ) ( (a) * (b) / (c) )
261
262 /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */
263 /* for clipping computations. It simply uses the FT_MulDiv() function */
264 /* defined in `ftcalc.h'. */
265#define SMulDiv FT_MulDiv
266#define SMulDiv_No_Round FT_MulDiv_No_Round
267
268 /* The rasterizer is a very general purpose component; please leave */
269 /* the following redefinitions there (you never know your target */
270 /* environment). */
271
272#ifndef TRUE
273#define TRUE 1
274#endif
275
276#ifndef FALSE
277#define FALSE 0
278#endif
279
280#ifndef NULL
281#define NULL (void*)0
282#endif
283
284#ifndef SUCCESS
285#define SUCCESS 0
286#endif
287
288#ifndef FAILURE
289#define FAILURE 1
290#endif
291
292
293#define MaxBezier 32 /* The maximum number of stacked Bezier curves. */
294 /* Setting this constant to more than 32 is a */
295 /* pure waste of space. */
296
297#define Pixel_Bits 6 /* fractional bits of *input* coordinates */
298
299
300 /*************************************************************************/
301 /*************************************************************************/
302 /** **/
303 /** SIMPLE TYPE DECLARATIONS **/
304 /** **/
305 /*************************************************************************/
306 /*************************************************************************/
307
308 typedef int Int;
309 typedef unsigned int UInt;
310 typedef short Short;
311 typedef unsigned short UShort, *PUShort;
312 typedef long Long, *PLong;
313 typedef unsigned long ULong;
314
315 typedef unsigned char Byte, *PByte;
316 typedef char Bool;
317
318
319 typedef union Alignment_
320 {
321 Long l;
322 void* p;
323 void (*f)(void);
324
325 } Alignment, *PAlignment;
326
327
328 typedef struct TPoint_
329 {
330 Long x;
331 Long y;
332
333 } TPoint;
334
335
336 /* values for the `flags' bit field */
337#define Flow_Up 0x08U
338#define Overshoot_Top 0x10U
339#define Overshoot_Bottom 0x20U
340
341
342 /* States of each line, arc, and profile */
343 typedef enum TStates_
344 {
345 Unknown_State,
346 Ascending_State,
347 Descending_State,
348 Flat_State
349
350 } TStates;
351
352
353 typedef struct TProfile_ TProfile;
354 typedef TProfile* PProfile;
355
356 struct TProfile_
357 {
358 FT_F26Dot6 X; /* current coordinate during sweep */
359 PProfile link; /* link to next profile (various purposes) */
360 PLong offset; /* start of profile's data in render pool */
361 UShort flags; /* Bit 0-2: drop-out mode */
362 /* Bit 3: profile orientation (up/down) */
363 /* Bit 4: is top profile? */
364 /* Bit 5: is bottom profile? */
365 Long height; /* profile's height in scanlines */
366 Long start; /* profile's starting scanline */
367
368 Int countL; /* number of lines to step before this */
369 /* profile becomes drawable */
370
371 PProfile next; /* next profile in same contour, used */
372 /* during drop-out control */
373 };
374
375 typedef PProfile TProfileList;
376 typedef PProfile* PProfileList;
377
378
379 /* Simple record used to implement a stack of bands, required */
380 /* by the sub-banding mechanism */
381 typedef struct black_TBand_
382 {
383 Short y_min; /* band's minimum */
384 Short y_max; /* band's maximum */
385
386 } black_TBand;
387
388
389#define AlignProfileSize \
390 ( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( Long ) )
391
392
393#undef RAS_ARG
394#undef RAS_ARGS
395#undef RAS_VAR
396#undef RAS_VARS
397
398#ifdef FT_STATIC_RASTER
399
400
401#define RAS_ARGS /* void */
402#define RAS_ARG /* void */
403
404#define RAS_VARS /* void */
405#define RAS_VAR /* void */
406
407#define FT_UNUSED_RASTER do { } while ( 0 )
408
409
410#else /* !FT_STATIC_RASTER */
411
412
413#define RAS_ARGS black_PWorker worker,
414#define RAS_ARG black_PWorker worker
415
416#define RAS_VARS worker,
417#define RAS_VAR worker
418
419#define FT_UNUSED_RASTER FT_UNUSED( worker )
420
421
422#endif /* !FT_STATIC_RASTER */
423
424
425 typedef struct black_TWorker_ black_TWorker, *black_PWorker;
426
427
428 /* prototypes used for sweep function dispatch */
429 typedef void
430 Function_Sweep_Init( RAS_ARGS Short* min,
431 Short* max );
432
433 typedef void
434 Function_Sweep_Span( RAS_ARGS Short y,
435 FT_F26Dot6 x1,
436 FT_F26Dot6 x2,
437 PProfile left,
438 PProfile right );
439
440 typedef void
441 Function_Sweep_Step( RAS_ARG );
442
443
444 /* NOTE: These operations are only valid on 2's complement processors */
445#undef FLOOR
446#undef CEILING
447#undef TRUNC
448#undef SCALED
449
450#define FLOOR( x ) ( (x) & -ras.precision )
451#define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision )
452#define TRUNC( x ) ( (Long)(x) >> ras.precision_bits )
453#define FRAC( x ) ( (x) & ( ras.precision - 1 ) )
454
455 /* scale and shift grid to pixel centers */
456#define SCALED( x ) ( (x) * ras.precision_scale - ras.precision_half )
457
458#define IS_BOTTOM_OVERSHOOT( x ) \
459 (Bool)( CEILING( x ) - x >= ras.precision_half )
460#define IS_TOP_OVERSHOOT( x ) \
461 (Bool)( x - FLOOR( x ) >= ras.precision_half )
462
463#if FT_RENDER_POOL_SIZE > 2048
464#define FT_MAX_BLACK_POOL ( FT_RENDER_POOL_SIZE / sizeof ( Long ) )
465#else
466#define FT_MAX_BLACK_POOL ( 2048 / sizeof ( Long ) )
467#endif
468
469 /* The most used variables are positioned at the top of the structure. */
470 /* Thus, their offset can be coded with less opcodes, resulting in a */
471 /* smaller executable. */
472
473 struct black_TWorker_
474 {
475 Int precision_bits; /* precision related variables */
476 Int precision;
477 Int precision_half;
478 Int precision_scale;
479 Int precision_step;
480 Int precision_jitter;
481
482 PLong buff; /* The profiles buffer */
483 PLong sizeBuff; /* Render pool size */
484 PLong maxBuff; /* Profiles buffer size */
485 PLong top; /* Current cursor in buffer */
486
487 FT_Error error;
488
489 Int numTurns; /* number of Y-turns in outline */
490
491 TPoint* arc; /* current Bezier arc pointer */
492
493 UShort bWidth; /* target bitmap width */
494 PByte bOrigin; /* target bitmap bottom-left origin */
495
496 Long lastX, lastY;
497 Long minY, maxY;
498
499 UShort num_Profs; /* current number of profiles */
500
501 Bool fresh; /* signals a fresh new profile which */
502 /* `start' field must be completed */
503 Bool joint; /* signals that the last arc ended */
504 /* exactly on a scanline. Allows */
505 /* removal of doublets */
506 PProfile cProfile; /* current profile */
507 PProfile fProfile; /* head of linked list of profiles */
508 PProfile gProfile; /* contour's first profile in case */
509 /* of impact */
510
511 TStates state; /* rendering state */
512
513 FT_Bitmap target; /* description of target bit/pixmap */
514 FT_Outline outline;
515
516 Long traceOfs; /* current offset in target bitmap */
517 Short traceIncr; /* sweep's increment in target bitmap */
518
519 /* dispatch variables */
520
521 Function_Sweep_Init* Proc_Sweep_Init;
522 Function_Sweep_Span* Proc_Sweep_Span;
523 Function_Sweep_Span* Proc_Sweep_Drop;
524 Function_Sweep_Step* Proc_Sweep_Step;
525
526 Byte dropOutControl; /* current drop_out control method */
527
528 Bool second_pass; /* indicates whether a horizontal pass */
529 /* should be performed to control */
530 /* drop-out accurately when calling */
531 /* Render_Glyph. */
532
533 TPoint arcs[3 * MaxBezier + 1]; /* The Bezier stack */
534
535 black_TBand band_stack[16]; /* band stack used for sub-banding */
536 Int band_top; /* band stack top */
537
538 };
539
540
541 typedef struct black_TRaster_
542 {
543 void* memory;
544
545 } black_TRaster, *black_PRaster;
546
547#ifdef FT_STATIC_RASTER
548
549 static black_TWorker cur_ras;
550#define ras cur_ras
551
552#else /* !FT_STATIC_RASTER */
553
554#define ras (*worker)
555
556#endif /* !FT_STATIC_RASTER */
557
558
559 /*************************************************************************/
560 /*************************************************************************/
561 /** **/
562 /** PROFILES COMPUTATION **/
563 /** **/
564 /*************************************************************************/
565 /*************************************************************************/
566
567
568 /**************************************************************************
569 *
570 * @Function:
571 * Set_High_Precision
572 *
573 * @Description:
574 * Set precision variables according to param flag.
575 *
576 * @Input:
577 * High ::
578 * Set to True for high precision (typically for ppem < 24),
579 * false otherwise.
580 */
581 static void
582 Set_High_Precision( RAS_ARGS Int High )
583 {
584 /*
585 * `precision_step' is used in `Bezier_Up' to decide when to split a
586 * given y-monotonous Bezier arc that crosses a scanline before
587 * approximating it as a straight segment. The default value of 32 (for
588 * low accuracy) corresponds to
589 *
590 * 32 / 64 == 0.5 pixels,
591 *
592 * while for the high accuracy case we have
593 *
594 * 256 / (1 << 12) = 0.0625 pixels.
595 *
596 * `precision_jitter' is an epsilon threshold used in
597 * `Vertical_Sweep_Span' to deal with small imperfections in the Bezier
598 * decomposition (after all, we are working with approximations only);
599 * it avoids switching on additional pixels which would cause artifacts
600 * otherwise.
601 *
602 * The value of `precision_jitter' has been determined heuristically.
603 *
604 */
605
606 if ( High )
607 {
608 ras.precision_bits = 12;
609 ras.precision_step = 256;
610 ras.precision_jitter = 30;
611 }
612 else
613 {
614 ras.precision_bits = 6;
615 ras.precision_step = 32;
616 ras.precision_jitter = 2;
617 }
618
619 FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" ));
620
621 ras.precision = 1 << ras.precision_bits;
622 ras.precision_half = ras.precision >> 1;
623 ras.precision_scale = ras.precision >> Pixel_Bits;
624 }
625
626
627 /**************************************************************************
628 *
629 * @Function:
630 * New_Profile
631 *
632 * @Description:
633 * Create a new profile in the render pool.
634 *
635 * @Input:
636 * aState ::
637 * The state/orientation of the new profile.
638 *
639 * overshoot ::
640 * Whether the profile's unrounded start position
641 * differs by at least a half pixel.
642 *
643 * @Return:
644 * SUCCESS on success. FAILURE in case of overflow or of incoherent
645 * profile.
646 */
647 static Bool
648 New_Profile( RAS_ARGS TStates aState,
649 Bool overshoot )
650 {
651 if ( !ras.fProfile )
652 {
653 ras.cProfile = (PProfile)ras.top;
654 ras.fProfile = ras.cProfile;
655 ras.top += AlignProfileSize;
656 }
657
658 if ( ras.top >= ras.maxBuff )
659 {
660 ras.error = FT_THROW( Overflow );
661 return FAILURE;
662 }
663
664 ras.cProfile->flags = 0;
665 ras.cProfile->start = 0;
666 ras.cProfile->height = 0;
667 ras.cProfile->offset = ras.top;
668 ras.cProfile->link = (PProfile)0;
669 ras.cProfile->next = (PProfile)0;
670 ras.cProfile->flags = ras.dropOutControl;
671
672 switch ( aState )
673 {
674 case Ascending_State:
675 ras.cProfile->flags |= Flow_Up;
676 if ( overshoot )
677 ras.cProfile->flags |= Overshoot_Bottom;
678
679 FT_TRACE6(( " new ascending profile = %p\n", ras.cProfile ));
680 break;
681
682 case Descending_State:
683 if ( overshoot )
684 ras.cProfile->flags |= Overshoot_Top;
685 FT_TRACE6(( " new descending profile = %p\n", ras.cProfile ));
686 break;
687
688 default:
689 FT_ERROR(( "New_Profile: invalid profile direction\n" ));
690 ras.error = FT_THROW( Invalid );
691 return FAILURE;
692 }
693
694 if ( !ras.gProfile )
695 ras.gProfile = ras.cProfile;
696
697 ras.state = aState;
698 ras.fresh = TRUE;
699 ras.joint = FALSE;
700
701 return SUCCESS;
702 }
703
704
705 /**************************************************************************
706 *
707 * @Function:
708 * End_Profile
709 *
710 * @Description:
711 * Finalize the current profile.
712 *
713 * @Input:
714 * overshoot ::
715 * Whether the profile's unrounded end position differs
716 * by at least a half pixel.
717 *
718 * @Return:
719 * SUCCESS on success. FAILURE in case of overflow or incoherency.
720 */
721 static Bool
722 End_Profile( RAS_ARGS Bool overshoot )
723 {
724 Long h;
725
726
727 h = (Long)( ras.top - ras.cProfile->offset );
728
729 if ( h < 0 )
730 {
731 FT_ERROR(( "End_Profile: negative height encountered\n" ));
732 ras.error = FT_THROW( Neg_Height );
733 return FAILURE;
734 }
735
736 if ( h > 0 )
737 {
738 PProfile oldProfile;
739
740
741 FT_TRACE6(( " ending profile %p, start = %ld, height = %ld\n",
742 ras.cProfile, ras.cProfile->start, h ));
743
744 ras.cProfile->height = h;
745 if ( overshoot )
746 {
747 if ( ras.cProfile->flags & Flow_Up )
748 ras.cProfile->flags |= Overshoot_Top;
749 else
750 ras.cProfile->flags |= Overshoot_Bottom;
751 }
752
753 oldProfile = ras.cProfile;
754 ras.cProfile = (PProfile)ras.top;
755
756 ras.top += AlignProfileSize;
757
758 ras.cProfile->height = 0;
759 ras.cProfile->offset = ras.top;
760
761 oldProfile->next = ras.cProfile;
762 ras.num_Profs++;
763 }
764
765 if ( ras.top >= ras.maxBuff )
766 {
767 FT_TRACE1(( "overflow in End_Profile\n" ));
768 ras.error = FT_THROW( Overflow );
769 return FAILURE;
770 }
771
772 ras.joint = FALSE;
773
774 return SUCCESS;
775 }
776
777
778 /**************************************************************************
779 *
780 * @Function:
781 * Insert_Y_Turn
782 *
783 * @Description:
784 * Insert a salient into the sorted list placed on top of the render
785 * pool.
786 *
787 * @Input:
788 * New y scanline position.
789 *
790 * @Return:
791 * SUCCESS on success. FAILURE in case of overflow.
792 */
793 static Bool
794 Insert_Y_Turn( RAS_ARGS Int y )
795 {
796 PLong y_turns;
797 Int n;
798
799
800 n = ras.numTurns - 1;
801 y_turns = ras.sizeBuff - ras.numTurns;
802
803 /* look for first y value that is <= */
804 while ( n >= 0 && y < y_turns[n] )
805 n--;
806
807 /* if it is <, simply insert it, ignore if == */
808 if ( n >= 0 && y > y_turns[n] )
809 do
810 {
811 Int y2 = (Int)y_turns[n];
812
813
814 y_turns[n] = y;
815 y = y2;
816 } while ( --n >= 0 );
817
818 if ( n < 0 )
819 {
820 ras.maxBuff--;
821 if ( ras.maxBuff <= ras.top )
822 {
823 ras.error = FT_THROW( Overflow );
824 return FAILURE;
825 }
826 ras.numTurns++;
827 ras.sizeBuff[-ras.numTurns] = y;
828 }
829
830 return SUCCESS;
831 }
832
833
834 /**************************************************************************
835 *
836 * @Function:
837 * Finalize_Profile_Table
838 *
839 * @Description:
840 * Adjust all links in the profiles list.
841 *
842 * @Return:
843 * SUCCESS on success. FAILURE in case of overflow.
844 */
845 static Bool
846 Finalize_Profile_Table( RAS_ARG )
847 {
848 UShort n;
849 PProfile p;
850
851
852 n = ras.num_Profs;
853 p = ras.fProfile;
854
855 if ( n > 1 && p )
856 {
857 do
858 {
859 Int bottom, top;
860
861
862 if ( n > 1 )
863 p->link = (PProfile)( p->offset + p->height );
864 else
865 p->link = NULL;
866
867 if ( p->flags & Flow_Up )
868 {
869 bottom = (Int)p->start;
870 top = (Int)( p->start + p->height - 1 );
871 }
872 else
873 {
874 bottom = (Int)( p->start - p->height + 1 );
875 top = (Int)p->start;
876 p->start = bottom;
877 p->offset += p->height - 1;
878 }
879
880 if ( Insert_Y_Turn( RAS_VARS bottom ) ||
881 Insert_Y_Turn( RAS_VARS top + 1 ) )
882 return FAILURE;
883
884 p = p->link;
885 } while ( --n );
886 }
887 else
888 ras.fProfile = NULL;
889
890 return SUCCESS;
891 }
892
893
894 /**************************************************************************
895 *
896 * @Function:
897 * Split_Conic
898 *
899 * @Description:
900 * Subdivide one conic Bezier into two joint sub-arcs in the Bezier
901 * stack.
902 *
903 * @Input:
904 * None (subdivided Bezier is taken from the top of the stack).
905 *
906 * @Note:
907 * This routine is the `beef' of this component. It is _the_ inner
908 * loop that should be optimized to hell to get the best performance.
909 */
910 static void
911 Split_Conic( TPoint* base )
912 {
913 Long a, b;
914
915
916 base[4].x = base[2].x;
917 b = base[1].x;
918 a = base[3].x = ( base[2].x + b ) / 2;
919 b = base[1].x = ( base[0].x + b ) / 2;
920 base[2].x = ( a + b ) / 2;
921
922 base[4].y = base[2].y;
923 b = base[1].y;
924 a = base[3].y = ( base[2].y + b ) / 2;
925 b = base[1].y = ( base[0].y + b ) / 2;
926 base[2].y = ( a + b ) / 2;
927
928 /* hand optimized. gcc doesn't seem to be too good at common */
929 /* expression substitution and instruction scheduling ;-) */
930 }
931
932
933 /**************************************************************************
934 *
935 * @Function:
936 * Split_Cubic
937 *
938 * @Description:
939 * Subdivide a third-order Bezier arc into two joint sub-arcs in the
940 * Bezier stack.
941 *
942 * @Note:
943 * This routine is the `beef' of the component. It is one of _the_
944 * inner loops that should be optimized like hell to get the best
945 * performance.
946 */
947 static void
948 Split_Cubic( TPoint* base )
949 {
950 Long a, b, c, d;
951
952
953 base[6].x = base[3].x;
954 c = base[1].x;
955 d = base[2].x;
956 base[1].x = a = ( base[0].x + c + 1 ) >> 1;
957 base[5].x = b = ( base[3].x + d + 1 ) >> 1;
958 c = ( c + d + 1 ) >> 1;
959 base[2].x = a = ( a + c + 1 ) >> 1;
960 base[4].x = b = ( b + c + 1 ) >> 1;
961 base[3].x = ( a + b + 1 ) >> 1;
962
963 base[6].y = base[3].y;
964 c = base[1].y;
965 d = base[2].y;
966 base[1].y = a = ( base[0].y + c + 1 ) >> 1;
967 base[5].y = b = ( base[3].y + d + 1 ) >> 1;
968 c = ( c + d + 1 ) >> 1;
969 base[2].y = a = ( a + c + 1 ) >> 1;
970 base[4].y = b = ( b + c + 1 ) >> 1;
971 base[3].y = ( a + b + 1 ) >> 1;
972 }
973
974
975 /**************************************************************************
976 *
977 * @Function:
978 * Line_Up
979 *
980 * @Description:
981 * Compute the x-coordinates of an ascending line segment and store
982 * them in the render pool.
983 *
984 * @Input:
985 * x1 ::
986 * The x-coordinate of the segment's start point.
987 *
988 * y1 ::
989 * The y-coordinate of the segment's start point.
990 *
991 * x2 ::
992 * The x-coordinate of the segment's end point.
993 *
994 * y2 ::
995 * The y-coordinate of the segment's end point.
996 *
997 * miny ::
998 * A lower vertical clipping bound value.
999 *
1000 * maxy ::
1001 * An upper vertical clipping bound value.
1002 *
1003 * @Return:
1004 * SUCCESS on success, FAILURE on render pool overflow.
1005 */
1006 static Bool
1007 Line_Up( RAS_ARGS Long x1,
1008 Long y1,
1009 Long x2,
1010 Long y2,
1011 Long miny,
1012 Long maxy )
1013 {
1014 Long Dx, Dy;
1015 Int e1, e2, f1, f2, size; /* XXX: is `Short' sufficient? */
1016 Long Ix, Rx, Ax;
1017
1018 PLong top;
1019
1020
1021 Dx = x2 - x1;
1022 Dy = y2 - y1;
1023
1024 if ( Dy <= 0 || y2 < miny || y1 > maxy )
1025 return SUCCESS;
1026
1027 if ( y1 < miny )
1028 {
1029 /* Take care: miny-y1 can be a very large value; we use */
1030 /* a slow MulDiv function to avoid clipping bugs */
1031 x1 += SMulDiv( Dx, miny - y1, Dy );
1032 e1 = (Int)TRUNC( miny );
1033 f1 = 0;
1034 }
1035 else
1036 {
1037 e1 = (Int)TRUNC( y1 );
1038 f1 = (Int)FRAC( y1 );
1039 }
1040
1041 if ( y2 > maxy )
1042 {
1043 /* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */
1044 e2 = (Int)TRUNC( maxy );
1045 f2 = 0;
1046 }
1047 else
1048 {
1049 e2 = (Int)TRUNC( y2 );
1050 f2 = (Int)FRAC( y2 );
1051 }
1052
1053 if ( f1 > 0 )
1054 {
1055 if ( e1 == e2 )
1056 return SUCCESS;
1057 else
1058 {
1059 x1 += SMulDiv( Dx, ras.precision - f1, Dy );
1060 e1 += 1;
1061 }
1062 }
1063 else
1064 if ( ras.joint )
1065 {
1066 ras.top--;
1067 ras.joint = FALSE;
1068 }
1069
1070 ras.joint = (char)( f2 == 0 );
1071
1072 if ( ras.fresh )
1073 {
1074 ras.cProfile->start = e1;
1075 ras.fresh = FALSE;
1076 }
1077
1078 size = e2 - e1 + 1;
1079 if ( ras.top + size >= ras.maxBuff )
1080 {
1081 ras.error = FT_THROW( Overflow );
1082 return FAILURE;
1083 }
1084
1085 if ( Dx > 0 )
1086 {
1087 Ix = SMulDiv_No_Round( ras.precision, Dx, Dy );
1088 Rx = ( ras.precision * Dx ) % Dy;
1089 Dx = 1;
1090 }
1091 else
1092 {
1093 Ix = -SMulDiv_No_Round( ras.precision, -Dx, Dy );
1094 Rx = ( ras.precision * -Dx ) % Dy;
1095 Dx = -1;
1096 }
1097
1098 Ax = -Dy;
1099 top = ras.top;
1100
1101 while ( size > 0 )
1102 {
1103 *top++ = x1;
1104
1105 x1 += Ix;
1106 Ax += Rx;
1107 if ( Ax >= 0 )
1108 {
1109 Ax -= Dy;
1110 x1 += Dx;
1111 }
1112 size--;
1113 }
1114
1115 ras.top = top;
1116 return SUCCESS;
1117 }
1118
1119
1120 /**************************************************************************
1121 *
1122 * @Function:
1123 * Line_Down
1124 *
1125 * @Description:
1126 * Compute the x-coordinates of an descending line segment and store
1127 * them in the render pool.
1128 *
1129 * @Input:
1130 * x1 ::
1131 * The x-coordinate of the segment's start point.
1132 *
1133 * y1 ::
1134 * The y-coordinate of the segment's start point.
1135 *
1136 * x2 ::
1137 * The x-coordinate of the segment's end point.
1138 *
1139 * y2 ::
1140 * The y-coordinate of the segment's end point.
1141 *
1142 * miny ::
1143 * A lower vertical clipping bound value.
1144 *
1145 * maxy ::
1146 * An upper vertical clipping bound value.
1147 *
1148 * @Return:
1149 * SUCCESS on success, FAILURE on render pool overflow.
1150 */
1151 static Bool
1152 Line_Down( RAS_ARGS Long x1,
1153 Long y1,
1154 Long x2,
1155 Long y2,
1156 Long miny,
1157 Long maxy )
1158 {
1159 Bool result, fresh;
1160
1161
1162 fresh = ras.fresh;
1163
1164 result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );
1165
1166 if ( fresh && !ras.fresh )
1167 ras.cProfile->start = -ras.cProfile->start;
1168
1169 return result;
1170 }
1171
1172
1173 /* A function type describing the functions used to split Bezier arcs */
1174 typedef void (*TSplitter)( TPoint* base );
1175
1176
1177 /**************************************************************************
1178 *
1179 * @Function:
1180 * Bezier_Up
1181 *
1182 * @Description:
1183 * Compute the x-coordinates of an ascending Bezier arc and store
1184 * them in the render pool.
1185 *
1186 * @Input:
1187 * degree ::
1188 * The degree of the Bezier arc (either 2 or 3).
1189 *
1190 * splitter ::
1191 * The function to split Bezier arcs.
1192 *
1193 * miny ::
1194 * A lower vertical clipping bound value.
1195 *
1196 * maxy ::
1197 * An upper vertical clipping bound value.
1198 *
1199 * @Return:
1200 * SUCCESS on success, FAILURE on render pool overflow.
1201 */
1202 static Bool
1203 Bezier_Up( RAS_ARGS Int degree,
1204 TSplitter splitter,
1205 Long miny,
1206 Long maxy )
1207 {
1208 Long y1, y2, e, e2, e0;
1209 Short f1;
1210
1211 TPoint* arc;
1212 TPoint* start_arc;
1213
1214 PLong top;
1215
1216
1217 arc = ras.arc;
1218 y1 = arc[degree].y;
1219 y2 = arc[0].y;
1220 top = ras.top;
1221
1222 if ( y2 < miny || y1 > maxy )
1223 goto Fin;
1224
1225 e2 = FLOOR( y2 );
1226
1227 if ( e2 > maxy )
1228 e2 = maxy;
1229
1230 e0 = miny;
1231
1232 if ( y1 < miny )
1233 e = miny;
1234 else
1235 {
1236 e = CEILING( y1 );
1237 f1 = (Short)( FRAC( y1 ) );
1238 e0 = e;
1239
1240 if ( f1 == 0 )
1241 {
1242 if ( ras.joint )
1243 {
1244 top--;
1245 ras.joint = FALSE;
1246 }
1247
1248 *top++ = arc[degree].x;
1249
1250 e += ras.precision;
1251 }
1252 }
1253
1254 if ( ras.fresh )
1255 {
1256 ras.cProfile->start = TRUNC( e0 );
1257 ras.fresh = FALSE;
1258 }
1259
1260 if ( e2 < e )
1261 goto Fin;
1262
1263 if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff )
1264 {
1265 ras.top = top;
1266 ras.error = FT_THROW( Overflow );
1267 return FAILURE;
1268 }
1269
1270 start_arc = arc;
1271
1272 do
1273 {
1274 ras.joint = FALSE;
1275
1276 y2 = arc[0].y;
1277
1278 if ( y2 > e )
1279 {
1280 y1 = arc[degree].y;
1281 if ( y2 - y1 >= ras.precision_step )
1282 {
1283 splitter( arc );
1284 arc += degree;
1285 }
1286 else
1287 {
1288 *top++ = arc[degree].x + FMulDiv( arc[0].x - arc[degree].x,
1289 e - y1, y2 - y1 );
1290 arc -= degree;
1291 e += ras.precision;
1292 }
1293 }
1294 else
1295 {
1296 if ( y2 == e )
1297 {
1298 ras.joint = TRUE;
1299 *top++ = arc[0].x;
1300
1301 e += ras.precision;
1302 }
1303 arc -= degree;
1304 }
1305 } while ( arc >= start_arc && e <= e2 );
1306
1307 Fin:
1308 ras.top = top;
1309 ras.arc -= degree;
1310 return SUCCESS;
1311 }
1312
1313
1314 /**************************************************************************
1315 *
1316 * @Function:
1317 * Bezier_Down
1318 *
1319 * @Description:
1320 * Compute the x-coordinates of an descending Bezier arc and store
1321 * them in the render pool.
1322 *
1323 * @Input:
1324 * degree ::
1325 * The degree of the Bezier arc (either 2 or 3).
1326 *
1327 * splitter ::
1328 * The function to split Bezier arcs.
1329 *
1330 * miny ::
1331 * A lower vertical clipping bound value.
1332 *
1333 * maxy ::
1334 * An upper vertical clipping bound value.
1335 *
1336 * @Return:
1337 * SUCCESS on success, FAILURE on render pool overflow.
1338 */
1339 static Bool
1340 Bezier_Down( RAS_ARGS Int degree,
1341 TSplitter splitter,
1342 Long miny,
1343 Long maxy )
1344 {
1345 TPoint* arc = ras.arc;
1346 Bool result, fresh;
1347
1348
1349 arc[0].y = -arc[0].y;
1350 arc[1].y = -arc[1].y;
1351 arc[2].y = -arc[2].y;
1352 if ( degree > 2 )
1353 arc[3].y = -arc[3].y;
1354
1355 fresh = ras.fresh;
1356
1357 result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny );
1358
1359 if ( fresh && !ras.fresh )
1360 ras.cProfile->start = -ras.cProfile->start;
1361
1362 arc[0].y = -arc[0].y;
1363 return result;
1364 }
1365
1366
1367 /**************************************************************************
1368 *
1369 * @Function:
1370 * Line_To
1371 *
1372 * @Description:
1373 * Inject a new line segment and adjust the Profiles list.
1374 *
1375 * @Input:
1376 * x ::
1377 * The x-coordinate of the segment's end point (its start point
1378 * is stored in `lastX').
1379 *
1380 * y ::
1381 * The y-coordinate of the segment's end point (its start point
1382 * is stored in `lastY').
1383 *
1384 * @Return:
1385 * SUCCESS on success, FAILURE on render pool overflow or incorrect
1386 * profile.
1387 */
1388 static Bool
1389 Line_To( RAS_ARGS Long x,
1390 Long y )
1391 {
1392 /* First, detect a change of direction */
1393
1394 switch ( ras.state )
1395 {
1396 case Unknown_State:
1397 if ( y > ras.lastY )
1398 {
1399 if ( New_Profile( RAS_VARS Ascending_State,
1400 IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
1401 return FAILURE;
1402 }
1403 else
1404 {
1405 if ( y < ras.lastY )
1406 if ( New_Profile( RAS_VARS Descending_State,
1407 IS_TOP_OVERSHOOT( ras.lastY ) ) )
1408 return FAILURE;
1409 }
1410 break;
1411
1412 case Ascending_State:
1413 if ( y < ras.lastY )
1414 {
1415 if ( End_Profile( RAS_VARS IS_TOP_OVERSHOOT( ras.lastY ) ) ||
1416 New_Profile( RAS_VARS Descending_State,
1417 IS_TOP_OVERSHOOT( ras.lastY ) ) )
1418 return FAILURE;
1419 }
1420 break;
1421
1422 case Descending_State:
1423 if ( y > ras.lastY )
1424 {
1425 if ( End_Profile( RAS_VARS IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ||
1426 New_Profile( RAS_VARS Ascending_State,
1427 IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
1428 return FAILURE;
1429 }
1430 break;
1431
1432 default:
1433 ;
1434 }
1435
1436 /* Then compute the lines */
1437
1438 switch ( ras.state )
1439 {
1440 case Ascending_State:
1441 if ( Line_Up( RAS_VARS ras.lastX, ras.lastY,
1442 x, y, ras.minY, ras.maxY ) )
1443 return FAILURE;
1444 break;
1445
1446 case Descending_State:
1447 if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
1448 x, y, ras.minY, ras.maxY ) )
1449 return FAILURE;
1450 break;
1451
1452 default:
1453 ;
1454 }
1455
1456 ras.lastX = x;
1457 ras.lastY = y;
1458
1459 return SUCCESS;
1460 }
1461
1462
1463 /**************************************************************************
1464 *
1465 * @Function:
1466 * Conic_To
1467 *
1468 * @Description:
1469 * Inject a new conic arc and adjust the profile list.
1470 *
1471 * @Input:
1472 * cx ::
1473 * The x-coordinate of the arc's new control point.
1474 *
1475 * cy ::
1476 * The y-coordinate of the arc's new control point.
1477 *
1478 * x ::
1479 * The x-coordinate of the arc's end point (its start point is
1480 * stored in `lastX').
1481 *
1482 * y ::
1483 * The y-coordinate of the arc's end point (its start point is
1484 * stored in `lastY').
1485 *
1486 * @Return:
1487 * SUCCESS on success, FAILURE on render pool overflow or incorrect
1488 * profile.
1489 */
1490 static Bool
1491 Conic_To( RAS_ARGS Long cx,
1492 Long cy,
1493 Long x,
1494 Long y )
1495 {
1496 Long y1, y2, y3, x3, ymin, ymax;
1497 TStates state_bez;
1498
1499
1500 ras.arc = ras.arcs;
1501 ras.arc[2].x = ras.lastX;
1502 ras.arc[2].y = ras.lastY;
1503 ras.arc[1].x = cx;
1504 ras.arc[1].y = cy;
1505 ras.arc[0].x = x;
1506 ras.arc[0].y = y;
1507
1508 do
1509 {
1510 y1 = ras.arc[2].y;
1511 y2 = ras.arc[1].y;
1512 y3 = ras.arc[0].y;
1513 x3 = ras.arc[0].x;
1514
1515 /* first, categorize the Bezier arc */
1516
1517 if ( y1 <= y3 )
1518 {
1519 ymin = y1;
1520 ymax = y3;
1521 }
1522 else
1523 {
1524 ymin = y3;
1525 ymax = y1;
1526 }
1527
1528 if ( y2 < ymin || y2 > ymax )
1529 {
1530 /* this arc has no given direction, split it! */
1531 Split_Conic( ras.arc );
1532 ras.arc += 2;
1533 }
1534 else if ( y1 == y3 )
1535 {
1536 /* this arc is flat, ignore it and pop it from the Bezier stack */
1537 ras.arc -= 2;
1538 }
1539 else
1540 {
1541 /* the arc is y-monotonous, either ascending or descending */
1542 /* detect a change of direction */
1543 state_bez = y1 < y3 ? Ascending_State : Descending_State;
1544 if ( ras.state != state_bez )
1545 {
1546 Bool o = ( state_bez == Ascending_State )
1547 ? IS_BOTTOM_OVERSHOOT( y1 )
1548 : IS_TOP_OVERSHOOT( y1 );
1549
1550
1551 /* finalize current profile if any */
1552 if ( ras.state != Unknown_State &&
1553 End_Profile( RAS_VARS o ) )
1554 goto Fail;
1555
1556 /* create a new profile */
1557 if ( New_Profile( RAS_VARS state_bez, o ) )
1558 goto Fail;
1559 }
1560
1561 /* now call the appropriate routine */
1562 if ( state_bez == Ascending_State )
1563 {
1564 if ( Bezier_Up( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
1565 goto Fail;
1566 }
1567 else
1568 if ( Bezier_Down( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
1569 goto Fail;
1570 }
1571
1572 } while ( ras.arc >= ras.arcs );
1573
1574 ras.lastX = x3;
1575 ras.lastY = y3;
1576
1577 return SUCCESS;
1578
1579 Fail:
1580 return FAILURE;
1581 }
1582
1583
1584 /**************************************************************************
1585 *
1586 * @Function:
1587 * Cubic_To
1588 *
1589 * @Description:
1590 * Inject a new cubic arc and adjust the profile list.
1591 *
1592 * @Input:
1593 * cx1 ::
1594 * The x-coordinate of the arc's first new control point.
1595 *
1596 * cy1 ::
1597 * The y-coordinate of the arc's first new control point.
1598 *
1599 * cx2 ::
1600 * The x-coordinate of the arc's second new control point.
1601 *
1602 * cy2 ::
1603 * The y-coordinate of the arc's second new control point.
1604 *
1605 * x ::
1606 * The x-coordinate of the arc's end point (its start point is
1607 * stored in `lastX').
1608 *
1609 * y ::
1610 * The y-coordinate of the arc's end point (its start point is
1611 * stored in `lastY').
1612 *
1613 * @Return:
1614 * SUCCESS on success, FAILURE on render pool overflow or incorrect
1615 * profile.
1616 */
1617 static Bool
1618 Cubic_To( RAS_ARGS Long cx1,
1619 Long cy1,
1620 Long cx2,
1621 Long cy2,
1622 Long x,
1623 Long y )
1624 {
1625 Long y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
1626 TStates state_bez;
1627
1628
1629 ras.arc = ras.arcs;
1630 ras.arc[3].x = ras.lastX;
1631 ras.arc[3].y = ras.lastY;
1632 ras.arc[2].x = cx1;
1633 ras.arc[2].y = cy1;
1634 ras.arc[1].x = cx2;
1635 ras.arc[1].y = cy2;
1636 ras.arc[0].x = x;
1637 ras.arc[0].y = y;
1638
1639 do
1640 {
1641 y1 = ras.arc[3].y;
1642 y2 = ras.arc[2].y;
1643 y3 = ras.arc[1].y;
1644 y4 = ras.arc[0].y;
1645 x4 = ras.arc[0].x;
1646
1647 /* first, categorize the Bezier arc */
1648
1649 if ( y1 <= y4 )
1650 {
1651 ymin1 = y1;
1652 ymax1 = y4;
1653 }
1654 else
1655 {
1656 ymin1 = y4;
1657 ymax1 = y1;
1658 }
1659
1660 if ( y2 <= y3 )
1661 {
1662 ymin2 = y2;
1663 ymax2 = y3;
1664 }
1665 else
1666 {
1667 ymin2 = y3;
1668 ymax2 = y2;
1669 }
1670
1671 if ( ymin2 < ymin1 || ymax2 > ymax1 )
1672 {
1673 /* this arc has no given direction, split it! */
1674 Split_Cubic( ras.arc );
1675 ras.arc += 3;
1676 }
1677 else if ( y1 == y4 )
1678 {
1679 /* this arc is flat, ignore it and pop it from the Bezier stack */
1680 ras.arc -= 3;
1681 }
1682 else
1683 {
1684 state_bez = ( y1 <= y4 ) ? Ascending_State : Descending_State;
1685
1686 /* detect a change of direction */
1687 if ( ras.state != state_bez )
1688 {
1689 Bool o = ( state_bez == Ascending_State )
1690 ? IS_BOTTOM_OVERSHOOT( y1 )
1691 : IS_TOP_OVERSHOOT( y1 );
1692
1693
1694 /* finalize current profile if any */
1695 if ( ras.state != Unknown_State &&
1696 End_Profile( RAS_VARS o ) )
1697 goto Fail;
1698
1699 if ( New_Profile( RAS_VARS state_bez, o ) )
1700 goto Fail;
1701 }
1702
1703 /* compute intersections */
1704 if ( state_bez == Ascending_State )
1705 {
1706 if ( Bezier_Up( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
1707 goto Fail;
1708 }
1709 else
1710 if ( Bezier_Down( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
1711 goto Fail;
1712 }
1713
1714 } while ( ras.arc >= ras.arcs );
1715
1716 ras.lastX = x4;
1717 ras.lastY = y4;
1718
1719 return SUCCESS;
1720
1721 Fail:
1722 return FAILURE;
1723 }
1724
1725
1726#undef SWAP_
1727#define SWAP_( x, y ) do \
1728 { \
1729 Long swap = x; \
1730 \
1731 \
1732 x = y; \
1733 y = swap; \
1734 } while ( 0 )
1735
1736
1737 /**************************************************************************
1738 *
1739 * @Function:
1740 * Decompose_Curve
1741 *
1742 * @Description:
1743 * Scan the outline arrays in order to emit individual segments and
1744 * Beziers by calling Line_To() and Bezier_To(). It handles all
1745 * weird cases, like when the first point is off the curve, or when
1746 * there are simply no `on' points in the contour!
1747 *
1748 * @Input:
1749 * first ::
1750 * The index of the first point in the contour.
1751 *
1752 * last ::
1753 * The index of the last point in the contour.
1754 *
1755 * flipped ::
1756 * If set, flip the direction of the curve.
1757 *
1758 * @Return:
1759 * SUCCESS on success, FAILURE on error.
1760 */
1761 static Bool
1762 Decompose_Curve( RAS_ARGS UShort first,
1763 UShort last,
1764 Int flipped )
1765 {
1766 FT_Vector v_last;
1767 FT_Vector v_control;
1768 FT_Vector v_start;
1769
1770 FT_Vector* points;
1771 FT_Vector* point;
1772 FT_Vector* limit;
1773 char* tags;
1774
1775 UInt tag; /* current point's state */
1776
1777
1778 points = ras.outline.points;
1779 limit = points + last;
1780
1781 v_start.x = SCALED( points[first].x );
1782 v_start.y = SCALED( points[first].y );
1783 v_last.x = SCALED( points[last].x );
1784 v_last.y = SCALED( points[last].y );
1785
1786 if ( flipped )
1787 {
1788 SWAP_( v_start.x, v_start.y );
1789 SWAP_( v_last.x, v_last.y );
1790 }
1791
1792 v_control = v_start;
1793
1794 point = points + first;
1795 tags = ras.outline.tags + first;
1796
1797 /* set scan mode if necessary */
1798 if ( tags[0] & FT_CURVE_TAG_HAS_SCANMODE )
1799 ras.dropOutControl = (Byte)tags[0] >> 5;
1800
1801 tag = FT_CURVE_TAG( tags[0] );
1802
1803 /* A contour cannot start with a cubic control point! */
1804 if ( tag == FT_CURVE_TAG_CUBIC )
1805 goto Invalid_Outline;
1806
1807 /* check first point to determine origin */
1808 if ( tag == FT_CURVE_TAG_CONIC )
1809 {
1810 /* first point is conic control. Yes, this happens. */
1811 if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_CURVE_TAG_ON )
1812 {
1813 /* start at last point if it is on the curve */
1814 v_start = v_last;
1815 limit--;
1816 }
1817 else
1818 {
1819 /* if both first and last points are conic, */
1820 /* start at their middle and record its position */
1821 /* for closure */
1822 v_start.x = ( v_start.x + v_last.x ) / 2;
1823 v_start.y = ( v_start.y + v_last.y ) / 2;
1824
1825 /* v_last = v_start; */
1826 }
1827 point--;
1828 tags--;
1829 }
1830
1831 ras.lastX = v_start.x;
1832 ras.lastY = v_start.y;
1833
1834 while ( point < limit )
1835 {
1836 point++;
1837 tags++;
1838
1839 tag = FT_CURVE_TAG( tags[0] );
1840
1841 switch ( tag )
1842 {
1843 case FT_CURVE_TAG_ON: /* emit a single line_to */
1844 {
1845 Long x, y;
1846
1847
1848 x = SCALED( point->x );
1849 y = SCALED( point->y );
1850 if ( flipped )
1851 SWAP_( x, y );
1852
1853 if ( Line_To( RAS_VARS x, y ) )
1854 goto Fail;
1855 continue;
1856 }
1857
1858 case FT_CURVE_TAG_CONIC: /* consume conic arcs */
1859 v_control.x = SCALED( point[0].x );
1860 v_control.y = SCALED( point[0].y );
1861
1862 if ( flipped )
1863 SWAP_( v_control.x, v_control.y );
1864
1865 Do_Conic:
1866 if ( point < limit )
1867 {
1868 FT_Vector v_middle;
1869 Long x, y;
1870
1871
1872 point++;
1873 tags++;
1874 tag = FT_CURVE_TAG( tags[0] );
1875
1876 x = SCALED( point[0].x );
1877 y = SCALED( point[0].y );
1878
1879 if ( flipped )
1880 SWAP_( x, y );
1881
1882 if ( tag == FT_CURVE_TAG_ON )
1883 {
1884 if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) )
1885 goto Fail;
1886 continue;
1887 }
1888
1889 if ( tag != FT_CURVE_TAG_CONIC )
1890 goto Invalid_Outline;
1891
1892 v_middle.x = ( v_control.x + x ) / 2;
1893 v_middle.y = ( v_control.y + y ) / 2;
1894
1895 if ( Conic_To( RAS_VARS v_control.x, v_control.y,
1896 v_middle.x, v_middle.y ) )
1897 goto Fail;
1898
1899 v_control.x = x;
1900 v_control.y = y;
1901
1902 goto Do_Conic;
1903 }
1904
1905 if ( Conic_To( RAS_VARS v_control.x, v_control.y,
1906 v_start.x, v_start.y ) )
1907 goto Fail;
1908
1909 goto Close;
1910
1911 default: /* FT_CURVE_TAG_CUBIC */
1912 {
1913 Long x1, y1, x2, y2, x3, y3;
1914
1915
1916 if ( point + 1 > limit ||
1917 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
1918 goto Invalid_Outline;
1919
1920 point += 2;
1921 tags += 2;
1922
1923 x1 = SCALED( point[-2].x );
1924 y1 = SCALED( point[-2].y );
1925 x2 = SCALED( point[-1].x );
1926 y2 = SCALED( point[-1].y );
1927
1928 if ( flipped )
1929 {
1930 SWAP_( x1, y1 );
1931 SWAP_( x2, y2 );
1932 }
1933
1934 if ( point <= limit )
1935 {
1936 x3 = SCALED( point[0].x );
1937 y3 = SCALED( point[0].y );
1938
1939 if ( flipped )
1940 SWAP_( x3, y3 );
1941
1942 if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) )
1943 goto Fail;
1944 continue;
1945 }
1946
1947 if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) )
1948 goto Fail;
1949 goto Close;
1950 }
1951 }
1952 }
1953
1954 /* close the contour with a line segment */
1955 if ( Line_To( RAS_VARS v_start.x, v_start.y ) )
1956 goto Fail;
1957
1958 Close:
1959 return SUCCESS;
1960
1961 Invalid_Outline:
1962 ras.error = FT_THROW( Invalid );
1963
1964 Fail:
1965 return FAILURE;
1966 }
1967
1968
1969 /**************************************************************************
1970 *
1971 * @Function:
1972 * Convert_Glyph
1973 *
1974 * @Description:
1975 * Convert a glyph into a series of segments and arcs and make a
1976 * profiles list with them.
1977 *
1978 * @Input:
1979 * flipped ::
1980 * If set, flip the direction of curve.
1981 *
1982 * @Return:
1983 * SUCCESS on success, FAILURE if any error was encountered during
1984 * rendering.
1985 */
1986 static Bool
1987 Convert_Glyph( RAS_ARGS Int flipped )
1988 {
1989 Int i;
1990 UInt start;
1991
1992
1993 ras.fProfile = NULL;
1994 ras.joint = FALSE;
1995 ras.fresh = FALSE;
1996
1997 ras.maxBuff = ras.sizeBuff - AlignProfileSize;
1998
1999 ras.numTurns = 0;
2000
2001 ras.cProfile = (PProfile)ras.top;
2002 ras.cProfile->offset = ras.top;
2003 ras.num_Profs = 0;
2004
2005 start = 0;
2006
2007 for ( i = 0; i < ras.outline.n_contours; i++ )
2008 {
2009 PProfile lastProfile;
2010 Bool o;
2011
2012
2013 ras.state = Unknown_State;
2014 ras.gProfile = NULL;
2015
2016 if ( Decompose_Curve( RAS_VARS (UShort)start,
2017 (UShort)ras.outline.contours[i],
2018 flipped ) )
2019 return FAILURE;
2020
2021 start = (UShort)ras.outline.contours[i] + 1;
2022
2023 /* we must now check whether the extreme arcs join or not */
2024 if ( FRAC( ras.lastY ) == 0 &&
2025 ras.lastY >= ras.minY &&
2026 ras.lastY <= ras.maxY )
2027 if ( ras.gProfile &&
2028 ( ras.gProfile->flags & Flow_Up ) ==
2029 ( ras.cProfile->flags & Flow_Up ) )
2030 ras.top--;
2031 /* Note that ras.gProfile can be nil if the contour was too small */
2032 /* to be drawn. */
2033
2034 lastProfile = ras.cProfile;
2035 if ( ras.top != ras.cProfile->offset &&
2036 ( ras.cProfile->flags & Flow_Up ) )
2037 o = IS_TOP_OVERSHOOT( ras.lastY );
2038 else
2039 o = IS_BOTTOM_OVERSHOOT( ras.lastY );
2040 if ( End_Profile( RAS_VARS o ) )
2041 return FAILURE;
2042
2043 /* close the `next profile in contour' linked list */
2044 if ( ras.gProfile )
2045 lastProfile->next = ras.gProfile;
2046 }
2047
2048 if ( Finalize_Profile_Table( RAS_VAR ) )
2049 return FAILURE;
2050
2051 return (Bool)( ras.top < ras.maxBuff ? SUCCESS : FAILURE );
2052 }
2053
2054
2055 /*************************************************************************/
2056 /*************************************************************************/
2057 /** **/
2058 /** SCAN-LINE SWEEPS AND DRAWING **/
2059 /** **/
2060 /*************************************************************************/
2061 /*************************************************************************/
2062
2063
2064 /**************************************************************************
2065 *
2066 * Init_Linked
2067 *
2068 * Initializes an empty linked list.
2069 */
2070 static void
2071 Init_Linked( TProfileList* l )
2072 {
2073 *l = NULL;
2074 }
2075
2076
2077 /**************************************************************************
2078 *
2079 * InsNew
2080 *
2081 * Inserts a new profile in a linked list.
2082 */
2083 static void
2084 InsNew( PProfileList list,
2085 PProfile profile )
2086 {
2087 PProfile *old, current;
2088 Long x;
2089
2090
2091 old = list;
2092 current = *old;
2093 x = profile->X;
2094
2095 while ( current )
2096 {
2097 if ( x < current->X )
2098 break;
2099 old = &current->link;
2100 current = *old;
2101 }
2102
2103 profile->link = current;
2104 *old = profile;
2105 }
2106
2107
2108 /**************************************************************************
2109 *
2110 * DelOld
2111 *
2112 * Removes an old profile from a linked list.
2113 */
2114 static void
2115 DelOld( PProfileList list,
2116 PProfile profile )
2117 {
2118 PProfile *old, current;
2119
2120
2121 old = list;
2122 current = *old;
2123
2124 while ( current )
2125 {
2126 if ( current == profile )
2127 {
2128 *old = current->link;
2129 return;
2130 }
2131
2132 old = &current->link;
2133 current = *old;
2134 }
2135
2136 /* we should never get there, unless the profile was not part of */
2137 /* the list. */
2138 }
2139
2140
2141 /**************************************************************************
2142 *
2143 * Sort
2144 *
2145 * Sorts a trace list. In 95%, the list is already sorted. We need
2146 * an algorithm which is fast in this case. Bubble sort is enough
2147 * and simple.
2148 */
2149 static void
2150 Sort( PProfileList list )
2151 {
2152 PProfile *old, current, next;
2153
2154
2155 /* First, set the new X coordinate of each profile */
2156 current = *list;
2157 while ( current )
2158 {
2159 current->X = *current->offset;
2160 current->offset += ( current->flags & Flow_Up ) ? 1 : -1;
2161 current->height--;
2162 current = current->link;
2163 }
2164
2165 /* Then sort them */
2166 old = list;
2167 current = *old;
2168
2169 if ( !current )
2170 return;
2171
2172 next = current->link;
2173
2174 while ( next )
2175 {
2176 if ( current->X <= next->X )
2177 {
2178 old = &current->link;
2179 current = *old;
2180
2181 if ( !current )
2182 return;
2183 }
2184 else
2185 {
2186 *old = next;
2187 current->link = next->link;
2188 next->link = current;
2189
2190 old = list;
2191 current = *old;
2192 }
2193
2194 next = current->link;
2195 }
2196 }
2197
2198
2199 /**************************************************************************
2200 *
2201 * Vertical Sweep Procedure Set
2202 *
2203 * These four routines are used during the vertical black/white sweep
2204 * phase by the generic Draw_Sweep() function.
2205 *
2206 */
2207
2208 static void
2209 Vertical_Sweep_Init( RAS_ARGS Short* min,
2210 Short* max )
2211 {
2212 Long pitch = ras.target.pitch;
2213
2214 FT_UNUSED( max );
2215
2216
2217 ras.traceIncr = (Short)-pitch;
2218 ras.traceOfs = -*min * pitch;
2219 }
2220
2221
2222 static void
2223 Vertical_Sweep_Span( RAS_ARGS Short y,
2224 FT_F26Dot6 x1,
2225 FT_F26Dot6 x2,
2226 PProfile left,
2227 PProfile right )
2228 {
2229 Long e1, e2;
2230 Byte* target;
2231
2232 Int dropOutControl = left->flags & 7;
2233
2234 FT_UNUSED( y );
2235 FT_UNUSED( left );
2236 FT_UNUSED( right );
2237
2238
2239 /* in high-precision mode, we need 12 digits after the comma to */
2240 /* represent multiples of 1/(1<<12) = 1/4096 */
2241 FT_TRACE7(( " y=%d x=[%.12f;%.12f], drop-out=%d",
2242 y,
2243 x1 / (double)ras.precision,
2244 x2 / (double)ras.precision,
2245 dropOutControl ));
2246
2247 /* Drop-out control */
2248
2249 e1 = CEILING( x1 );
2250 e2 = FLOOR( x2 );
2251
2252 /* take care of the special case where both the left */
2253 /* and right contour lie exactly on pixel centers */
2254 if ( dropOutControl != 2 &&
2255 x2 - x1 - ras.precision <= ras.precision_jitter &&
2256 e1 != x1 && e2 != x2 )
2257 e2 = e1;
2258
2259 e1 = TRUNC( e1 );
2260 e2 = TRUNC( e2 );
2261
2262 if ( e2 >= 0 && e1 < ras.bWidth )
2263 {
2264 Int c1, c2;
2265 Byte f1, f2;
2266
2267
2268 if ( e1 < 0 )
2269 e1 = 0;
2270 if ( e2 >= ras.bWidth )
2271 e2 = ras.bWidth - 1;
2272
2273 FT_TRACE7(( " -> x=[%d;%d]", e1, e2 ));
2274
2275 c1 = (Short)( e1 >> 3 );
2276 c2 = (Short)( e2 >> 3 );
2277
2278 f1 = (Byte) ( 0xFF >> ( e1 & 7 ) );
2279 f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) );
2280
2281 target = ras.bOrigin + ras.traceOfs + c1;
2282 c2 -= c1;
2283
2284 if ( c2 > 0 )
2285 {
2286 target[0] |= f1;
2287
2288 /* memset() is slower than the following code on many platforms. */
2289 /* This is due to the fact that, in the vast majority of cases, */
2290 /* the span length in bytes is relatively small. */
2291 while ( --c2 > 0 )
2292 *(++target) = 0xFF;
2293
2294 target[1] |= f2;
2295 }
2296 else
2297 *target |= ( f1 & f2 );
2298 }
2299
2300 FT_TRACE7(( "\n" ));
2301 }
2302
2303
2304 static void
2305 Vertical_Sweep_Drop( RAS_ARGS Short y,
2306 FT_F26Dot6 x1,
2307 FT_F26Dot6 x2,
2308 PProfile left,
2309 PProfile right )
2310 {
2311 Long e1, e2, pxl;
2312 Short c1, f1;
2313
2314
2315 FT_TRACE7(( " y=%d x=[%.12f;%.12f]",
2316 y,
2317 x1 / (double)ras.precision,
2318 x2 / (double)ras.precision ));
2319
2320 /* Drop-out control */
2321
2322 /* e2 x2 x1 e1 */
2323 /* */
2324 /* ^ | */
2325 /* | | */
2326 /* +-------------+---------------------+------------+ */
2327 /* | | */
2328 /* | v */
2329 /* */
2330 /* pixel contour contour pixel */
2331 /* center center */
2332
2333 /* drop-out mode scan conversion rules (as defined in OpenType) */
2334 /* --------------------------------------------------------------- */
2335 /* 0 1, 2, 3 */
2336 /* 1 1, 2, 4 */
2337 /* 2 1, 2 */
2338 /* 3 same as mode 2 */
2339 /* 4 1, 2, 5 */
2340 /* 5 1, 2, 6 */
2341 /* 6, 7 same as mode 2 */
2342
2343 e1 = CEILING( x1 );
2344 e2 = FLOOR ( x2 );
2345 pxl = e1;
2346
2347 if ( e1 > e2 )
2348 {
2349 Int dropOutControl = left->flags & 7;
2350
2351
2352 FT_TRACE7(( ", drop-out=%d", dropOutControl ));
2353
2354 if ( e1 == e2 + ras.precision )
2355 {
2356 switch ( dropOutControl )
2357 {
2358 case 0: /* simple drop-outs including stubs */
2359 pxl = e2;
2360 break;
2361
2362 case 4: /* smart drop-outs including stubs */
2363 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2364 break;
2365
2366 case 1: /* simple drop-outs excluding stubs */
2367 case 5: /* smart drop-outs excluding stubs */
2368
2369 /* Drop-out Control Rules #4 and #6 */
2370
2371 /* The specification neither provides an exact definition */
2372 /* of a `stub' nor gives exact rules to exclude them. */
2373 /* */
2374 /* Here the constraints we use to recognize a stub. */
2375 /* */
2376 /* upper stub: */
2377 /* */
2378 /* - P_Left and P_Right are in the same contour */
2379 /* - P_Right is the successor of P_Left in that contour */
2380 /* - y is the top of P_Left and P_Right */
2381 /* */
2382 /* lower stub: */
2383 /* */
2384 /* - P_Left and P_Right are in the same contour */
2385 /* - P_Left is the successor of P_Right in that contour */
2386 /* - y is the bottom of P_Left */
2387 /* */
2388 /* We draw a stub if the following constraints are met. */
2389 /* */
2390 /* - for an upper or lower stub, there is top or bottom */
2391 /* overshoot, respectively */
2392 /* - the covered interval is greater or equal to a half */
2393 /* pixel */
2394
2395 /* upper stub test */
2396 if ( left->next == right &&
2397 left->height <= 0 &&
2398 !( left->flags & Overshoot_Top &&
2399 x2 - x1 >= ras.precision_half ) )
2400 goto Exit;
2401
2402 /* lower stub test */
2403 if ( right->next == left &&
2404 left->start == y &&
2405 !( left->flags & Overshoot_Bottom &&
2406 x2 - x1 >= ras.precision_half ) )
2407 goto Exit;
2408
2409 if ( dropOutControl == 1 )
2410 pxl = e2;
2411 else
2412 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2413 break;
2414
2415 default: /* modes 2, 3, 6, 7 */
2416 goto Exit; /* no drop-out control */
2417 }
2418
2419 /* undocumented but confirmed: If the drop-out would result in a */
2420 /* pixel outside of the bounding box, use the pixel inside of the */
2421 /* bounding box instead */
2422 if ( pxl < 0 )
2423 pxl = e1;
2424 else if ( TRUNC( pxl ) >= ras.bWidth )
2425 pxl = e2;
2426
2427 /* check that the other pixel isn't set */
2428 e1 = ( pxl == e1 ) ? e2 : e1;
2429
2430 e1 = TRUNC( e1 );
2431
2432 c1 = (Short)( e1 >> 3 );
2433 f1 = (Short)( e1 & 7 );
2434
2435 if ( e1 >= 0 && e1 < ras.bWidth &&
2436 ras.bOrigin[ras.traceOfs + c1] & ( 0x80 >> f1 ) )
2437 goto Exit;
2438 }
2439 else
2440 goto Exit;
2441 }
2442
2443 e1 = TRUNC( pxl );
2444
2445 if ( e1 >= 0 && e1 < ras.bWidth )
2446 {
2447 FT_TRACE7(( " -> x=%d (drop-out)", e1 ));
2448
2449 c1 = (Short)( e1 >> 3 );
2450 f1 = (Short)( e1 & 7 );
2451
2452 ras.bOrigin[ras.traceOfs + c1] |= (char)( 0x80 >> f1 );
2453 }
2454
2455 Exit:
2456 FT_TRACE7(( "\n" ));
2457 }
2458
2459
2460 static void
2461 Vertical_Sweep_Step( RAS_ARG )
2462 {
2463 ras.traceOfs += ras.traceIncr;
2464 }
2465
2466
2467 /************************************************************************
2468 *
2469 * Horizontal Sweep Procedure Set
2470 *
2471 * These four routines are used during the horizontal black/white
2472 * sweep phase by the generic Draw_Sweep() function.
2473 *
2474 */
2475
2476 static void
2477 Horizontal_Sweep_Init( RAS_ARGS Short* min,
2478 Short* max )
2479 {
2480 /* nothing, really */
2481 FT_UNUSED_RASTER;
2482 FT_UNUSED( min );
2483 FT_UNUSED( max );
2484 }
2485
2486
2487 static void
2488 Horizontal_Sweep_Span( RAS_ARGS Short y,
2489 FT_F26Dot6 x1,
2490 FT_F26Dot6 x2,
2491 PProfile left,
2492 PProfile right )
2493 {
2494 FT_UNUSED( left );
2495 FT_UNUSED( right );
2496
2497
2498 if ( x2 - x1 < ras.precision )
2499 {
2500 Long e1, e2;
2501
2502
2503 FT_TRACE7(( " x=%d y=[%.12f;%.12f]",
2504 y,
2505 x1 / (double)ras.precision,
2506 x2 / (double)ras.precision ));
2507
2508 e1 = CEILING( x1 );
2509 e2 = FLOOR ( x2 );
2510
2511 if ( e1 == e2 )
2512 {
2513 e1 = TRUNC( e1 );
2514
2515 if ( e1 >= 0 && (ULong)e1 < ras.target.rows )
2516 {
2517 Byte f1;
2518 PByte bits;
2519
2520
2521 FT_TRACE7(( " -> y=%d (drop-out)", e1 ));
2522
2523 bits = ras.bOrigin + ( y >> 3 ) - e1 * ras.target.pitch;
2524 f1 = (Byte)( 0x80 >> ( y & 7 ) );
2525
2526 bits[0] |= f1;
2527 }
2528 }
2529
2530 FT_TRACE7(( "\n" ));
2531 }
2532 }
2533
2534
2535 static void
2536 Horizontal_Sweep_Drop( RAS_ARGS Short y,
2537 FT_F26Dot6 x1,
2538 FT_F26Dot6 x2,
2539 PProfile left,
2540 PProfile right )
2541 {
2542 Long e1, e2, pxl;
2543 PByte bits;
2544 Byte f1;
2545
2546
2547 FT_TRACE7(( " x=%d y=[%.12f;%.12f]",
2548 y,
2549 x1 / (double)ras.precision,
2550 x2 / (double)ras.precision ));
2551
2552 /* During the horizontal sweep, we only take care of drop-outs */
2553
2554 /* e1 + <-- pixel center */
2555 /* | */
2556 /* x1 ---+--> <-- contour */
2557 /* | */
2558 /* | */
2559 /* x2 <--+--- <-- contour */
2560 /* | */
2561 /* | */
2562 /* e2 + <-- pixel center */
2563
2564 e1 = CEILING( x1 );
2565 e2 = FLOOR ( x2 );
2566 pxl = e1;
2567
2568 if ( e1 > e2 )
2569 {
2570 Int dropOutControl = left->flags & 7;
2571
2572
2573 FT_TRACE7(( ", dropout=%d", dropOutControl ));
2574
2575 if ( e1 == e2 + ras.precision )
2576 {
2577 switch ( dropOutControl )
2578 {
2579 case 0: /* simple drop-outs including stubs */
2580 pxl = e2;
2581 break;
2582
2583 case 4: /* smart drop-outs including stubs */
2584 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2585 break;
2586
2587 case 1: /* simple drop-outs excluding stubs */
2588 case 5: /* smart drop-outs excluding stubs */
2589 /* see Vertical_Sweep_Drop for details */
2590
2591 /* rightmost stub test */
2592 if ( left->next == right &&
2593 left->height <= 0 &&
2594 !( left->flags & Overshoot_Top &&
2595 x2 - x1 >= ras.precision_half ) )
2596 goto Exit;
2597
2598 /* leftmost stub test */
2599 if ( right->next == left &&
2600 left->start == y &&
2601 !( left->flags & Overshoot_Bottom &&
2602 x2 - x1 >= ras.precision_half ) )
2603 goto Exit;
2604
2605 if ( dropOutControl == 1 )
2606 pxl = e2;
2607 else
2608 pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
2609 break;
2610
2611 default: /* modes 2, 3, 6, 7 */
2612 goto Exit; /* no drop-out control */
2613 }
2614
2615 /* undocumented but confirmed: If the drop-out would result in a */
2616 /* pixel outside of the bounding box, use the pixel inside of the */
2617 /* bounding box instead */
2618 if ( pxl < 0 )
2619 pxl = e1;
2620 else if ( (ULong)( TRUNC( pxl ) ) >= ras.target.rows )
2621 pxl = e2;
2622
2623 /* check that the other pixel isn't set */
2624 e1 = ( pxl == e1 ) ? e2 : e1;
2625
2626 e1 = TRUNC( e1 );
2627
2628 bits = ras.bOrigin + ( y >> 3 ) - e1 * ras.target.pitch;
2629 f1 = (Byte)( 0x80 >> ( y & 7 ) );
2630
2631 if ( e1 >= 0 &&
2632 (ULong)e1 < ras.target.rows &&
2633 *bits & f1 )
2634 goto Exit;
2635 }
2636 else
2637 goto Exit;
2638 }
2639
2640 e1 = TRUNC( pxl );
2641
2642 if ( e1 >= 0 && (ULong)e1 < ras.target.rows )
2643 {
2644 FT_TRACE7(( " -> y=%d (drop-out)", e1 ));
2645
2646 bits = ras.bOrigin + ( y >> 3 ) - e1 * ras.target.pitch;
2647 f1 = (Byte)( 0x80 >> ( y & 7 ) );
2648
2649 bits[0] |= f1;
2650 }
2651
2652 Exit:
2653 FT_TRACE7(( "\n" ));
2654 }
2655
2656
2657 static void
2658 Horizontal_Sweep_Step( RAS_ARG )
2659 {
2660 /* Nothing, really */
2661 FT_UNUSED_RASTER;
2662 }
2663
2664
2665 /**************************************************************************
2666 *
2667 * Generic Sweep Drawing routine
2668 *
2669 */
2670
2671 static Bool
2672 Draw_Sweep( RAS_ARG )
2673 {
2674 Short y, y_change, y_height;
2675
2676 PProfile P, Q, P_Left, P_Right;
2677
2678 Short min_Y, max_Y, top, bottom, dropouts;
2679
2680 Long x1, x2, xs, e1, e2;
2681
2682 TProfileList waiting;
2683 TProfileList draw_left, draw_right;
2684
2685
2686 /* initialize empty linked lists */
2687
2688 Init_Linked( &waiting );
2689
2690 Init_Linked( &draw_left );
2691 Init_Linked( &draw_right );
2692
2693 /* first, compute min and max Y */
2694
2695 P = ras.fProfile;
2696 max_Y = (Short)TRUNC( ras.minY );
2697 min_Y = (Short)TRUNC( ras.maxY );
2698
2699 while ( P )
2700 {
2701 Q = P->link;
2702
2703 bottom = (Short)P->start;
2704 top = (Short)( P->start + P->height - 1 );
2705
2706 if ( min_Y > bottom )
2707 min_Y = bottom;
2708 if ( max_Y < top )
2709 max_Y = top;
2710
2711 P->X = 0;
2712 InsNew( &waiting, P );
2713
2714 P = Q;
2715 }
2716
2717 /* check the Y-turns */
2718 if ( ras.numTurns == 0 )
2719 {
2720 ras.error = FT_THROW( Invalid );
2721 return FAILURE;
2722 }
2723
2724 /* now initialize the sweep */
2725
2726 ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y );
2727
2728 /* then compute the distance of each profile from min_Y */
2729
2730 P = waiting;
2731
2732 while ( P )
2733 {
2734 P->countL = P->start - min_Y;
2735 P = P->link;
2736 }
2737
2738 /* let's go */
2739
2740 y = min_Y;
2741 y_height = 0;
2742
2743 if ( ras.numTurns > 0 &&
2744 ras.sizeBuff[-ras.numTurns] == min_Y )
2745 ras.numTurns--;
2746
2747 while ( ras.numTurns > 0 )
2748 {
2749 /* check waiting list for new activations */
2750
2751 P = waiting;
2752
2753 while ( P )
2754 {
2755 Q = P->link;
2756 P->countL -= y_height;
2757 if ( P->countL == 0 )
2758 {
2759 DelOld( &waiting, P );
2760
2761 if ( P->flags & Flow_Up )
2762 InsNew( &draw_left, P );
2763 else
2764 InsNew( &draw_right, P );
2765 }
2766
2767 P = Q;
2768 }
2769
2770 /* sort the drawing lists */
2771
2772 Sort( &draw_left );
2773 Sort( &draw_right );
2774
2775 y_change = (Short)ras.sizeBuff[-ras.numTurns--];
2776 y_height = (Short)( y_change - y );
2777
2778 while ( y < y_change )
2779 {
2780 /* let's trace */
2781
2782 dropouts = 0;
2783
2784 P_Left = draw_left;
2785 P_Right = draw_right;
2786
2787 while ( P_Left )
2788 {
2789 x1 = P_Left ->X;
2790 x2 = P_Right->X;
2791
2792 if ( x1 > x2 )
2793 {
2794 xs = x1;
2795 x1 = x2;
2796 x2 = xs;
2797 }
2798
2799 e1 = FLOOR( x1 );
2800 e2 = CEILING( x2 );
2801
2802 if ( x2 - x1 <= ras.precision &&
2803 e1 != x1 && e2 != x2 )
2804 {
2805 if ( e1 > e2 || e2 == e1 + ras.precision )
2806 {
2807 Int dropOutControl = P_Left->flags & 7;
2808
2809
2810 if ( dropOutControl != 2 )
2811 {
2812 /* a drop-out was detected */
2813
2814 P_Left ->X = x1;
2815 P_Right->X = x2;
2816
2817 /* mark profile for drop-out processing */
2818 P_Left->countL = 1;
2819 dropouts++;
2820 }
2821
2822 goto Skip_To_Next;
2823 }
2824 }
2825
2826 ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right );
2827
2828 Skip_To_Next:
2829
2830 P_Left = P_Left->link;
2831 P_Right = P_Right->link;
2832 }
2833
2834 /* handle drop-outs _after_ the span drawing -- */
2835 /* drop-out processing has been moved out of the loop */
2836 /* for performance tuning */
2837 if ( dropouts > 0 )
2838 goto Scan_DropOuts;
2839
2840 Next_Line:
2841
2842 ras.Proc_Sweep_Step( RAS_VAR );
2843
2844 y++;
2845
2846 if ( y < y_change )
2847 {
2848 Sort( &draw_left );
2849 Sort( &draw_right );
2850 }
2851 }
2852
2853 /* now finalize the profiles that need it */
2854
2855 P = draw_left;
2856 while ( P )
2857 {
2858 Q = P->link;
2859 if ( P->height == 0 )
2860 DelOld( &draw_left, P );
2861 P = Q;
2862 }
2863
2864 P = draw_right;
2865 while ( P )
2866 {
2867 Q = P->link;
2868 if ( P->height == 0 )
2869 DelOld( &draw_right, P );
2870 P = Q;
2871 }
2872 }
2873
2874 /* for gray-scaling, flush the bitmap scanline cache */
2875 while ( y <= max_Y )
2876 {
2877 ras.Proc_Sweep_Step( RAS_VAR );
2878 y++;
2879 }
2880
2881 return SUCCESS;
2882
2883 Scan_DropOuts:
2884
2885 P_Left = draw_left;
2886 P_Right = draw_right;
2887
2888 while ( P_Left )
2889 {
2890 if ( P_Left->countL )
2891 {
2892 P_Left->countL = 0;
2893#if 0
2894 dropouts--; /* -- this is useful when debugging only */
2895#endif
2896 ras.Proc_Sweep_Drop( RAS_VARS y,
2897 P_Left->X,
2898 P_Right->X,
2899 P_Left,
2900 P_Right );
2901 }
2902
2903 P_Left = P_Left->link;
2904 P_Right = P_Right->link;
2905 }
2906
2907 goto Next_Line;
2908 }
2909
2910
2911#ifdef STANDALONE_
2912
2913 /**************************************************************************
2914 *
2915 * The following functions should only compile in stand-alone mode,
2916 * i.e., when building this component without the rest of FreeType.
2917 *
2918 */
2919
2920 /**************************************************************************
2921 *
2922 * @Function:
2923 * FT_Outline_Get_CBox
2924 *
2925 * @Description:
2926 * Return an outline's `control box'. The control box encloses all
2927 * the outline's points, including Bézier control points. Though it
2928 * coincides with the exact bounding box for most glyphs, it can be
2929 * slightly larger in some situations (like when rotating an outline
2930 * that contains Bézier outside arcs).
2931 *
2932 * Computing the control box is very fast, while getting the bounding
2933 * box can take much more time as it needs to walk over all segments
2934 * and arcs in the outline. To get the latter, you can use the
2935 * `ftbbox' component, which is dedicated to this single task.
2936 *
2937 * @Input:
2938 * outline ::
2939 * A pointer to the source outline descriptor.
2940 *
2941 * @Output:
2942 * acbox ::
2943 * The outline's control box.
2944 *
2945 * @Note:
2946 * See @FT_Glyph_Get_CBox for a discussion of tricky fonts.
2947 */
2948
2949 static void
2950 FT_Outline_Get_CBox( const FT_Outline* outline,
2951 FT_BBox *acbox )
2952 {
2953 Long xMin, yMin, xMax, yMax;
2954
2955
2956 if ( outline && acbox )
2957 {
2958 if ( outline->n_points == 0 )
2959 {
2960 xMin = 0;
2961 yMin = 0;
2962 xMax = 0;
2963 yMax = 0;
2964 }
2965 else
2966 {
2967 FT_Vector* vec = outline->points;
2968 FT_Vector* limit = vec + outline->n_points;
2969
2970
2971 xMin = xMax = vec->x;
2972 yMin = yMax = vec->y;
2973 vec++;
2974
2975 for ( ; vec < limit; vec++ )
2976 {
2977 Long x, y;
2978
2979
2980 x = vec->x;
2981 if ( x < xMin ) xMin = x;
2982 if ( x > xMax ) xMax = x;
2983
2984 y = vec->y;
2985 if ( y < yMin ) yMin = y;
2986 if ( y > yMax ) yMax = y;
2987 }
2988 }
2989 acbox->xMin = xMin;
2990 acbox->xMax = xMax;
2991 acbox->yMin = yMin;
2992 acbox->yMax = yMax;
2993 }
2994 }
2995
2996#endif /* STANDALONE_ */
2997
2998
2999 /**************************************************************************
3000 *
3001 * @Function:
3002 * Render_Single_Pass
3003 *
3004 * @Description:
3005 * Perform one sweep with sub-banding.
3006 *
3007 * @Input:
3008 * flipped ::
3009 * If set, flip the direction of the outline.
3010 *
3011 * @Return:
3012 * Renderer error code.
3013 */
3014 static int
3015 Render_Single_Pass( RAS_ARGS Bool flipped )
3016 {
3017 Short i, j, k;
3018
3019
3020 while ( ras.band_top >= 0 )
3021 {
3022 ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision;
3023 ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision;
3024
3025 ras.top = ras.buff;
3026
3027 ras.error = Raster_Err_None;
3028
3029 if ( Convert_Glyph( RAS_VARS flipped ) )
3030 {
3031 if ( ras.error != Raster_Err_Overflow )
3032 return FAILURE;
3033
3034 ras.error = Raster_Err_None;
3035
3036 /* sub-banding */
3037
3038#ifdef DEBUG_RASTER
3039 ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) );
3040#endif
3041
3042 i = ras.band_stack[ras.band_top].y_min;
3043 j = ras.band_stack[ras.band_top].y_max;
3044
3045 k = (Short)( ( i + j ) / 2 );
3046
3047 if ( ras.band_top >= 7 || k < i )
3048 {
3049 ras.band_top = 0;
3050 ras.error = FT_THROW( Invalid );
3051
3052 return ras.error;
3053 }
3054
3055 ras.band_stack[ras.band_top + 1].y_min = k;
3056 ras.band_stack[ras.band_top + 1].y_max = j;
3057
3058 ras.band_stack[ras.band_top].y_max = (Short)( k - 1 );
3059
3060 ras.band_top++;
3061 }
3062 else
3063 {
3064 if ( ras.fProfile )
3065 if ( Draw_Sweep( RAS_VAR ) )
3066 return ras.error;
3067 ras.band_top--;
3068 }
3069 }
3070
3071 return SUCCESS;
3072 }
3073
3074
3075 /**************************************************************************
3076 *
3077 * @Function:
3078 * Render_Glyph
3079 *
3080 * @Description:
3081 * Render a glyph in a bitmap. Sub-banding if needed.
3082 *
3083 * @Return:
3084 * FreeType error code. 0 means success.
3085 */
3086 static FT_Error
3087 Render_Glyph( RAS_ARG )
3088 {
3089 FT_Error error;
3090
3091
3092 Set_High_Precision( RAS_VARS ras.outline.flags &
3093 FT_OUTLINE_HIGH_PRECISION );
3094
3095 if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )
3096 ras.dropOutControl = 2;
3097 else
3098 {
3099 if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS )
3100 ras.dropOutControl = 4;
3101 else
3102 ras.dropOutControl = 0;
3103
3104 if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) )
3105 ras.dropOutControl += 1;
3106 }
3107
3108 ras.second_pass = (Bool)( !( ras.outline.flags &
3109 FT_OUTLINE_SINGLE_PASS ) );
3110
3111 /* Vertical Sweep */
3112 FT_TRACE7(( "Vertical pass (ftraster)\n" ));
3113
3114 ras.Proc_Sweep_Init = Vertical_Sweep_Init;
3115 ras.Proc_Sweep_Span = Vertical_Sweep_Span;
3116 ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
3117 ras.Proc_Sweep_Step = Vertical_Sweep_Step;
3118
3119 ras.band_top = 0;
3120 ras.band_stack[0].y_min = 0;
3121 ras.band_stack[0].y_max = (Short)( ras.target.rows - 1 );
3122
3123 ras.bWidth = (UShort)ras.target.width;
3124 ras.bOrigin = (Byte*)ras.target.buffer;
3125
3126 if ( ras.target.pitch > 0 )
3127 ras.bOrigin += (Long)( ras.target.rows - 1 ) * ras.target.pitch;
3128
3129 if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 )
3130 return error;
3131
3132 /* Horizontal Sweep */
3133 if ( ras.second_pass && ras.dropOutControl != 2 )
3134 {
3135 FT_TRACE7(( "Horizontal pass (ftraster)\n" ));
3136
3137 ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
3138 ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
3139 ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;
3140 ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
3141
3142 ras.band_top = 0;
3143 ras.band_stack[0].y_min = 0;
3144 ras.band_stack[0].y_max = (Short)( ras.target.width - 1 );
3145
3146 if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 )
3147 return error;
3148 }
3149
3150 return Raster_Err_None;
3151 }
3152
3153
3154 static void
3155 ft_black_init( black_PRaster raster )
3156 {
3157 FT_UNUSED( raster );
3158 }
3159
3160
3161 /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
3162 /**** a static object. *****/
3163
3164
3165#ifdef STANDALONE_
3166
3167
3168 static int
3169 ft_black_new( void* memory,
3170 FT_Raster *araster )
3171 {
3172 static black_TRaster the_raster;
3173 FT_UNUSED( memory );
3174
3175
3176 *araster = (FT_Raster)&the_raster;
3177 FT_ZERO( &the_raster );
3178 ft_black_init( &the_raster );
3179
3180 return 0;
3181 }
3182
3183
3184 static void
3185 ft_black_done( FT_Raster raster )
3186 {
3187 /* nothing */
3188 FT_UNUSED( raster );
3189 }
3190
3191
3192#else /* !STANDALONE_ */
3193
3194
3195 static int
3196 ft_black_new( FT_Memory memory,
3197 black_PRaster *araster )
3198 {
3199 FT_Error error;
3200 black_PRaster raster = NULL;
3201
3202
3203 *araster = 0;
3204 if ( !FT_NEW( raster ) )
3205 {
3206 raster->memory = memory;
3207 ft_black_init( raster );
3208
3209 *araster = raster;
3210 }
3211
3212 return error;
3213 }
3214
3215
3216 static void
3217 ft_black_done( black_PRaster raster )
3218 {
3219 FT_Memory memory = (FT_Memory)raster->memory;
3220
3221
3222 FT_FREE( raster );
3223 }
3224
3225
3226#endif /* !STANDALONE_ */
3227
3228
3229 static void
3230 ft_black_reset( FT_Raster raster,
3231 PByte pool_base,
3232 ULong pool_size )
3233 {
3234 FT_UNUSED( raster );
3235 FT_UNUSED( pool_base );
3236 FT_UNUSED( pool_size );
3237 }
3238
3239
3240 static int
3241 ft_black_set_mode( FT_Raster raster,
3242 ULong mode,
3243 void* args )
3244 {
3245 FT_UNUSED( raster );
3246 FT_UNUSED( mode );
3247 FT_UNUSED( args );
3248
3249 return 0;
3250 }
3251
3252
3253 static int
3254 ft_black_render( FT_Raster raster,
3255 const FT_Raster_Params* params )
3256 {
3257 const FT_Outline* outline = (const FT_Outline*)params->source;
3258 const FT_Bitmap* target_map = params->target;
3259
3260 black_TWorker worker[1];
3261
3262 Long buffer[FT_MAX_BLACK_POOL];
3263
3264
3265 if ( !raster )
3266 return FT_THROW( Not_Ini );
3267
3268 if ( !outline )
3269 return FT_THROW( Invalid );
3270
3271 /* return immediately if the outline is empty */
3272 if ( outline->n_points == 0 || outline->n_contours <= 0 )
3273 return Raster_Err_None;
3274
3275 if ( !outline->contours || !outline->points )
3276 return FT_THROW( Invalid );
3277
3278 if ( outline->n_points !=
3279 outline->contours[outline->n_contours - 1] + 1 )
3280 return FT_THROW( Invalid );
3281
3282 /* this version of the raster does not support direct rendering, sorry */
3283 if ( params->flags & FT_RASTER_FLAG_DIRECT )
3284 return FT_THROW( Unsupported );
3285
3286 if ( params->flags & FT_RASTER_FLAG_AA )
3287 return FT_THROW( Unsupported );
3288
3289 if ( !target_map )
3290 return FT_THROW( Invalid );
3291
3292 /* nothing to do */
3293 if ( !target_map->width || !target_map->rows )
3294 return Raster_Err_None;
3295
3296 if ( !target_map->buffer )
3297 return FT_THROW( Invalid );
3298
3299 ras.outline = *outline;
3300 ras.target = *target_map;
3301
3302 worker->buff = buffer;
3303 worker->sizeBuff = (&buffer)[1]; /* Points to right after buffer. */
3304
3305 return Render_Glyph( RAS_VAR );
3306 }
3307
3308
3309 FT_DEFINE_RASTER_FUNCS(
3310 ft_standard_raster,
3311
3312 FT_GLYPH_FORMAT_OUTLINE,
3313
3314 (FT_Raster_New_Func) ft_black_new, /* raster_new */
3315 (FT_Raster_Reset_Func) ft_black_reset, /* raster_reset */
3316 (FT_Raster_Set_Mode_Func)ft_black_set_mode, /* raster_set_mode */
3317 (FT_Raster_Render_Func) ft_black_render, /* raster_render */
3318 (FT_Raster_Done_Func) ft_black_done /* raster_done */
3319 )
3320
3321
3322/* END */
3323