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