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