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