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