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