xref: /aosp_15_r20/external/freetype/src/raster/ftraster.c (revision 63949dbd25bcc50c4e1178497ff9e9574d44fc5a)
1 /****************************************************************************
2  *
3  * ftraster.c
4  *
5  *   The FreeType glyph rasterizer (body).
6  *
7  * Copyright (C) 1996-2023 by
8  * David Turner, Robert Wilhelm, and Werner Lemberg.
9  *
10  * This file is part of the FreeType project, and may only be used,
11  * modified, and distributed under the terms of the FreeType project
12  * license, LICENSE.TXT.  By continuing to use, modify, or distribute
13  * this file you indicate that you have read the license and
14  * understand and accept it fully.
15  *
16  */
17 
18   /**************************************************************************
19    *
20    * This file can be compiled without the rest of the FreeType engine, by
21    * defining the STANDALONE_ macro when compiling it.  You also need to
22    * put the files `ftimage.h' and `ftmisc.h' into the $(incdir)
23    * directory.  Typically, you should do something like
24    *
25    * - copy `src/raster/ftraster.c' (this file) to your current directory
26    *
27    * - copy `include/freetype/ftimage.h' and `src/raster/ftmisc.h' to your
28    *   current directory
29    *
30    * - compile `ftraster' with the STANDALONE_ macro defined, as in
31    *
32    *     cc -c -DSTANDALONE_ ftraster.c
33    *
34    * The renderer can be initialized with a call to
35    * `ft_standard_raster.raster_new'; a bitmap can be generated
36    * with a call to `ft_standard_raster.raster_render'.
37    *
38    * See the comments and documentation in the file `ftimage.h' for more
39    * details on how the raster works.
40    *
41    */
42 
43 
44   /**************************************************************************
45    *
46    * This is a rewrite of the FreeType 1.x scan-line converter
47    *
48    */
49 
50 #ifdef STANDALONE_
51 
52   /* The size in bytes of the render pool used by the scan-line converter  */
53   /* to do all of its work.                                                */
54 #define FT_RENDER_POOL_SIZE  16384L
55 
56 #define FT_CONFIG_STANDARD_LIBRARY_H  <stdlib.h>
57 
58 #include <string.h>           /* for memset */
59 
60 #include "ftmisc.h"
61 #include "ftimage.h"
62 
63 #else /* !STANDALONE_ */
64 
65 #include "ftraster.h"
66 #include <freetype/internal/ftcalc.h> /* for FT_MulDiv and FT_MulDiv_No_Round */
67 #include <freetype/ftoutln.h>         /* for FT_Outline_Get_CBox              */
68 
69 #endif /* !STANDALONE_ */
70 
71 
72   /**************************************************************************
73    *
74    * A simple technical note on how the raster works
75    * -----------------------------------------------
76    *
77    *   Converting an outline into a bitmap is achieved in several steps:
78    *
79    *   1 - Decomposing the outline into successive `profiles'.  Each
80    *       profile is simply an array of scanline intersections on a given
81    *       dimension.  A profile's main attributes are
82    *
83    *       o its scanline position boundaries, i.e. `Ymin' and `Ymax'
84    *
85    *       o an array of intersection coordinates for each scanline
86    *         between `Ymin' and `Ymax'
87    *
88    *       o a direction, indicating whether it was built going `up' or
89    *         `down', as this is very important for filling rules
90    *
91    *       o its drop-out mode
92    *
93    *   2 - Sweeping the target map's scanlines in order to compute segment
94    *       `spans' which are then filled.  Additionally, this pass
95    *       performs drop-out control.
96    *
97    *   The outline data is parsed during step 1 only.  The profiles are
98    *   built from the bottom of the render pool, used as a stack.  The
99    *   following graphics shows the profile list under construction:
100    *
101    *    __________________________________________________________ _ _
102    *   |         |                 |         |                 |
103    *   | profile | coordinates for | profile | coordinates for |-->
104    *   |    1    |  profile 1      |    2    |  profile 2      |-->
105    *   |_________|_________________|_________|_________________|__ _ _
106    *
107    *   ^                                                       ^
108    *   |                                                       |
109    * start of render pool                                      top
110    *
111    *   The top of the profile stack is kept in the `top' variable.
112    *
113    *   As you can see, a profile record is pushed on top of the render
114    *   pool, which is then followed by its coordinates/intersections.  If
115    *   a change of direction is detected in the outline, a new profile is
116    *   generated until the end of the outline.
117    *
118    *   Note that when all profiles have been generated, the function
119    *   Finalize_Profile_Table() is used to record, for each profile, its
120    *   bottom-most scanline as well as the scanline above its upmost
121    *   boundary.  These positions are called `y-turns' because they (sort
122    *   of) correspond to local extrema.  They are stored in a sorted list
123    *   built from the top of the render pool as a downwards stack:
124    *
125    *     _ _ _______________________________________
126    *                           |                    |
127    *                        <--| sorted list of     |
128    *                        <--|  extrema scanlines |
129    *     _ _ __________________|____________________|
130    *
131    *                           ^                    ^
132    *                           |                    |
133    *                         maxBuff           sizeBuff = end of pool
134    *
135    *   This list is later used during the sweep phase in order to
136    *   optimize performance (see technical note on the sweep below).
137    *
138    *   Of course, the raster detects whether the two stacks collide and
139    *   handles the situation properly.
140    *
141    */
142 
143 
144   /*************************************************************************/
145   /*************************************************************************/
146   /**                                                                     **/
147   /**  CONFIGURATION MACROS                                               **/
148   /**                                                                     **/
149   /*************************************************************************/
150   /*************************************************************************/
151 
152 
153   /*************************************************************************/
154   /*************************************************************************/
155   /**                                                                     **/
156   /**  OTHER MACROS (do not change)                                       **/
157   /**                                                                     **/
158   /*************************************************************************/
159   /*************************************************************************/
160 
161   /**************************************************************************
162    *
163    * The macro FT_COMPONENT is used in trace mode.  It is an implicit
164    * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
165    * messages during execution.
166    */
167 #undef  FT_COMPONENT
168 #define FT_COMPONENT  raster
169 
170 
171 #ifdef STANDALONE_
172 
173   /* Auxiliary macros for token concatenation. */
174 #define FT_ERR_XCAT( x, y )  x ## y
175 #define FT_ERR_CAT( x, y )   FT_ERR_XCAT( x, y )
176 
177   /* This macro is used to indicate that a function parameter is unused. */
178   /* Its purpose is simply to reduce compiler warnings.  Note also that  */
179   /* simply defining it as `(void)x' doesn't avoid warnings with certain */
180   /* ANSI compilers (e.g. LCC).                                          */
181 #define FT_UNUSED( x )  (x) = (x)
182 
183   /* Disable the tracing mechanism for simplicity -- developers can      */
184   /* activate it easily by redefining these macros.                      */
185 #ifndef FT_ERROR
186 #define FT_ERROR( x )  do { } while ( 0 )     /* nothing */
187 #endif
188 
189 #ifndef FT_TRACE
190 #define FT_TRACE( x )   do { } while ( 0 )    /* nothing */
191 #define FT_TRACE1( x )  do { } while ( 0 )    /* nothing */
192 #define FT_TRACE6( x )  do { } while ( 0 )    /* nothing */
193 #define FT_TRACE7( x )  do { } while ( 0 )    /* nothing */
194 #endif
195 
196 #ifndef FT_THROW
197 #define FT_THROW( e )  FT_ERR_CAT( Raster_Err_, e )
198 #endif
199 
200 #define Raster_Err_Ok                       0
201 #define Raster_Err_Invalid_Outline         -1
202 #define Raster_Err_Cannot_Render_Glyph     -2
203 #define Raster_Err_Invalid_Argument        -3
204 #define Raster_Err_Raster_Overflow         -4
205 #define Raster_Err_Raster_Uninitialized    -5
206 #define Raster_Err_Raster_Negative_Height  -6
207 
208 #define ft_memset  memset
209 
210 #define FT_DEFINE_RASTER_FUNCS( class_, glyph_format_, raster_new_, \
211                                 raster_reset_, raster_set_mode_,    \
212                                 raster_render_, raster_done_ )      \
213           const FT_Raster_Funcs class_ =                            \
214           {                                                         \
215             glyph_format_,                                          \
216             raster_new_,                                            \
217             raster_reset_,                                          \
218             raster_set_mode_,                                       \
219             raster_render_,                                         \
220             raster_done_                                            \
221          };
222 
223 #else /* !STANDALONE_ */
224 
225 
226 #include <freetype/internal/ftobjs.h>
227 #include <freetype/internal/ftdebug.h> /* for FT_TRACE, FT_ERROR, and FT_THROW */
228 
229 #include "rasterrs.h"
230 
231 
232 #endif /* !STANDALONE_ */
233 
234 
235 #ifndef FT_MEM_SET
236 #define FT_MEM_SET( d, s, c )  ft_memset( d, s, c )
237 #endif
238 
239 #ifndef FT_MEM_ZERO
240 #define FT_MEM_ZERO( dest, count )  FT_MEM_SET( dest, 0, count )
241 #endif
242 
243 #ifndef FT_ZERO
244 #define FT_ZERO( p )  FT_MEM_ZERO( p, sizeof ( *(p) ) )
245 #endif
246 
247   /* FMulDiv means `Fast MulDiv'; it is used in case where `b' is       */
248   /* typically a small value and the result of a*b is known to fit into */
249   /* 32 bits.                                                           */
250 #define FMulDiv( a, b, c )  ( (a) * (b) / (c) )
251 
252   /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */
253   /* for clipping computations.  It simply uses the FT_MulDiv() function   */
254   /* defined in `ftcalc.h'.                                                */
255 #define SMulDiv           FT_MulDiv
256 #define SMulDiv_No_Round  FT_MulDiv_No_Round
257 
258   /* The rasterizer is a very general purpose component; please leave */
259   /* the following redefinitions there (you never know your target    */
260   /* environment).                                                    */
261 
262 #ifndef TRUE
263 #define TRUE   1
264 #endif
265 
266 #ifndef FALSE
267 #define FALSE  0
268 #endif
269 
270 #ifndef NULL
271 #define NULL  (void*)0
272 #endif
273 
274 #ifndef SUCCESS
275 #define SUCCESS  0
276 #endif
277 
278 #ifndef FAILURE
279 #define FAILURE  1
280 #endif
281 
282 
283 #define MaxBezier  32   /* The maximum number of stacked Bezier curves. */
284                         /* Setting this constant to more than 32 is a   */
285                         /* pure waste of space.                         */
286 
287 #define Pixel_Bits  6   /* fractional bits of *input* coordinates */
288 
289 
290   /*************************************************************************/
291   /*************************************************************************/
292   /**                                                                     **/
293   /**  SIMPLE TYPE DECLARATIONS                                           **/
294   /**                                                                     **/
295   /*************************************************************************/
296   /*************************************************************************/
297 
298   typedef int             Int;
299   typedef unsigned int    UInt;
300   typedef short           Short;
301   typedef unsigned short  UShort, *PUShort;
302   typedef long            Long, *PLong;
303   typedef unsigned long   ULong;
304 
305   typedef unsigned char   Byte, *PByte;
306   typedef char            Bool;
307 
308 
309   typedef union  Alignment_
310   {
311     Long    l;
312     void*   p;
313     void  (*f)(void);
314 
315   } Alignment, *PAlignment;
316 
317 
318   typedef struct  TPoint_
319   {
320     Long  x;
321     Long  y;
322 
323   } TPoint;
324 
325 
326   /* values for the `flags' bit field */
327 #define Flow_Up           0x08U
328 #define Overshoot_Top     0x10U
329 #define Overshoot_Bottom  0x20U
330 
331 
332   /* States of each line, arc, and profile */
333   typedef enum  TStates_
334   {
335     Unknown_State,
336     Ascending_State,
337     Descending_State,
338     Flat_State
339 
340   } TStates;
341 
342 
343   typedef struct TProfile_  TProfile;
344   typedef TProfile*         PProfile;
345 
346   struct  TProfile_
347   {
348     FT_F26Dot6  X;           /* current coordinate during sweep          */
349     PProfile    link;        /* link to next profile (various purposes)  */
350     PLong       offset;      /* start of profile's data in render pool   */
351     UShort      flags;       /* Bit 0-2: drop-out mode                   */
352                              /* Bit 3: profile orientation (up/down)     */
353                              /* Bit 4: is top profile?                   */
354                              /* Bit 5: is bottom profile?                */
355     Long        height;      /* profile's height in scanlines            */
356     Long        start;       /* profile's starting scanline              */
357 
358     Int         countL;      /* number of lines to step before this      */
359                              /* profile becomes drawable                 */
360 
361     PProfile    next;        /* next profile in same contour, used       */
362                              /* during drop-out control                  */
363   };
364 
365   typedef PProfile   TProfileList;
366   typedef PProfile*  PProfileList;
367 
368 
369 #define AlignProfileSize \
370   ( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( Long ) )
371 
372 
373 #undef RAS_ARG
374 #undef RAS_ARGS
375 #undef RAS_VAR
376 #undef RAS_VARS
377 
378 #ifdef FT_STATIC_RASTER
379 
380 
381 #define RAS_ARGS       /* void */
382 #define RAS_ARG        void
383 
384 #define RAS_VARS       /* void */
385 #define RAS_VAR        /* void */
386 
387 #define FT_UNUSED_RASTER  do { } while ( 0 )
388 
389 
390 #else /* !FT_STATIC_RASTER */
391 
392 
393 #define RAS_ARGS       black_PWorker  worker,
394 #define RAS_ARG        black_PWorker  worker
395 
396 #define RAS_VARS       worker,
397 #define RAS_VAR        worker
398 
399 #define FT_UNUSED_RASTER  FT_UNUSED( worker )
400 
401 
402 #endif /* !FT_STATIC_RASTER */
403 
404 
405   typedef struct black_TWorker_  black_TWorker, *black_PWorker;
406 
407 
408   /* prototypes used for sweep function dispatch */
409   typedef void
410   Function_Sweep_Init( RAS_ARGS Short  min,
411                                 Short  max );
412 
413   typedef void
414   Function_Sweep_Span( RAS_ARGS Short       y,
415                                 FT_F26Dot6  x1,
416                                 FT_F26Dot6  x2,
417                                 PProfile    left,
418                                 PProfile    right );
419 
420   typedef void
421   Function_Sweep_Step( RAS_ARG );
422 
423 
424   /* NOTE: These operations are only valid on 2's complement processors */
425 #undef FLOOR
426 #undef CEILING
427 #undef TRUNC
428 #undef SCALED
429 
430 #define FLOOR( x )    ( (x) & -ras.precision )
431 #define CEILING( x )  ( ( (x) + ras.precision - 1 ) & -ras.precision )
432 #define TRUNC( x )    ( (Long)(x) >> ras.precision_bits )
433 #define FRAC( x )     ( (x) & ( ras.precision - 1 ) )
434 
435   /* scale and shift grid to pixel centers */
436 #define SCALED( x )   ( (x) * ras.precision_scale - ras.precision_half )
437 
438 #define IS_BOTTOM_OVERSHOOT( x ) \
439           (Bool)( CEILING( x ) - x >= ras.precision_half )
440 #define IS_TOP_OVERSHOOT( x )    \
441           (Bool)( x - FLOOR( x ) >= ras.precision_half )
442 
443   /* Smart dropout rounding to find which pixel is closer to span ends. */
444   /* To mimick Windows, symmetric cases break down indepenently of the  */
445   /* precision.                                                         */
446 #define SMART( p, q )  FLOOR( ( (p) + (q) + ras.precision * 63 / 64 ) >> 1 )
447 
448 #if FT_RENDER_POOL_SIZE > 2048
449 #define FT_MAX_BLACK_POOL  ( FT_RENDER_POOL_SIZE / sizeof ( Long ) )
450 #else
451 #define FT_MAX_BLACK_POOL  ( 2048 / sizeof ( Long ) )
452 #endif
453 
454   /* The most used variables are positioned at the top of the structure. */
455   /* Thus, their offset can be coded with less opcodes, resulting in a   */
456   /* smaller executable.                                                 */
457 
458   struct  black_TWorker_
459   {
460     Int         precision_bits;     /* precision related variables         */
461     Int         precision;
462     Int         precision_half;
463     Int         precision_scale;
464     Int         precision_step;
465     Int         precision_jitter;
466 
467     PLong       buff;               /* The profiles buffer                 */
468     PLong       sizeBuff;           /* Render pool size                    */
469     PLong       maxBuff;            /* Profiles buffer size                */
470     PLong       top;                /* Current cursor in buffer            */
471 
472     FT_Error    error;
473 
474     Int         numTurns;           /* number of Y-turns in outline        */
475 
476     Byte        dropOutControl;     /* current drop_out control method     */
477 
478     UShort      bWidth;             /* target bitmap width                 */
479     PByte       bOrigin;            /* target bitmap bottom-left origin    */
480     PByte       bLine;              /* target bitmap current line          */
481 
482     Long        lastX, lastY;
483     Long        minY, maxY;
484 
485     UShort      num_Profs;          /* current number of profiles          */
486 
487     Bool        fresh;              /* signals a fresh new profile which   */
488                                     /* `start' field must be completed     */
489     Bool        joint;              /* signals that the last arc ended     */
490                                     /* exactly on a scanline.  Allows      */
491                                     /* removal of doublets                 */
492     PProfile    cProfile;           /* current profile                     */
493     PProfile    fProfile;           /* head of linked list of profiles     */
494     PProfile    gProfile;           /* contour's first profile in case     */
495                                     /* of impact                           */
496 
497     TStates     state;              /* rendering state                     */
498 
499     FT_Bitmap   target;             /* description of target bit/pixmap    */
500     FT_Outline  outline;
501 
502     /* dispatch variables */
503 
504     Function_Sweep_Init*  Proc_Sweep_Init;
505     Function_Sweep_Span*  Proc_Sweep_Span;
506     Function_Sweep_Span*  Proc_Sweep_Drop;
507     Function_Sweep_Step*  Proc_Sweep_Step;
508 
509   };
510 
511 
512   typedef struct  black_TRaster_
513   {
514     void*          memory;
515 
516   } black_TRaster, *black_PRaster;
517 
518 #ifdef FT_STATIC_RASTER
519 
520   static black_TWorker  ras;
521 
522 #else /* !FT_STATIC_RASTER */
523 
524 #define ras  (*worker)
525 
526 #endif /* !FT_STATIC_RASTER */
527 
528 
529   /*************************************************************************/
530   /*************************************************************************/
531   /**                                                                     **/
532   /**  PROFILES COMPUTATION                                               **/
533   /**                                                                     **/
534   /*************************************************************************/
535   /*************************************************************************/
536 
537 
538   /**************************************************************************
539    *
540    * @Function:
541    *   Set_High_Precision
542    *
543    * @Description:
544    *   Set precision variables according to param flag.
545    *
546    * @Input:
547    *   High ::
548    *     Set to True for high precision (typically for ppem < 24),
549    *     false otherwise.
550    */
551   static void
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 Int first,Int last,Int flipped)1745   Decompose_Curve( RAS_ARGS Int  first,
1746                             Int  last,
1747                             Int  flipped )
1748   {
1749     FT_Vector   v_last;
1750     FT_Vector   v_control;
1751     FT_Vector   v_start;
1752 
1753     FT_Vector*  points;
1754     FT_Vector*  point;
1755     FT_Vector*  limit;
1756     char*       tags;
1757 
1758     UInt        tag;       /* current point's state           */
1759 
1760 
1761     points = ras.outline.points;
1762     limit  = points + last;
1763 
1764     v_start.x = SCALED( points[first].x );
1765     v_start.y = SCALED( points[first].y );
1766     v_last.x  = SCALED( points[last].x );
1767     v_last.y  = SCALED( points[last].y );
1768 
1769     if ( flipped )
1770     {
1771       SWAP_( v_start.x, v_start.y );
1772       SWAP_( v_last.x, v_last.y );
1773     }
1774 
1775     v_control = v_start;
1776 
1777     point = points + first;
1778     tags  = ras.outline.tags + first;
1779 
1780     /* set scan mode if necessary */
1781     if ( tags[0] & FT_CURVE_TAG_HAS_SCANMODE )
1782       ras.dropOutControl = (Byte)tags[0] >> 5;
1783 
1784     tag = FT_CURVE_TAG( tags[0] );
1785 
1786     /* A contour cannot start with a cubic control point! */
1787     if ( tag == FT_CURVE_TAG_CUBIC )
1788       goto Invalid_Outline;
1789 
1790     /* check first point to determine origin */
1791     if ( tag == FT_CURVE_TAG_CONIC )
1792     {
1793       /* first point is conic control.  Yes, this happens. */
1794       if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_CURVE_TAG_ON )
1795       {
1796         /* start at last point if it is on the curve */
1797         v_start = v_last;
1798         limit--;
1799       }
1800       else
1801       {
1802         /* if both first and last points are conic,         */
1803         /* start at their middle and record its position    */
1804         /* for closure                                      */
1805         v_start.x = ( v_start.x + v_last.x ) / 2;
1806         v_start.y = ( v_start.y + v_last.y ) / 2;
1807 
1808      /* v_last = v_start; */
1809       }
1810       point--;
1811       tags--;
1812     }
1813 
1814     ras.lastX = v_start.x;
1815     ras.lastY = v_start.y;
1816 
1817     while ( point < limit )
1818     {
1819       point++;
1820       tags++;
1821 
1822       tag = FT_CURVE_TAG( tags[0] );
1823 
1824       switch ( tag )
1825       {
1826       case FT_CURVE_TAG_ON:  /* emit a single line_to */
1827         {
1828           Long  x, y;
1829 
1830 
1831           x = SCALED( point->x );
1832           y = SCALED( point->y );
1833           if ( flipped )
1834             SWAP_( x, y );
1835 
1836           if ( Line_To( RAS_VARS x, y ) )
1837             goto Fail;
1838           continue;
1839         }
1840 
1841       case FT_CURVE_TAG_CONIC:  /* consume conic arcs */
1842         v_control.x = SCALED( point[0].x );
1843         v_control.y = SCALED( point[0].y );
1844 
1845         if ( flipped )
1846           SWAP_( v_control.x, v_control.y );
1847 
1848       Do_Conic:
1849         if ( point < limit )
1850         {
1851           FT_Vector  v_middle;
1852           Long       x, y;
1853 
1854 
1855           point++;
1856           tags++;
1857           tag = FT_CURVE_TAG( tags[0] );
1858 
1859           x = SCALED( point[0].x );
1860           y = SCALED( point[0].y );
1861 
1862           if ( flipped )
1863             SWAP_( x, y );
1864 
1865           if ( tag == FT_CURVE_TAG_ON )
1866           {
1867             if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) )
1868               goto Fail;
1869             continue;
1870           }
1871 
1872           if ( tag != FT_CURVE_TAG_CONIC )
1873             goto Invalid_Outline;
1874 
1875           v_middle.x = ( v_control.x + x ) / 2;
1876           v_middle.y = ( v_control.y + y ) / 2;
1877 
1878           if ( Conic_To( RAS_VARS v_control.x, v_control.y,
1879                                   v_middle.x,  v_middle.y ) )
1880             goto Fail;
1881 
1882           v_control.x = x;
1883           v_control.y = y;
1884 
1885           goto Do_Conic;
1886         }
1887 
1888         if ( Conic_To( RAS_VARS v_control.x, v_control.y,
1889                                 v_start.x,   v_start.y ) )
1890           goto Fail;
1891 
1892         goto Close;
1893 
1894       default:  /* FT_CURVE_TAG_CUBIC */
1895         {
1896           Long  x1, y1, x2, y2, x3, y3;
1897 
1898 
1899           if ( point + 1 > limit                             ||
1900                FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
1901             goto Invalid_Outline;
1902 
1903           point += 2;
1904           tags  += 2;
1905 
1906           x1 = SCALED( point[-2].x );
1907           y1 = SCALED( point[-2].y );
1908           x2 = SCALED( point[-1].x );
1909           y2 = SCALED( point[-1].y );
1910 
1911           if ( flipped )
1912           {
1913             SWAP_( x1, y1 );
1914             SWAP_( x2, y2 );
1915           }
1916 
1917           if ( point <= limit )
1918           {
1919             x3 = SCALED( point[0].x );
1920             y3 = SCALED( point[0].y );
1921 
1922             if ( flipped )
1923               SWAP_( x3, y3 );
1924 
1925             if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) )
1926               goto Fail;
1927             continue;
1928           }
1929 
1930           if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) )
1931             goto Fail;
1932           goto Close;
1933         }
1934       }
1935     }
1936 
1937     /* close the contour with a line segment */
1938     if ( Line_To( RAS_VARS v_start.x, v_start.y ) )
1939       goto Fail;
1940 
1941   Close:
1942     return SUCCESS;
1943 
1944   Invalid_Outline:
1945     ras.error = FT_THROW( Invalid_Outline );
1946 
1947   Fail:
1948     return FAILURE;
1949   }
1950 
1951 
1952   /**************************************************************************
1953    *
1954    * @Function:
1955    *   Convert_Glyph
1956    *
1957    * @Description:
1958    *   Convert a glyph into a series of segments and arcs and make a
1959    *   profiles list with them.
1960    *
1961    * @Input:
1962    *   flipped ::
1963    *     If set, flip the direction of curve.
1964    *
1965    * @Return:
1966    *   SUCCESS on success, FAILURE if any error was encountered during
1967    *   rendering.
1968    */
1969   static Bool
Convert_Glyph(RAS_ARGS Int flipped)1970   Convert_Glyph( RAS_ARGS Int  flipped )
1971   {
1972     Int  i;
1973     Int  first, last;
1974 
1975 
1976     ras.fProfile = NULL;
1977     ras.joint    = FALSE;
1978     ras.fresh    = FALSE;
1979 
1980     ras.maxBuff  = ras.sizeBuff - AlignProfileSize;
1981 
1982     ras.numTurns = 0;
1983 
1984     ras.cProfile         = (PProfile)ras.top;
1985     ras.cProfile->offset = ras.top;
1986     ras.num_Profs        = 0;
1987 
1988     last = -1;
1989     for ( i = 0; i < ras.outline.n_contours; i++ )
1990     {
1991       PProfile  lastProfile;
1992       Bool      o;
1993 
1994 
1995       ras.state    = Unknown_State;
1996       ras.gProfile = NULL;
1997 
1998       first = last + 1;
1999       last  = ras.outline.contours[i];
2000 
2001       if ( Decompose_Curve( RAS_VARS first, last, flipped ) )
2002         return FAILURE;
2003 
2004       /* we must now check whether the extreme arcs join or not */
2005       if ( FRAC( ras.lastY ) == 0 &&
2006            ras.lastY >= ras.minY  &&
2007            ras.lastY <= ras.maxY  )
2008         if ( ras.gProfile                        &&
2009              ( ras.gProfile->flags & Flow_Up ) ==
2010                ( ras.cProfile->flags & Flow_Up ) )
2011           ras.top--;
2012         /* Note that ras.gProfile can be nil if the contour was too small */
2013         /* to be drawn.                                                   */
2014 
2015       lastProfile = ras.cProfile;
2016       if ( ras.top != ras.cProfile->offset &&
2017            ( ras.cProfile->flags & Flow_Up ) )
2018         o = IS_TOP_OVERSHOOT( ras.lastY );
2019       else
2020         o = IS_BOTTOM_OVERSHOOT( ras.lastY );
2021       if ( End_Profile( RAS_VARS o ) )
2022         return FAILURE;
2023 
2024       /* close the `next profile in contour' linked list */
2025       if ( ras.gProfile )
2026         lastProfile->next = ras.gProfile;
2027     }
2028 
2029     if ( Finalize_Profile_Table( RAS_VAR ) )
2030       return FAILURE;
2031 
2032     return (Bool)( ras.top < ras.maxBuff ? SUCCESS : FAILURE );
2033   }
2034 
2035 
2036   /*************************************************************************/
2037   /*************************************************************************/
2038   /**                                                                     **/
2039   /**  SCAN-LINE SWEEPS AND DRAWING                                       **/
2040   /**                                                                     **/
2041   /*************************************************************************/
2042   /*************************************************************************/
2043 
2044 
2045   /**************************************************************************
2046    *
2047    * Init_Linked
2048    *
2049    *   Initializes an empty linked list.
2050    */
2051   static void
Init_Linked(TProfileList * l)2052   Init_Linked( TProfileList*  l )
2053   {
2054     *l = NULL;
2055   }
2056 
2057 
2058   /**************************************************************************
2059    *
2060    * InsNew
2061    *
2062    *   Inserts a new profile in a linked list.
2063    */
2064   static void
InsNew(PProfileList list,PProfile profile)2065   InsNew( PProfileList  list,
2066           PProfile      profile )
2067   {
2068     PProfile  *old, current;
2069     Long       x;
2070 
2071 
2072     old     = list;
2073     current = *old;
2074     x       = profile->X;
2075 
2076     while ( current )
2077     {
2078       if ( x < current->X )
2079         break;
2080       old     = &current->link;
2081       current = *old;
2082     }
2083 
2084     profile->link = current;
2085     *old          = profile;
2086   }
2087 
2088 
2089   /**************************************************************************
2090    *
2091    * DelOld
2092    *
2093    *   Removes an old profile from a linked list.
2094    */
2095   static void
DelOld(PProfileList list,const PProfile profile)2096   DelOld( PProfileList    list,
2097           const PProfile  profile )
2098   {
2099     PProfile  *old, current;
2100 
2101 
2102     old     = list;
2103     current = *old;
2104 
2105     while ( current )
2106     {
2107       if ( current == profile )
2108       {
2109         *old = current->link;
2110         return;
2111       }
2112 
2113       old     = &current->link;
2114       current = *old;
2115     }
2116 
2117     /* we should never get there, unless the profile was not part of */
2118     /* the list.                                                     */
2119   }
2120 
2121 
2122   /**************************************************************************
2123    *
2124    * Sort
2125    *
2126    *   Sorts a trace list.  In 95%, the list is already sorted.  We need
2127    *   an algorithm which is fast in this case.  Bubble sort is enough
2128    *   and simple.
2129    */
2130   static void
Sort(PProfileList list)2131   Sort( PProfileList  list )
2132   {
2133     PProfile  *old, current, next;
2134 
2135 
2136     /* First, set the new X coordinate of each profile */
2137     current = *list;
2138     while ( current )
2139     {
2140       current->X       = *current->offset;
2141       current->offset += ( current->flags & Flow_Up ) ? 1 : -1;
2142       current->height--;
2143       current = current->link;
2144     }
2145 
2146     /* Then sort them */
2147     old     = list;
2148     current = *old;
2149 
2150     if ( !current )
2151       return;
2152 
2153     next = current->link;
2154 
2155     while ( next )
2156     {
2157       if ( current->X <= next->X )
2158       {
2159         old     = &current->link;
2160         current = *old;
2161 
2162         if ( !current )
2163           return;
2164       }
2165       else
2166       {
2167         *old          = next;
2168         current->link = next->link;
2169         next->link    = current;
2170 
2171         old     = list;
2172         current = *old;
2173       }
2174 
2175       next = current->link;
2176     }
2177   }
2178 
2179 
2180   /**************************************************************************
2181    *
2182    * Vertical Sweep Procedure Set
2183    *
2184    * These four routines are used during the vertical black/white sweep
2185    * phase by the generic Draw_Sweep() function.
2186    *
2187    */
2188 
2189   static void
Vertical_Sweep_Init(RAS_ARGS Short min,Short max)2190   Vertical_Sweep_Init( RAS_ARGS Short  min,
2191                                 Short  max )
2192   {
2193     FT_UNUSED( max );
2194 
2195 
2196     ras.bLine = ras.bOrigin - min * ras.target.pitch;
2197   }
2198 
2199 
2200   static void
Vertical_Sweep_Span(RAS_ARGS Short y,FT_F26Dot6 x1,FT_F26Dot6 x2,PProfile left,PProfile right)2201   Vertical_Sweep_Span( RAS_ARGS Short       y,
2202                                 FT_F26Dot6  x1,
2203                                 FT_F26Dot6  x2,
2204                                 PProfile    left,
2205                                 PProfile    right )
2206   {
2207     Long  e1, e2;
2208 
2209     Int  dropOutControl = left->flags & 7;
2210 
2211     FT_UNUSED( y );
2212     FT_UNUSED( left );
2213     FT_UNUSED( right );
2214 
2215 
2216     /* in high-precision mode, we need 12 digits after the comma to */
2217     /* represent multiples of 1/(1<<12) = 1/4096                    */
2218     FT_TRACE7(( "  y=%d x=[% .12f;% .12f]",
2219                 y,
2220                 (double)x1 / (double)ras.precision,
2221                 (double)x2 / (double)ras.precision ));
2222 
2223     /* Drop-out control */
2224 
2225     e1 = CEILING( x1 );
2226     e2 = FLOOR( x2 );
2227 
2228     /* take care of the special case where both the left */
2229     /* and right contour lie exactly on pixel centers    */
2230     if ( dropOutControl != 2                             &&
2231          x2 - x1 - ras.precision <= ras.precision_jitter &&
2232          e1 != x1 && e2 != x2                            )
2233       e2 = e1;
2234 
2235     e1 = TRUNC( e1 );
2236     e2 = TRUNC( e2 );
2237 
2238     if ( e2 >= 0 && e1 < ras.bWidth )
2239     {
2240       Byte*  target;
2241 
2242       Int   c1, c2;
2243       Byte  f1, f2;
2244 
2245 
2246       if ( e1 < 0 )
2247         e1 = 0;
2248       if ( e2 >= ras.bWidth )
2249         e2 = ras.bWidth - 1;
2250 
2251       FT_TRACE7(( " -> x=[%ld;%ld]", e1, e2 ));
2252 
2253       c1 = (Short)( e1 >> 3 );
2254       c2 = (Short)( e2 >> 3 );
2255 
2256       f1 = (Byte)  ( 0xFF >> ( e1 & 7 ) );
2257       f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) );
2258 
2259       target = ras.bLine + c1;
2260       c2 -= c1;
2261 
2262       if ( c2 > 0 )
2263       {
2264         target[0] |= f1;
2265 
2266         /* memset() is slower than the following code on many platforms. */
2267         /* This is due to the fact that, in the vast majority of cases,  */
2268         /* the span length in bytes is relatively small.                 */
2269         while ( --c2 > 0 )
2270           *( ++target ) = 0xFF;
2271 
2272         target[1] |= f2;
2273       }
2274       else
2275         *target |= ( f1 & f2 );
2276     }
2277 
2278     FT_TRACE7(( "\n" ));
2279   }
2280 
2281 
2282   static void
Vertical_Sweep_Drop(RAS_ARGS Short y,FT_F26Dot6 x1,FT_F26Dot6 x2,PProfile left,PProfile right)2283   Vertical_Sweep_Drop( RAS_ARGS Short       y,
2284                                 FT_F26Dot6  x1,
2285                                 FT_F26Dot6  x2,
2286                                 PProfile    left,
2287                                 PProfile    right )
2288   {
2289     Long   e1, e2, pxl;
2290     Short  c1, f1;
2291 
2292 
2293     FT_TRACE7(( "  y=%d x=[% .12f;% .12f]",
2294                 y,
2295                 (double)x1 / (double)ras.precision,
2296                 (double)x2 / (double)ras.precision ));
2297 
2298     /* Drop-out control */
2299 
2300     /*   e2            x2                    x1           e1   */
2301     /*                                                         */
2302     /*                 ^                     |                 */
2303     /*                 |                     |                 */
2304     /*   +-------------+---------------------+------------+    */
2305     /*                 |                     |                 */
2306     /*                 |                     v                 */
2307     /*                                                         */
2308     /* pixel         contour              contour       pixel  */
2309     /* center                                           center */
2310 
2311     /* drop-out mode    scan conversion rules (as defined in OpenType) */
2312     /* --------------------------------------------------------------- */
2313     /*  0                1, 2, 3                                       */
2314     /*  1                1, 2, 4                                       */
2315     /*  2                1, 2                                          */
2316     /*  3                same as mode 2                                */
2317     /*  4                1, 2, 5                                       */
2318     /*  5                1, 2, 6                                       */
2319     /*  6, 7             same as mode 2                                */
2320 
2321     e1  = CEILING( x1 );
2322     e2  = FLOOR  ( x2 );
2323     pxl = e1;
2324 
2325     if ( e1 > e2 )
2326     {
2327       Int  dropOutControl = left->flags & 7;
2328 
2329 
2330       if ( e1 == e2 + ras.precision )
2331       {
2332         switch ( dropOutControl )
2333         {
2334         case 0: /* simple drop-outs including stubs */
2335           pxl = e2;
2336           break;
2337 
2338         case 4: /* smart drop-outs including stubs */
2339           pxl = SMART( x1, x2 );
2340           break;
2341 
2342         case 1: /* simple drop-outs excluding stubs */
2343         case 5: /* smart drop-outs excluding stubs  */
2344 
2345           /* Drop-out Control Rules #4 and #6 */
2346 
2347           /* The specification neither provides an exact definition */
2348           /* of a `stub' nor gives exact rules to exclude them.     */
2349           /*                                                        */
2350           /* Here the constraints we use to recognize a stub.       */
2351           /*                                                        */
2352           /*  upper stub:                                           */
2353           /*                                                        */
2354           /*   - P_Left and P_Right are in the same contour         */
2355           /*   - P_Right is the successor of P_Left in that contour */
2356           /*   - y is the top of P_Left and P_Right                 */
2357           /*                                                        */
2358           /*  lower stub:                                           */
2359           /*                                                        */
2360           /*   - P_Left and P_Right are in the same contour         */
2361           /*   - P_Left is the successor of P_Right in that contour */
2362           /*   - y is the bottom of P_Left                          */
2363           /*                                                        */
2364           /* We draw a stub if the following constraints are met.   */
2365           /*                                                        */
2366           /*   - for an upper or lower stub, there is top or bottom */
2367           /*     overshoot, respectively                            */
2368           /*   - the covered interval is greater or equal to a half */
2369           /*     pixel                                              */
2370 
2371           /* upper stub test */
2372           if ( left->next == right                &&
2373                left->height <= 0                  &&
2374                !( left->flags & Overshoot_Top   &&
2375                   x2 - x1 >= ras.precision_half ) )
2376             goto Exit;
2377 
2378           /* lower stub test */
2379           if ( right->next == left                 &&
2380                left->start == y                    &&
2381                !( left->flags & Overshoot_Bottom &&
2382                   x2 - x1 >= ras.precision_half  ) )
2383             goto Exit;
2384 
2385           if ( dropOutControl == 1 )
2386             pxl = e2;
2387           else
2388             pxl = SMART( x1, x2 );
2389           break;
2390 
2391         default: /* modes 2, 3, 6, 7 */
2392           goto Exit;  /* no drop-out control */
2393         }
2394 
2395         /* undocumented but confirmed: If the drop-out would result in a  */
2396         /* pixel outside of the bounding box, use the pixel inside of the */
2397         /* bounding box instead                                           */
2398         if ( pxl < 0 )
2399           pxl = e1;
2400         else if ( TRUNC( pxl ) >= ras.bWidth )
2401           pxl = e2;
2402 
2403         /* check that the other pixel isn't set */
2404         e1 = ( pxl == e1 ) ? e2 : e1;
2405 
2406         e1 = TRUNC( e1 );
2407 
2408         c1 = (Short)( e1 >> 3 );
2409         f1 = (Short)( e1 &  7 );
2410 
2411         if ( e1 >= 0 && e1 < ras.bWidth     &&
2412              ras.bLine[c1] & ( 0x80 >> f1 ) )
2413           goto Exit;
2414       }
2415       else
2416         goto Exit;
2417     }
2418 
2419     e1 = TRUNC( pxl );
2420 
2421     if ( e1 >= 0 && e1 < ras.bWidth )
2422     {
2423       FT_TRACE7(( " -> x=%ld", e1 ));
2424 
2425       c1 = (Short)( e1 >> 3 );
2426       f1 = (Short)( e1 & 7 );
2427 
2428       ras.bLine[c1] |= (char)( 0x80 >> f1 );
2429     }
2430 
2431   Exit:
2432     FT_TRACE7(( " dropout=%d\n", left->flags & 7 ));
2433   }
2434 
2435 
2436   static void
Vertical_Sweep_Step(RAS_ARG)2437   Vertical_Sweep_Step( RAS_ARG )
2438   {
2439     ras.bLine -= ras.target.pitch;
2440   }
2441 
2442 
2443   /************************************************************************
2444    *
2445    * Horizontal Sweep Procedure Set
2446    *
2447    * These four routines are used during the horizontal black/white
2448    * sweep phase by the generic Draw_Sweep() function.
2449    *
2450    */
2451 
2452   static void
Horizontal_Sweep_Init(RAS_ARGS Short min,Short max)2453   Horizontal_Sweep_Init( RAS_ARGS Short  min,
2454                                   Short  max )
2455   {
2456     /* nothing, really */
2457     FT_UNUSED_RASTER;
2458     FT_UNUSED( min );
2459     FT_UNUSED( max );
2460   }
2461 
2462 
2463   static void
Horizontal_Sweep_Span(RAS_ARGS Short y,FT_F26Dot6 x1,FT_F26Dot6 x2,PProfile left,PProfile right)2464   Horizontal_Sweep_Span( RAS_ARGS Short       y,
2465                                   FT_F26Dot6  x1,
2466                                   FT_F26Dot6  x2,
2467                                   PProfile    left,
2468                                   PProfile    right )
2469   {
2470     Long  e1, e2;
2471 
2472     FT_UNUSED( left );
2473     FT_UNUSED( right );
2474 
2475 
2476     FT_TRACE7(( "  x=%d y=[% .12f;% .12f]",
2477                 y,
2478                 (double)x1 / (double)ras.precision,
2479                 (double)x2 / (double)ras.precision ));
2480 
2481     /* We should not need this procedure but the vertical sweep   */
2482     /* mishandles horizontal lines through pixel centers.  So we  */
2483     /* have to check perfectly aligned span edges here.           */
2484     /*                                                            */
2485     /* XXX: Can we handle horizontal lines better and drop this?  */
2486 
2487     e1 = CEILING( x1 );
2488 
2489     if ( x1 == e1 )
2490     {
2491       e1 = TRUNC( e1 );
2492 
2493       if ( e1 >= 0 && (ULong)e1 < ras.target.rows )
2494       {
2495         Byte   f1;
2496         PByte  bits;
2497 
2498 
2499         bits = ras.bOrigin + ( y >> 3 ) - e1 * ras.target.pitch;
2500         f1   = (Byte)( 0x80 >> ( y & 7 ) );
2501 
2502         FT_TRACE7(( bits[0] & f1 ? " redundant"
2503                                  : " -> y=%ld edge", e1 ));
2504 
2505         bits[0] |= f1;
2506       }
2507     }
2508 
2509     e2 = FLOOR  ( x2 );
2510 
2511     if ( x2 == e2 )
2512     {
2513       e2 = TRUNC( e2 );
2514 
2515       if ( e2 >= 0 && (ULong)e2 < ras.target.rows )
2516       {
2517         Byte   f1;
2518         PByte  bits;
2519 
2520 
2521         bits = ras.bOrigin + ( y >> 3 ) - e2 * ras.target.pitch;
2522         f1   = (Byte)( 0x80 >> ( y & 7 ) );
2523 
2524         FT_TRACE7(( bits[0] & f1 ? " redundant"
2525                                  : " -> y=%ld edge", e2 ));
2526 
2527         bits[0] |= f1;
2528       }
2529     }
2530 
2531     FT_TRACE7(( "\n" ));
2532   }
2533 
2534 
2535   static void
Horizontal_Sweep_Drop(RAS_ARGS Short y,FT_F26Dot6 x1,FT_F26Dot6 x2,PProfile left,PProfile right)2536   Horizontal_Sweep_Drop( RAS_ARGS Short       y,
2537                                   FT_F26Dot6  x1,
2538                                   FT_F26Dot6  x2,
2539                                   PProfile    left,
2540                                   PProfile    right )
2541   {
2542     Long   e1, e2, pxl;
2543     PByte  bits;
2544     Byte   f1;
2545 
2546 
2547     FT_TRACE7(( "  x=%d y=[% .12f;% .12f]",
2548                 y,
2549                 (double)x1 / (double)ras.precision,
2550                 (double)x2 / (double)ras.precision ));
2551 
2552     /* During the horizontal sweep, we only take care of drop-outs */
2553 
2554     /* e1     +       <-- pixel center */
2555     /*        |                        */
2556     /* x1  ---+-->    <-- contour      */
2557     /*        |                        */
2558     /*        |                        */
2559     /* x2  <--+---    <-- contour      */
2560     /*        |                        */
2561     /*        |                        */
2562     /* e2     +       <-- pixel center */
2563 
2564     e1  = CEILING( x1 );
2565     e2  = FLOOR  ( x2 );
2566     pxl = e1;
2567 
2568     if ( e1 > e2 )
2569     {
2570       Int  dropOutControl = left->flags & 7;
2571 
2572 
2573       if ( e1 == e2 + ras.precision )
2574       {
2575         switch ( dropOutControl )
2576         {
2577         case 0: /* simple drop-outs including stubs */
2578           pxl = e2;
2579           break;
2580 
2581         case 4: /* smart drop-outs including stubs */
2582           pxl = SMART( x1, x2 );
2583           break;
2584 
2585         case 1: /* simple drop-outs excluding stubs */
2586         case 5: /* smart drop-outs excluding stubs  */
2587           /* see Vertical_Sweep_Drop for details */
2588 
2589           /* rightmost stub test */
2590           if ( left->next == right                &&
2591                left->height <= 0                  &&
2592                !( left->flags & Overshoot_Top   &&
2593                   x2 - x1 >= ras.precision_half ) )
2594             goto Exit;
2595 
2596           /* leftmost stub test */
2597           if ( right->next == left                 &&
2598                left->start == y                    &&
2599                !( left->flags & Overshoot_Bottom &&
2600                   x2 - x1 >= ras.precision_half  ) )
2601             goto Exit;
2602 
2603           if ( dropOutControl == 1 )
2604             pxl = e2;
2605           else
2606             pxl = SMART( x1, x2 );
2607           break;
2608 
2609         default: /* modes 2, 3, 6, 7 */
2610           goto Exit;  /* no drop-out control */
2611         }
2612 
2613         /* undocumented but confirmed: If the drop-out would result in a  */
2614         /* pixel outside of the bounding box, use the pixel inside of the */
2615         /* bounding box instead                                           */
2616         if ( pxl < 0 )
2617           pxl = e1;
2618         else if ( (ULong)( TRUNC( pxl ) ) >= ras.target.rows )
2619           pxl = e2;
2620 
2621         /* check that the other pixel isn't set */
2622         e1 = ( pxl == e1 ) ? e2 : e1;
2623 
2624         e1 = TRUNC( e1 );
2625 
2626         bits = ras.bOrigin + ( y >> 3 ) - e1 * ras.target.pitch;
2627         f1   = (Byte)( 0x80 >> ( y & 7 ) );
2628 
2629         if ( e1 >= 0                     &&
2630              (ULong)e1 < ras.target.rows &&
2631              *bits & f1                  )
2632           goto Exit;
2633       }
2634       else
2635         goto Exit;
2636     }
2637 
2638     e1 = TRUNC( pxl );
2639 
2640     if ( e1 >= 0 && (ULong)e1 < ras.target.rows )
2641     {
2642       FT_TRACE7(( " -> y=%ld", e1 ));
2643 
2644       bits  = ras.bOrigin + ( y >> 3 ) - e1 * ras.target.pitch;
2645       f1    = (Byte)( 0x80 >> ( y & 7 ) );
2646 
2647       bits[0] |= f1;
2648     }
2649 
2650   Exit:
2651     FT_TRACE7(( " dropout=%d\n", left->flags & 7 ));
2652   }
2653 
2654 
2655   static void
Horizontal_Sweep_Step(RAS_ARG)2656   Horizontal_Sweep_Step( RAS_ARG )
2657   {
2658     /* Nothing, really */
2659     FT_UNUSED_RASTER;
2660   }
2661 
2662 
2663   /**************************************************************************
2664    *
2665    * Generic Sweep Drawing routine
2666    *
2667    */
2668 
2669   static Bool
Draw_Sweep(RAS_ARG)2670   Draw_Sweep( RAS_ARG )
2671   {
2672     Short         y, y_change, y_height;
2673 
2674     PProfile      P, Q, P_Left, P_Right;
2675 
2676     Short         min_Y, max_Y, top, bottom, dropouts;
2677 
2678     Long          x1, x2, xs, e1, e2;
2679 
2680     TProfileList  waiting;
2681     TProfileList  draw_left, draw_right;
2682 
2683 
2684     /* initialize empty linked lists */
2685 
2686     Init_Linked( &waiting );
2687 
2688     Init_Linked( &draw_left  );
2689     Init_Linked( &draw_right );
2690 
2691     /* first, compute min and max Y */
2692 
2693     P     = ras.fProfile;
2694     max_Y = (Short)TRUNC( ras.minY );
2695     min_Y = (Short)TRUNC( ras.maxY );
2696 
2697     while ( P )
2698     {
2699       Q = P->link;
2700 
2701       bottom = (Short)P->start;
2702       top    = (Short)( P->start + P->height - 1 );
2703 
2704       if ( min_Y > bottom )
2705         min_Y = bottom;
2706       if ( max_Y < top )
2707         max_Y = top;
2708 
2709       P->X = 0;
2710       InsNew( &waiting, P );
2711 
2712       P = Q;
2713     }
2714 
2715     /* check the Y-turns */
2716     if ( ras.numTurns == 0 )
2717     {
2718       ras.error = FT_THROW( Invalid_Outline );
2719       return FAILURE;
2720     }
2721 
2722     /* now initialize the sweep */
2723 
2724     ras.Proc_Sweep_Init( RAS_VARS min_Y, max_Y );
2725 
2726     /* then compute the distance of each profile from min_Y */
2727 
2728     P = waiting;
2729 
2730     while ( P )
2731     {
2732       P->countL = P->start - min_Y;
2733       P = P->link;
2734     }
2735 
2736     /* let's go */
2737 
2738     y        = min_Y;
2739     y_height = 0;
2740 
2741     if ( ras.numTurns > 0                     &&
2742          ras.sizeBuff[-ras.numTurns] == min_Y )
2743       ras.numTurns--;
2744 
2745     while ( ras.numTurns > 0 )
2746     {
2747       /* check waiting list for new activations */
2748 
2749       P = waiting;
2750 
2751       while ( P )
2752       {
2753         Q = P->link;
2754         P->countL -= y_height;
2755         if ( P->countL == 0 )
2756         {
2757           DelOld( &waiting, P );
2758 
2759           if ( P->flags & Flow_Up )
2760             InsNew( &draw_left,  P );
2761           else
2762             InsNew( &draw_right, P );
2763         }
2764 
2765         P = Q;
2766       }
2767 
2768       /* sort the drawing lists */
2769 
2770       Sort( &draw_left );
2771       Sort( &draw_right );
2772 
2773       y_change = (Short)ras.sizeBuff[-ras.numTurns--];
2774       y_height = (Short)( y_change - y );
2775 
2776       while ( y < y_change )
2777       {
2778         /* let's trace */
2779 
2780         dropouts = 0;
2781 
2782         P_Left  = draw_left;
2783         P_Right = draw_right;
2784 
2785         while ( P_Left && P_Right )
2786         {
2787           x1 = P_Left ->X;
2788           x2 = P_Right->X;
2789 
2790           if ( x1 > x2 )
2791           {
2792             xs = x1;
2793             x1 = x2;
2794             x2 = xs;
2795           }
2796 
2797           e1 = FLOOR( x1 );
2798           e2 = CEILING( x2 );
2799 
2800           if ( x2 - x1 <= ras.precision &&
2801                e1 != x1 && e2 != x2     )
2802           {
2803             if ( e1 > e2 || e2 == e1 + ras.precision )
2804             {
2805               Int  dropOutControl = P_Left->flags & 7;
2806 
2807 
2808               if ( dropOutControl != 2 )
2809               {
2810                 /* a drop-out was detected */
2811 
2812                 P_Left ->X = x1;
2813                 P_Right->X = x2;
2814 
2815                 /* mark profile for drop-out processing */
2816                 P_Left->countL = 1;
2817                 dropouts++;
2818               }
2819 
2820               goto Skip_To_Next;
2821             }
2822           }
2823 
2824           ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right );
2825 
2826         Skip_To_Next:
2827 
2828           P_Left  = P_Left->link;
2829           P_Right = P_Right->link;
2830         }
2831 
2832         /* handle drop-outs _after_ the span drawing --       */
2833         /* drop-out processing has been moved out of the loop */
2834         /* for performance tuning                             */
2835         if ( dropouts > 0 )
2836           goto Scan_DropOuts;
2837 
2838       Next_Line:
2839 
2840         ras.Proc_Sweep_Step( RAS_VAR );
2841 
2842         y++;
2843 
2844         if ( y < y_change )
2845         {
2846           Sort( &draw_left  );
2847           Sort( &draw_right );
2848         }
2849       }
2850 
2851       /* now finalize the profiles that need it */
2852 
2853       P = draw_left;
2854       while ( P )
2855       {
2856         Q = P->link;
2857         if ( P->height == 0 )
2858           DelOld( &draw_left, P );
2859         P = Q;
2860       }
2861 
2862       P = draw_right;
2863       while ( P )
2864       {
2865         Q = P->link;
2866         if ( P->height == 0 )
2867           DelOld( &draw_right, P );
2868         P = Q;
2869       }
2870     }
2871 
2872     /* for gray-scaling, flush the bitmap scanline cache */
2873     while ( y <= max_Y )
2874     {
2875       ras.Proc_Sweep_Step( RAS_VAR );
2876       y++;
2877     }
2878 
2879     return SUCCESS;
2880 
2881   Scan_DropOuts:
2882 
2883     P_Left  = draw_left;
2884     P_Right = draw_right;
2885 
2886     while ( P_Left && P_Right )
2887     {
2888       if ( P_Left->countL )
2889       {
2890         P_Left->countL = 0;
2891 #if 0
2892         dropouts--;  /* -- this is useful when debugging only */
2893 #endif
2894         ras.Proc_Sweep_Drop( RAS_VARS y,
2895                                       P_Left->X,
2896                                       P_Right->X,
2897                                       P_Left,
2898                                       P_Right );
2899       }
2900 
2901       P_Left  = P_Left->link;
2902       P_Right = P_Right->link;
2903     }
2904 
2905     goto Next_Line;
2906   }
2907 
2908 
2909 #ifdef STANDALONE_
2910 
2911   /**************************************************************************
2912    *
2913    * The following functions should only compile in stand-alone mode,
2914    * i.e., when building this component without the rest of FreeType.
2915    *
2916    */
2917 
2918   /**************************************************************************
2919    *
2920    * @Function:
2921    *   FT_Outline_Get_CBox
2922    *
2923    * @Description:
2924    *   Return an outline's `control box'.  The control box encloses all
2925    *   the outline's points, including Bézier control points.  Though it
2926    *   coincides with the exact bounding box for most glyphs, it can be
2927    *   slightly larger in some situations (like when rotating an outline
2928    *   that contains Bézier outside arcs).
2929    *
2930    *   Computing the control box is very fast, while getting the bounding
2931    *   box can take much more time as it needs to walk over all segments
2932    *   and arcs in the outline.  To get the latter, you can use the
2933    *   `ftbbox' component, which is dedicated to this single task.
2934    *
2935    * @Input:
2936    *   outline ::
2937    *     A pointer to the source outline descriptor.
2938    *
2939    * @Output:
2940    *   acbox ::
2941    *     The outline's control box.
2942    *
2943    * @Note:
2944    *   See @FT_Glyph_Get_CBox for a discussion of tricky fonts.
2945    */
2946 
2947   static void
FT_Outline_Get_CBox(const FT_Outline * outline,FT_BBox * acbox)2948   FT_Outline_Get_CBox( const FT_Outline*  outline,
2949                        FT_BBox           *acbox )
2950   {
2951     if ( outline && acbox )
2952     {
2953       Long  xMin, yMin, xMax, yMax;
2954 
2955 
2956       if ( outline->n_points == 0 )
2957       {
2958         xMin = 0;
2959         yMin = 0;
2960         xMax = 0;
2961         yMax = 0;
2962       }
2963       else
2964       {
2965         FT_Vector*  vec   = outline->points;
2966         FT_Vector*  limit = vec + outline->n_points;
2967 
2968 
2969         xMin = xMax = vec->x;
2970         yMin = yMax = vec->y;
2971         vec++;
2972 
2973         for ( ; vec < limit; vec++ )
2974         {
2975           Long  x, y;
2976 
2977 
2978           x = vec->x;
2979           if ( x < xMin ) xMin = x;
2980           if ( x > xMax ) xMax = x;
2981 
2982           y = vec->y;
2983           if ( y < yMin ) yMin = y;
2984           if ( y > yMax ) yMax = y;
2985         }
2986       }
2987       acbox->xMin = xMin;
2988       acbox->xMax = xMax;
2989       acbox->yMin = yMin;
2990       acbox->yMax = yMax;
2991     }
2992   }
2993 
2994 #endif /* STANDALONE_ */
2995 
2996 
2997   /**************************************************************************
2998    *
2999    * @Function:
3000    *   Render_Single_Pass
3001    *
3002    * @Description:
3003    *   Perform one sweep with sub-banding.
3004    *
3005    * @Input:
3006    *   flipped ::
3007    *     If set, flip the direction of the outline.
3008    *
3009    * @Return:
3010    *   Renderer error code.
3011    */
3012   static int
Render_Single_Pass(RAS_ARGS Bool flipped,Int y_min,Int y_max)3013   Render_Single_Pass( RAS_ARGS Bool  flipped,
3014                                Int   y_min,
3015                                Int   y_max )
3016   {
3017     Int  y_mid;
3018     Int  band_top = 0;
3019     Int  band_stack[32];  /* enough to bisect 32-bit int bands */
3020 
3021 
3022     while ( 1 )
3023     {
3024       ras.minY = (Long)y_min * ras.precision;
3025       ras.maxY = (Long)y_max * ras.precision;
3026 
3027       ras.top = ras.buff;
3028 
3029       ras.error = Raster_Err_Ok;
3030 
3031       if ( Convert_Glyph( RAS_VARS flipped ) )
3032       {
3033         if ( ras.error != Raster_Err_Raster_Overflow )
3034           return ras.error;
3035 
3036         /* sub-banding */
3037 
3038         if ( y_min == y_max )
3039           return ras.error;  /* still Raster_Overflow */
3040 
3041         y_mid = ( y_min + y_max ) >> 1;
3042 
3043         band_stack[band_top++] = y_min;
3044         y_min                  = y_mid + 1;
3045       }
3046       else
3047       {
3048         if ( ras.fProfile )
3049           if ( Draw_Sweep( RAS_VAR ) )
3050              return ras.error;
3051 
3052         if ( --band_top < 0 )
3053           break;
3054 
3055         y_max = y_min - 1;
3056         y_min = band_stack[band_top];
3057       }
3058     }
3059 
3060     return Raster_Err_Ok;
3061   }
3062 
3063 
3064   /**************************************************************************
3065    *
3066    * @Function:
3067    *   Render_Glyph
3068    *
3069    * @Description:
3070    *   Render a glyph in a bitmap.  Sub-banding if needed.
3071    *
3072    * @Return:
3073    *   FreeType error code.  0 means success.
3074    */
3075   static FT_Error
Render_Glyph(RAS_ARG)3076   Render_Glyph( RAS_ARG )
3077   {
3078     FT_Error  error;
3079 
3080 
3081     Set_High_Precision( RAS_VARS ras.outline.flags &
3082                                  FT_OUTLINE_HIGH_PRECISION );
3083 
3084     if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )
3085       ras.dropOutControl = 2;
3086     else
3087     {
3088       if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS )
3089         ras.dropOutControl = 4;
3090       else
3091         ras.dropOutControl = 0;
3092 
3093       if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) )
3094         ras.dropOutControl += 1;
3095     }
3096 
3097     /* Vertical Sweep */
3098     FT_TRACE7(( "Vertical pass (ftraster)\n" ));
3099 
3100     ras.Proc_Sweep_Init = Vertical_Sweep_Init;
3101     ras.Proc_Sweep_Span = Vertical_Sweep_Span;
3102     ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
3103     ras.Proc_Sweep_Step = Vertical_Sweep_Step;
3104 
3105     ras.bWidth  = (UShort)ras.target.width;
3106     ras.bOrigin = (Byte*)ras.target.buffer;
3107 
3108     if ( ras.target.pitch > 0 )
3109       ras.bOrigin += (Long)( ras.target.rows - 1 ) * ras.target.pitch;
3110 
3111     error = Render_Single_Pass( RAS_VARS 0, 0, (Int)ras.target.rows - 1 );
3112     if ( error )
3113       return error;
3114 
3115     /* Horizontal Sweep */
3116     if ( !( ras.outline.flags & FT_OUTLINE_SINGLE_PASS ) )
3117     {
3118       FT_TRACE7(( "Horizontal pass (ftraster)\n" ));
3119 
3120       ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
3121       ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
3122       ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;
3123       ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
3124 
3125       error = Render_Single_Pass( RAS_VARS 1, 0, (Int)ras.target.width - 1 );
3126       if ( error )
3127         return error;
3128     }
3129 
3130     return Raster_Err_Ok;
3131   }
3132 
3133 
3134   /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
3135   /****                         a static object.                  *****/
3136 
3137 
3138 #ifdef STANDALONE_
3139 
3140 
3141   static int
ft_black_new(void * memory,FT_Raster * araster)3142   ft_black_new( void*       memory,
3143                 FT_Raster  *araster )
3144   {
3145      static black_TRaster  the_raster;
3146      FT_UNUSED( memory );
3147 
3148 
3149      *araster = (FT_Raster)&the_raster;
3150      FT_ZERO( &the_raster );
3151 
3152      return 0;
3153   }
3154 
3155 
3156   static void
ft_black_done(FT_Raster raster)3157   ft_black_done( FT_Raster  raster )
3158   {
3159     /* nothing */
3160     FT_UNUSED( raster );
3161   }
3162 
3163 
3164 #else /* !STANDALONE_ */
3165 
3166 
3167   static int
ft_black_new(void * memory_,FT_Raster * araster_)3168   ft_black_new( void*       memory_,    /* FT_Memory     */
3169                 FT_Raster  *araster_ )  /* black_PRaster */
3170   {
3171     FT_Memory       memory = (FT_Memory)memory_;
3172     black_PRaster  *araster = (black_PRaster*)araster_;
3173 
3174     FT_Error       error;
3175     black_PRaster  raster = NULL;
3176 
3177 
3178     if ( !FT_NEW( raster ) )
3179       raster->memory = memory;
3180 
3181     *araster = raster;
3182 
3183     return error;
3184   }
3185 
3186 
3187   static void
ft_black_done(FT_Raster raster_)3188   ft_black_done( FT_Raster  raster_ )   /* black_PRaster */
3189   {
3190     black_PRaster  raster = (black_PRaster)raster_;
3191     FT_Memory      memory = (FT_Memory)raster->memory;
3192 
3193 
3194     FT_FREE( raster );
3195   }
3196 
3197 
3198 #endif /* !STANDALONE_ */
3199 
3200 
3201   static void
ft_black_reset(FT_Raster raster,PByte pool_base,ULong pool_size)3202   ft_black_reset( FT_Raster  raster,
3203                   PByte      pool_base,
3204                   ULong      pool_size )
3205   {
3206     FT_UNUSED( raster );
3207     FT_UNUSED( pool_base );
3208     FT_UNUSED( pool_size );
3209   }
3210 
3211 
3212   static int
ft_black_set_mode(FT_Raster raster,ULong mode,void * args)3213   ft_black_set_mode( FT_Raster  raster,
3214                      ULong      mode,
3215                      void*      args )
3216   {
3217     FT_UNUSED( raster );
3218     FT_UNUSED( mode );
3219     FT_UNUSED( args );
3220 
3221     return 0;
3222   }
3223 
3224 
3225   static int
ft_black_render(FT_Raster raster,const FT_Raster_Params * params)3226   ft_black_render( FT_Raster                raster,
3227                    const FT_Raster_Params*  params )
3228   {
3229     const FT_Outline*  outline    = (const FT_Outline*)params->source;
3230     const FT_Bitmap*   target_map = params->target;
3231 
3232 #ifndef FT_STATIC_RASTER
3233     black_TWorker  worker[1];
3234 #endif
3235 
3236     Long  buffer[FT_MAX_BLACK_POOL];
3237 
3238 
3239     if ( !raster )
3240       return FT_THROW( Raster_Uninitialized );
3241 
3242     if ( !outline )
3243       return FT_THROW( Invalid_Outline );
3244 
3245     /* return immediately if the outline is empty */
3246     if ( outline->n_points == 0 || outline->n_contours <= 0 )
3247       return Raster_Err_Ok;
3248 
3249     if ( !outline->contours || !outline->points )
3250       return FT_THROW( Invalid_Outline );
3251 
3252     if ( outline->n_points !=
3253            outline->contours[outline->n_contours - 1] + 1 )
3254       return FT_THROW( Invalid_Outline );
3255 
3256     /* this version of the raster does not support direct rendering, sorry */
3257     if ( params->flags & FT_RASTER_FLAG_DIRECT ||
3258          params->flags & FT_RASTER_FLAG_AA     )
3259       return FT_THROW( Cannot_Render_Glyph );
3260 
3261     if ( !target_map )
3262       return FT_THROW( Invalid_Argument );
3263 
3264     /* nothing to do */
3265     if ( !target_map->width || !target_map->rows )
3266       return Raster_Err_Ok;
3267 
3268     if ( !target_map->buffer )
3269       return FT_THROW( Invalid_Argument );
3270 
3271     ras.outline = *outline;
3272     ras.target  = *target_map;
3273 
3274     ras.buff     = buffer;
3275     ras.sizeBuff = (&buffer)[1]; /* Points to right after buffer. */
3276 
3277     return Render_Glyph( RAS_VAR );
3278   }
3279 
3280 
3281   FT_DEFINE_RASTER_FUNCS(
3282     ft_standard_raster,
3283 
3284     FT_GLYPH_FORMAT_OUTLINE,
3285 
3286     ft_black_new,       /* FT_Raster_New_Func      raster_new      */
3287     ft_black_reset,     /* FT_Raster_Reset_Func    raster_reset    */
3288     ft_black_set_mode,  /* FT_Raster_Set_Mode_Func raster_set_mode */
3289     ft_black_render,    /* FT_Raster_Render_Func   raster_render   */
3290     ft_black_done       /* FT_Raster_Done_Func     raster_done     */
3291   )
3292 
3293 
3294 /* END */
3295