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