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