~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/3rdparty/freetype/src/base/ftstroke.c

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************/
 
2
/*                                                                         */
 
3
/*  ftstroke.c                                                             */
 
4
/*                                                                         */
 
5
/*    FreeType path stroker (body).                                        */
 
6
/*                                                                         */
 
7
/*  Copyright 2002, 2003, 2004 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
#include <ft2build.h>
 
20
#include FT_STROKER_H
 
21
#include FT_TRIGONOMETRY_H
 
22
#include FT_OUTLINE_H
 
23
#include FT_INTERNAL_MEMORY_H
 
24
#include FT_INTERNAL_DEBUG_H
 
25
#include FT_INTERNAL_OBJECTS_H
 
26
 
 
27
  FT_EXPORT_DEF( FT_StrokerBorder )
 
28
  FT_Outline_GetInsideBorder( FT_Outline*  outline )
 
29
  {
 
30
    FT_Orientation  o = FT_Outline_Get_Orientation( outline );
 
31
 
 
32
 
 
33
    return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_RIGHT
 
34
                                        : FT_STROKER_BORDER_LEFT ;
 
35
  }
 
36
 
 
37
 
 
38
  FT_EXPORT_DEF( FT_StrokerBorder )
 
39
  FT_Outline_GetOutsideBorder( FT_Outline*  outline )
 
40
  {
 
41
    FT_Orientation  o = FT_Outline_Get_Orientation( outline );
 
42
 
 
43
 
 
44
    return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_LEFT
 
45
                                        : FT_STROKER_BORDER_RIGHT ;
 
46
  }
 
47
 
 
48
 
 
49
 /***************************************************************************/
 
50
 /***************************************************************************/
 
51
 /*****                                                                 *****/
 
52
 /*****                       BEZIER COMPUTATIONS                       *****/
 
53
 /*****                                                                 *****/
 
54
 /***************************************************************************/
 
55
 /***************************************************************************/
 
56
 
 
57
#define FT_SMALL_CONIC_THRESHOLD  ( FT_ANGLE_PI / 6 )
 
58
#define FT_SMALL_CUBIC_THRESHOLD  ( FT_ANGLE_PI / 6 )
 
59
#define FT_EPSILON  2
 
60
 
 
61
#define FT_IS_SMALL( x )  ( (x) > -FT_EPSILON && (x) < FT_EPSILON )
 
62
 
 
63
 
 
64
  static FT_Pos
 
65
  ft_pos_abs( FT_Pos  x )
 
66
  {
 
67
    return x >= 0 ? x : -x ;
 
68
  }
 
69
 
 
70
 
 
71
  static void
 
72
  ft_conic_split( FT_Vector*  base )
 
73
  {
 
74
    FT_Pos  a, b;
 
75
 
 
76
 
 
77
    base[4].x = base[2].x;
 
78
    b = base[1].x;
 
79
    a = base[3].x = ( base[2].x + b ) / 2;
 
80
    b = base[1].x = ( base[0].x + b ) / 2;
 
81
    base[2].x = ( a + b ) / 2;
 
82
 
 
83
    base[4].y = base[2].y;
 
84
    b = base[1].y;
 
85
    a = base[3].y = ( base[2].y + b ) / 2;
 
86
    b = base[1].y = ( base[0].y + b ) / 2;
 
87
    base[2].y = ( a + b ) / 2;
 
88
  }
 
89
 
 
90
 
 
91
  static FT_Bool
 
92
  ft_conic_is_small_enough( FT_Vector*  base,
 
93
                            FT_Angle   *angle_in,
 
94
                            FT_Angle   *angle_out )
 
95
  {
 
96
    FT_Vector  d1, d2;
 
97
    FT_Angle   theta;
 
98
    FT_Int     close1, close2;
 
99
 
 
100
 
 
101
    d1.x = base[1].x - base[2].x;
 
102
    d1.y = base[1].y - base[2].y;
 
103
    d2.x = base[0].x - base[1].x;
 
104
    d2.y = base[0].y - base[1].y;
 
105
 
 
106
    close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
 
107
    close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
 
108
 
 
109
    if ( close1 )
 
110
    {
 
111
      if ( close2 )
 
112
        *angle_in = *angle_out = 0;
 
113
      else
 
114
        *angle_in = *angle_out = FT_Atan2( d2.x, d2.y );
 
115
    }
 
116
    else if ( close2 )
 
117
    {
 
118
      *angle_in = *angle_out = FT_Atan2( d1.x, d1.y );
 
119
    }
 
120
    else
 
121
    {
 
122
      *angle_in  = FT_Atan2( d1.x, d1.y );
 
123
      *angle_out = FT_Atan2( d2.x, d2.y );
 
124
    }
 
125
 
 
126
    theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) );
 
127
 
 
128
    return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD );
 
129
  }
 
130
 
 
131
 
 
132
  static void
 
133
  ft_cubic_split( FT_Vector*  base )
 
134
  {
 
135
    FT_Pos  a, b, c, d;
 
136
 
 
137
 
 
138
    base[6].x = base[3].x;
 
139
    c = base[1].x;
 
140
    d = base[2].x;
 
141
    base[1].x = a = ( base[0].x + c ) / 2;
 
142
    base[5].x = b = ( base[3].x + d ) / 2;
 
143
    c = ( c + d ) / 2;
 
144
    base[2].x = a = ( a + c ) / 2;
 
145
    base[4].x = b = ( b + c ) / 2;
 
146
    base[3].x = ( a + b ) / 2;
 
147
 
 
148
    base[6].y = base[3].y;
 
149
    c = base[1].y;
 
150
    d = base[2].y;
 
151
    base[1].y = a = ( base[0].y + c ) / 2;
 
152
    base[5].y = b = ( base[3].y + d ) / 2;
 
153
    c = ( c + d ) / 2;
 
154
    base[2].y = a = ( a + c ) / 2;
 
155
    base[4].y = b = ( b + c ) / 2;
 
156
    base[3].y = ( a + b ) / 2;
 
157
  }
 
158
 
 
159
 
 
160
  static FT_Bool
 
161
  ft_cubic_is_small_enough( FT_Vector*  base,
 
162
                            FT_Angle   *angle_in,
 
163
                            FT_Angle   *angle_mid,
 
164
                            FT_Angle   *angle_out )
 
165
  {
 
166
    FT_Vector  d1, d2, d3;
 
167
    FT_Angle   theta1, theta2;
 
168
    FT_Int     close1, close2, close3;
 
169
 
 
170
 
 
171
    d1.x = base[2].x - base[3].x;
 
172
    d1.y = base[2].y - base[3].y;
 
173
    d2.x = base[1].x - base[2].x;
 
174
    d2.y = base[1].y - base[2].y;
 
175
    d3.x = base[0].x - base[1].x;
 
176
    d3.y = base[0].y - base[1].y;
 
177
 
 
178
    close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
 
179
    close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
 
180
    close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y );
 
181
 
 
182
    if ( close1 || close3 )
 
183
    {
 
184
      if ( close2 )
 
185
      {
 
186
        /* basically a point */
 
187
        *angle_in = *angle_out = *angle_mid = 0;
 
188
      }
 
189
      else if ( close1 )
 
190
      {
 
191
        *angle_in  = *angle_mid = FT_Atan2( d2.x, d2.y );
 
192
        *angle_out = FT_Atan2( d3.x, d3.y );
 
193
      }
 
194
      else  /* close2 */
 
195
      {
 
196
        *angle_in  = FT_Atan2( d1.x, d1.y );
 
197
        *angle_mid = *angle_out = FT_Atan2( d2.x, d2.y );
 
198
      }
 
199
    }
 
200
    else if ( close2 )
 
201
    {
 
202
      *angle_in  = *angle_mid = FT_Atan2( d1.x, d1.y );
 
203
      *angle_out = FT_Atan2( d3.x, d3.y );
 
204
    }
 
205
    else
 
206
    {
 
207
      *angle_in  = FT_Atan2( d1.x, d1.y );
 
208
      *angle_mid = FT_Atan2( d2.x, d2.y );
 
209
      *angle_out = FT_Atan2( d3.x, d3.y );
 
210
    }
 
211
 
 
212
    theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in,  *angle_mid ) );
 
213
    theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) );
 
214
 
 
215
    return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD &&
 
216
                    theta2 < FT_SMALL_CUBIC_THRESHOLD );
 
217
  }
 
218
 
 
219
 
 
220
 /***************************************************************************/
 
221
 /***************************************************************************/
 
222
 /*****                                                                 *****/
 
223
 /*****                       STROKE BORDERS                            *****/
 
224
 /*****                                                                 *****/
 
225
 /***************************************************************************/
 
226
 /***************************************************************************/
 
227
 
 
228
  typedef enum
 
229
  {
 
230
    FT_STROKE_TAG_ON    = 1,   /* on-curve point  */
 
231
    FT_STROKE_TAG_CUBIC = 2,   /* cubic off-point */
 
232
    FT_STROKE_TAG_BEGIN = 4,   /* sub-path start  */
 
233
    FT_STROKE_TAG_END   = 8    /* sub-path end    */
 
234
 
 
235
  } FT_StrokeTags;
 
236
 
 
237
 
 
238
  typedef struct  FT_StrokeBorderRec_
 
239
  {
 
240
    FT_UInt     num_points;
 
241
    FT_UInt     max_points;
 
242
    FT_Vector*  points;
 
243
    FT_Byte*    tags;
 
244
    FT_Bool     movable;
 
245
    FT_Int      start;    /* index of current sub-path start point */
 
246
    FT_Memory   memory;
 
247
    FT_Bool     valid;
 
248
 
 
249
  } FT_StrokeBorderRec, *FT_StrokeBorder;
 
250
 
 
251
 
 
252
  static FT_Error
 
253
  ft_stroke_border_grow( FT_StrokeBorder  border,
 
254
                         FT_UInt          new_points )
 
255
  {
 
256
    FT_UInt   old_max = border->max_points;
 
257
    FT_UInt   new_max = border->num_points + new_points;
 
258
    FT_Error  error   = 0;
 
259
 
 
260
 
 
261
    if ( new_max > old_max )
 
262
    {
 
263
      FT_UInt    cur_max = old_max;
 
264
      FT_Memory  memory  = border->memory;
 
265
 
 
266
 
 
267
      while ( cur_max < new_max )
 
268
        cur_max += ( cur_max >> 1 ) + 16;
 
269
 
 
270
      if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) ||
 
271
           FT_RENEW_ARRAY( border->tags,   old_max, cur_max ) )
 
272
        goto Exit;
 
273
 
 
274
      border->max_points = cur_max;
 
275
    }
 
276
  Exit:
 
277
    return error;
 
278
  }
 
279
 
 
280
 
 
281
  static void
 
282
  ft_stroke_border_close( FT_StrokeBorder  border )
 
283
  {
 
284
    FT_ASSERT( border->start >= 0 );
 
285
 
 
286
    /* don't record empty paths! */
 
287
    if ( border->num_points > (FT_UInt)border->start )
 
288
    {
 
289
      border->tags[border->start         ] |= FT_STROKE_TAG_BEGIN;
 
290
      border->tags[border->num_points - 1] |= FT_STROKE_TAG_END;
 
291
    }
 
292
 
 
293
    border->start   = -1;
 
294
    border->movable = 0;
 
295
  }
 
296
 
 
297
 
 
298
  static FT_Error
 
299
  ft_stroke_border_lineto( FT_StrokeBorder  border,
 
300
                           FT_Vector*       to,
 
301
                           FT_Bool          movable )
 
302
  {
 
303
    FT_Error  error = 0;
 
304
 
 
305
 
 
306
    FT_ASSERT( border->start >= 0 );
 
307
 
 
308
    if ( border->movable )
 
309
    {
 
310
      /* move last point */
 
311
      border->points[border->num_points - 1] = *to;
 
312
    }
 
313
    else
 
314
    {
 
315
      /* add one point */
 
316
      error = ft_stroke_border_grow( border, 1 );
 
317
      if ( !error )
 
318
      {
 
319
        FT_Vector*  vec = border->points + border->num_points;
 
320
        FT_Byte*    tag = border->tags   + border->num_points;
 
321
 
 
322
 
 
323
        vec[0] = *to;
 
324
        tag[0] = FT_STROKE_TAG_ON;
 
325
 
 
326
        border->num_points += 1;
 
327
      }
 
328
    }
 
329
    border->movable = movable;
 
330
    return error;
 
331
  }
 
332
 
 
333
 
 
334
  static FT_Error
 
335
  ft_stroke_border_conicto( FT_StrokeBorder  border,
 
336
                            FT_Vector*       control,
 
337
                            FT_Vector*       to )
 
338
  {
 
339
    FT_Error  error;
 
340
 
 
341
 
 
342
    FT_ASSERT( border->start >= 0 );
 
343
 
 
344
    error = ft_stroke_border_grow( border, 2 );
 
345
    if ( !error )
 
346
    {
 
347
      FT_Vector*  vec = border->points + border->num_points;
 
348
      FT_Byte*    tag = border->tags   + border->num_points;
 
349
 
 
350
      vec[0] = *control;
 
351
      vec[1] = *to;
 
352
 
 
353
      tag[0] = 0;
 
354
      tag[1] = FT_STROKE_TAG_ON;
 
355
 
 
356
      border->num_points += 2;
 
357
    }
 
358
    border->movable = 0;
 
359
    return error;
 
360
  }
 
361
 
 
362
 
 
363
  static FT_Error
 
364
  ft_stroke_border_cubicto( FT_StrokeBorder  border,
 
365
                            FT_Vector*       control1,
 
366
                            FT_Vector*       control2,
 
367
                            FT_Vector*       to )
 
368
  {
 
369
    FT_Error  error;
 
370
 
 
371
 
 
372
    FT_ASSERT( border->start >= 0 );
 
373
 
 
374
    error = ft_stroke_border_grow( border, 3 );
 
375
    if ( !error )
 
376
    {
 
377
      FT_Vector*  vec = border->points + border->num_points;
 
378
      FT_Byte*    tag = border->tags   + border->num_points;
 
379
 
 
380
 
 
381
      vec[0] = *control1;
 
382
      vec[1] = *control2;
 
383
      vec[2] = *to;
 
384
 
 
385
      tag[0] = FT_STROKE_TAG_CUBIC;
 
386
      tag[1] = FT_STROKE_TAG_CUBIC;
 
387
      tag[2] = FT_STROKE_TAG_ON;
 
388
 
 
389
      border->num_points += 3;
 
390
    }
 
391
    border->movable = 0;
 
392
    return error;
 
393
  }
 
394
 
 
395
 
 
396
#define FT_ARC_CUBIC_ANGLE  ( FT_ANGLE_PI / 2 )
 
397
 
 
398
 
 
399
  static FT_Error
 
400
  ft_stroke_border_arcto( FT_StrokeBorder  border,
 
401
                          FT_Vector*       center,
 
402
                          FT_Fixed         radius,
 
403
                          FT_Angle         angle_start,
 
404
                          FT_Angle         angle_diff )
 
405
  {
 
406
    FT_Angle   total, angle, step, rotate, next, theta;
 
407
    FT_Vector  a, b, a2, b2;
 
408
    FT_Fixed   length;
 
409
    FT_Error   error = 0;
 
410
 
 
411
 
 
412
    /* compute start point */
 
413
    FT_Vector_From_Polar( &a, radius, angle_start );
 
414
    a.x += center->x;
 
415
    a.y += center->y;
 
416
 
 
417
    total  = angle_diff;
 
418
    angle  = angle_start;
 
419
    rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2;
 
420
 
 
421
    while ( total != 0 )
 
422
    {
 
423
      step = total;
 
424
      if ( step > FT_ARC_CUBIC_ANGLE )
 
425
        step = FT_ARC_CUBIC_ANGLE;
 
426
 
 
427
      else if ( step < -FT_ARC_CUBIC_ANGLE )
 
428
        step = -FT_ARC_CUBIC_ANGLE;
 
429
 
 
430
      next  = angle + step;
 
431
      theta = step;
 
432
      if ( theta < 0 )
 
433
        theta = -theta;
 
434
 
 
435
      theta >>= 1;
 
436
 
 
437
      /* compute end point */
 
438
      FT_Vector_From_Polar( &b, radius, next );
 
439
      b.x += center->x;
 
440
      b.y += center->y;
 
441
 
 
442
      /* compute first and second control points */
 
443
      length = FT_MulDiv( radius, FT_Sin( theta ) * 4,
 
444
                          ( 0x10000L + FT_Cos( theta ) ) * 3 );
 
445
 
 
446
      FT_Vector_From_Polar( &a2, length, angle + rotate );
 
447
      a2.x += a.x;
 
448
      a2.y += a.y;
 
449
 
 
450
      FT_Vector_From_Polar( &b2, length, next - rotate );
 
451
      b2.x += b.x;
 
452
      b2.y += b.y;
 
453
 
 
454
      /* add cubic arc */
 
455
      error = ft_stroke_border_cubicto( border, &a2, &b2, &b );
 
456
      if ( error )
 
457
        break;
 
458
 
 
459
      /* process the rest of the arc ?? */
 
460
      a      = b;
 
461
      total -= step;
 
462
      angle  = next;
 
463
    }
 
464
 
 
465
    return error;
 
466
  }
 
467
 
 
468
 
 
469
  static FT_Error
 
470
  ft_stroke_border_moveto( FT_StrokeBorder  border,
 
471
                           FT_Vector*       to )
 
472
  {
 
473
    /* close current open path if any ? */
 
474
    if ( border->start >= 0 )
 
475
      ft_stroke_border_close( border );
 
476
 
 
477
    border->start   = border->num_points;
 
478
    border->movable = 0;
 
479
 
 
480
    return ft_stroke_border_lineto( border, to, 0 );
 
481
  }
 
482
 
 
483
 
 
484
  static void
 
485
  ft_stroke_border_init( FT_StrokeBorder  border,
 
486
                         FT_Memory        memory )
 
487
  {
 
488
    border->memory = memory;
 
489
    border->points = NULL;
 
490
    border->tags   = NULL;
 
491
 
 
492
    border->num_points = 0;
 
493
    border->max_points = 0;
 
494
    border->start      = -1;
 
495
    border->valid      = 0;
 
496
  }
 
497
 
 
498
 
 
499
  static void
 
500
  ft_stroke_border_reset( FT_StrokeBorder  border )
 
501
  {
 
502
    border->num_points = 0;
 
503
    border->start      = -1;
 
504
    border->valid      = 0;
 
505
  }
 
506
 
 
507
 
 
508
  static void
 
509
  ft_stroke_border_done( FT_StrokeBorder  border )
 
510
  {
 
511
    FT_Memory  memory = border->memory;
 
512
 
 
513
 
 
514
    FT_FREE( border->points );
 
515
    FT_FREE( border->tags );
 
516
 
 
517
    border->num_points = 0;
 
518
    border->max_points = 0;
 
519
    border->start      = -1;
 
520
    border->valid      = 0;
 
521
  }
 
522
 
 
523
 
 
524
  static FT_Error
 
525
  ft_stroke_border_get_counts( FT_StrokeBorder  border,
 
526
                               FT_UInt         *anum_points,
 
527
                               FT_UInt         *anum_contours )
 
528
  {
 
529
    FT_Error  error        = 0;
 
530
    FT_UInt   num_points   = 0;
 
531
    FT_UInt   num_contours = 0;
 
532
 
 
533
    FT_UInt     count      = border->num_points;
 
534
    FT_Vector*  point      = border->points;
 
535
    FT_Byte*    tags       = border->tags;
 
536
    FT_Int      in_contour = 0;
 
537
 
 
538
 
 
539
    for ( ; count > 0; count--, num_points++, point++, tags++ )
 
540
    {
 
541
      if ( tags[0] & FT_STROKE_TAG_BEGIN )
 
542
      {
 
543
        if ( in_contour != 0 )
 
544
          goto Fail;
 
545
 
 
546
        in_contour = 1;
 
547
      }
 
548
      else if ( in_contour == 0 )
 
549
        goto Fail;
 
550
 
 
551
      if ( tags[0] & FT_STROKE_TAG_END )
 
552
      {
 
553
        if ( in_contour == 0 )
 
554
          goto Fail;
 
555
 
 
556
        in_contour = 0;
 
557
        num_contours++;
 
558
      }
 
559
    }
 
560
 
 
561
    if ( in_contour != 0 )
 
562
      goto Fail;
 
563
 
 
564
    border->valid = 1;
 
565
 
 
566
  Exit:
 
567
    *anum_points   = num_points;
 
568
    *anum_contours = num_contours;
 
569
    return error;
 
570
 
 
571
  Fail:
 
572
    num_points   = 0;
 
573
    num_contours = 0;
 
574
    goto Exit;
 
575
  }
 
576
 
 
577
 
 
578
  static void
 
579
  ft_stroke_border_export( FT_StrokeBorder  border,
 
580
                           FT_Outline*      outline )
 
581
  {
 
582
    /* copy point locations */
 
583
    FT_ARRAY_COPY( outline->points + outline->n_points,
 
584
                   border->points,
 
585
                   border->num_points );
 
586
 
 
587
    /* copy tags */
 
588
    {
 
589
      FT_UInt   count = border->num_points;
 
590
      FT_Byte*  read  = border->tags;
 
591
      FT_Byte*  write = (FT_Byte*)outline->tags + outline->n_points;
 
592
 
 
593
 
 
594
      for ( ; count > 0; count--, read++, write++ )
 
595
      {
 
596
        if ( *read & FT_STROKE_TAG_ON )
 
597
          *write = FT_CURVE_TAG_ON;
 
598
        else if ( *read & FT_STROKE_TAG_CUBIC )
 
599
          *write = FT_CURVE_TAG_CUBIC;
 
600
        else
 
601
          *write = FT_CURVE_TAG_CONIC;
 
602
      }
 
603
    }
 
604
 
 
605
    /* copy contours */
 
606
    {
 
607
      FT_UInt    count = border->num_points;
 
608
      FT_Byte*   tags  = border->tags;
 
609
      FT_Short*  write = outline->contours + outline->n_contours;
 
610
      FT_Short   idx   = (FT_Short)outline->n_points;
 
611
 
 
612
 
 
613
      for ( ; count > 0; count--, tags++, idx++ )
 
614
      {
 
615
        if ( *tags & FT_STROKE_TAG_END )
 
616
        {
 
617
          *write++ = idx;
 
618
          outline->n_contours++;
 
619
        }
 
620
      }
 
621
    }
 
622
 
 
623
    outline->n_points  = (short)( outline->n_points + border->num_points );
 
624
 
 
625
    FT_ASSERT( FT_Outline_Check( outline ) == 0 );
 
626
  }
 
627
 
 
628
 
 
629
 /***************************************************************************/
 
630
 /***************************************************************************/
 
631
 /*****                                                                 *****/
 
632
 /*****                           STROKER                               *****/
 
633
 /*****                                                                 *****/
 
634
 /***************************************************************************/
 
635
 /***************************************************************************/
 
636
 
 
637
#define FT_SIDE_TO_ROTATE( s )   ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
 
638
 
 
639
  typedef struct  FT_StrokerRec_
 
640
  {
 
641
    FT_Angle             angle_in;
 
642
    FT_Angle             angle_out;
 
643
    FT_Vector            center;
 
644
    FT_Bool              first_point;
 
645
    FT_Bool              subpath_open;
 
646
    FT_Angle             subpath_angle;
 
647
    FT_Vector            subpath_start;
 
648
 
 
649
    FT_Stroker_LineCap   line_cap;
 
650
    FT_Stroker_LineJoin  line_join;
 
651
    FT_Fixed             miter_limit;
 
652
    FT_Fixed             radius;
 
653
 
 
654
    FT_Bool              valid;
 
655
    FT_StrokeBorderRec   borders[2];
 
656
    FT_Memory            memory;
 
657
 
 
658
  } FT_StrokerRec;
 
659
 
 
660
 
 
661
  FT_EXPORT_DEF( FT_Error )
 
662
  FT_Stroker_New( FT_Memory    memory,
 
663
                  FT_Stroker  *astroker )
 
664
  {
 
665
    FT_Error    error;
 
666
    FT_Stroker  stroker;
 
667
 
 
668
 
 
669
    if ( !FT_NEW( stroker ) )
 
670
    {
 
671
      stroker->memory = memory;
 
672
 
 
673
      ft_stroke_border_init( &stroker->borders[0], memory );
 
674
      ft_stroke_border_init( &stroker->borders[1], memory );
 
675
    }
 
676
    *astroker = stroker;
 
677
    return error;
 
678
  }
 
679
 
 
680
 
 
681
  FT_EXPORT_DEF( void )
 
682
  FT_Stroker_Set( FT_Stroker           stroker,
 
683
                  FT_Fixed             radius,
 
684
                  FT_Stroker_LineCap   line_cap,
 
685
                  FT_Stroker_LineJoin  line_join,
 
686
                  FT_Fixed             miter_limit )
 
687
  {
 
688
    stroker->radius      = radius;
 
689
    stroker->line_cap    = line_cap;
 
690
    stroker->line_join   = line_join;
 
691
    stroker->miter_limit = miter_limit;
 
692
 
 
693
    FT_Stroker_Rewind( stroker );
 
694
  }
 
695
 
 
696
 
 
697
  FT_EXPORT_DEF( void )
 
698
  FT_Stroker_Rewind( FT_Stroker  stroker )
 
699
  {
 
700
    if ( stroker )
 
701
    {
 
702
      ft_stroke_border_reset( &stroker->borders[0] );
 
703
      ft_stroke_border_reset( &stroker->borders[1] );
 
704
    }
 
705
  }
 
706
 
 
707
 
 
708
  FT_EXPORT_DEF( void )
 
709
  FT_Stroker_Done( FT_Stroker  stroker )
 
710
  {
 
711
    if ( stroker )
 
712
    {
 
713
      FT_Memory  memory = stroker->memory;
 
714
 
 
715
 
 
716
      ft_stroke_border_done( &stroker->borders[0] );
 
717
      ft_stroke_border_done( &stroker->borders[1] );
 
718
 
 
719
      stroker->memory = NULL;
 
720
      FT_FREE( stroker );
 
721
    }
 
722
  }
 
723
 
 
724
 
 
725
  /* creates a circular arc at a corner or cap */
 
726
  static FT_Error
 
727
  ft_stroker_arcto( FT_Stroker  stroker,
 
728
                    FT_Int      side )
 
729
  {
 
730
    FT_Angle         total, rotate;
 
731
    FT_Fixed         radius = stroker->radius;
 
732
    FT_Error         error  = 0;
 
733
    FT_StrokeBorder  border = stroker->borders + side;
 
734
 
 
735
 
 
736
    rotate = FT_SIDE_TO_ROTATE( side );
 
737
 
 
738
    total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
 
739
    if ( total == FT_ANGLE_PI )
 
740
      total = -rotate * 2;
 
741
 
 
742
    error = ft_stroke_border_arcto( border,
 
743
                                    &stroker->center,
 
744
                                    radius,
 
745
                                    stroker->angle_in + rotate,
 
746
                                    total );
 
747
    border->movable = 0;
 
748
    return error;
 
749
  }
 
750
 
 
751
 
 
752
  /* adds a cap at the end of an opened path */
 
753
  static FT_Error
 
754
  ft_stroker_cap( FT_Stroker  stroker,
 
755
                  FT_Angle    angle,
 
756
                  FT_Int      side )
 
757
  {
 
758
    FT_Error  error  = 0;
 
759
 
 
760
 
 
761
    if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
 
762
    {
 
763
      /* add a round cap */
 
764
      stroker->angle_in  = angle;
 
765
      stroker->angle_out = angle + FT_ANGLE_PI;
 
766
      error = ft_stroker_arcto( stroker, side );
 
767
    }
 
768
    else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
 
769
    {
 
770
      /* add a square cap */
 
771
      FT_Vector        delta, delta2;
 
772
      FT_Angle         rotate = FT_SIDE_TO_ROTATE( side );
 
773
      FT_Fixed         radius = stroker->radius;
 
774
      FT_StrokeBorder  border = stroker->borders + side;
 
775
 
 
776
 
 
777
      FT_Vector_From_Polar( &delta2, radius, angle + rotate );
 
778
      FT_Vector_From_Polar( &delta,  radius, angle );
 
779
 
 
780
      delta.x += stroker->center.x + delta2.x;
 
781
      delta.y += stroker->center.y + delta2.y;
 
782
 
 
783
      error = ft_stroke_border_lineto( border, &delta, 0 );
 
784
      if ( error )
 
785
        goto Exit;
 
786
 
 
787
      FT_Vector_From_Polar( &delta2, radius, angle - rotate );
 
788
      FT_Vector_From_Polar( &delta,  radius, angle );
 
789
 
 
790
      delta.x += delta2.x + stroker->center.x;
 
791
      delta.y += delta2.y + stroker->center.y;
 
792
 
 
793
      error = ft_stroke_border_lineto( border, &delta, 0 );
 
794
    }
 
795
 
 
796
  Exit:
 
797
    return error;
 
798
  }
 
799
 
 
800
 
 
801
  /* process an inside corner, i.e. compute intersection */
 
802
  static FT_Error
 
803
  ft_stroker_inside( FT_Stroker  stroker,
 
804
                     FT_Int      side)
 
805
  {
 
806
    FT_StrokeBorder  border = stroker->borders + side;
 
807
    FT_Angle         phi, theta, rotate;
 
808
    FT_Fixed         length, thcos, sigma;
 
809
    FT_Vector        delta;
 
810
    FT_Error         error = 0;
 
811
 
 
812
 
 
813
    rotate = FT_SIDE_TO_ROTATE( side );
 
814
 
 
815
    /* compute median angle */
 
816
    theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
 
817
    if ( theta == FT_ANGLE_PI )
 
818
      theta = rotate;
 
819
    else
 
820
      theta = theta / 2;
 
821
 
 
822
    phi = stroker->angle_in + theta;
 
823
 
 
824
    thcos  = FT_Cos( theta );
 
825
    sigma  = FT_MulFix( stroker->miter_limit, thcos );
 
826
 
 
827
    if ( sigma < 0x10000L )
 
828
    {
 
829
      FT_Vector_From_Polar( &delta, stroker->radius,
 
830
                            stroker->angle_out + rotate );
 
831
      delta.x += stroker->center.x;
 
832
      delta.y += stroker->center.y;
 
833
      border->movable = 0;
 
834
    }
 
835
    else
 
836
    {
 
837
      length = FT_DivFix( stroker->radius, thcos );
 
838
 
 
839
      FT_Vector_From_Polar( &delta, length, phi + rotate );
 
840
      delta.x += stroker->center.x;
 
841
      delta.y += stroker->center.y;
 
842
    }
 
843
 
 
844
    error = ft_stroke_border_lineto( border, &delta, 0 );
 
845
 
 
846
    return error;
 
847
  }
 
848
 
 
849
 
 
850
  /* process an outside corner, i.e. compute bevel/miter/round */
 
851
  static FT_Error
 
852
  ft_stroker_outside( FT_Stroker  stroker,
 
853
                      FT_Int      side )
 
854
  {
 
855
    FT_StrokeBorder  border = stroker->borders + side;
 
856
    FT_Error         error;
 
857
    FT_Angle         rotate;
 
858
 
 
859
 
 
860
    if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
 
861
    {
 
862
      error = ft_stroker_arcto( stroker, side );
 
863
    }
 
864
    else
 
865
    {
 
866
      /* this is a mitered or beveled corner */
 
867
      FT_Fixed  sigma, radius = stroker->radius;
 
868
      FT_Angle  theta, phi;
 
869
      FT_Fixed  thcos;
 
870
      FT_Bool   miter;
 
871
 
 
872
 
 
873
      rotate = FT_SIDE_TO_ROTATE( side );
 
874
      miter  = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
 
875
 
 
876
      theta  = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
 
877
      if ( theta == FT_ANGLE_PI )
 
878
      {
 
879
        theta = rotate;
 
880
        phi   = stroker->angle_in;
 
881
      }
 
882
      else
 
883
      {
 
884
        theta = theta / 2;
 
885
        phi   = stroker->angle_in + theta + rotate;
 
886
      }
 
887
 
 
888
      thcos = FT_Cos( theta );
 
889
      sigma = FT_MulFix( stroker->miter_limit, thcos );
 
890
 
 
891
      if ( sigma >= 0x10000L )
 
892
        miter = 0;
 
893
 
 
894
      if ( miter )  /* this is a miter (broken angle) */
 
895
      {
 
896
        FT_Vector  middle, delta;
 
897
        FT_Fixed   length;
 
898
 
 
899
 
 
900
        /* compute middle point */
 
901
        FT_Vector_From_Polar( &middle,
 
902
                              FT_MulFix( radius, stroker->miter_limit ),
 
903
                              phi );
 
904
        middle.x += stroker->center.x;
 
905
        middle.y += stroker->center.y;
 
906
 
 
907
        /* compute first angle point */
 
908
        length = FT_MulFix( radius,
 
909
                            FT_DivFix( 0x10000L - sigma,
 
910
                                       ft_pos_abs( FT_Sin( theta ) ) ) );
 
911
 
 
912
        FT_Vector_From_Polar( &delta, length, phi + rotate );
 
913
        delta.x += middle.x;
 
914
        delta.y += middle.y;
 
915
 
 
916
        error = ft_stroke_border_lineto( border, &delta, 0 );
 
917
        if ( error )
 
918
          goto Exit;
 
919
 
 
920
        /* compute second angle point */
 
921
        FT_Vector_From_Polar( &delta, length, phi - rotate );
 
922
        delta.x += middle.x;
 
923
        delta.y += middle.y;
 
924
 
 
925
        error = ft_stroke_border_lineto( border, &delta, 0 );
 
926
        if ( error )
 
927
          goto Exit;
 
928
 
 
929
        /* finally, add a movable end point */
 
930
        FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
 
931
        delta.x += stroker->center.x;
 
932
        delta.y += stroker->center.y;
 
933
 
 
934
        error = ft_stroke_border_lineto( border, &delta, 1 );
 
935
      }
 
936
 
 
937
      else /* this is a bevel (intersection) */
 
938
      {
 
939
        FT_Fixed   length;
 
940
        FT_Vector  delta;
 
941
 
 
942
 
 
943
        length = FT_DivFix( stroker->radius, thcos );
 
944
 
 
945
        FT_Vector_From_Polar( &delta, length, phi );
 
946
        delta.x += stroker->center.x;
 
947
        delta.y += stroker->center.y;
 
948
 
 
949
        error = ft_stroke_border_lineto( border, &delta, 0 );
 
950
        if (error) goto Exit;
 
951
 
 
952
        /* now add end point */
 
953
        FT_Vector_From_Polar( &delta, stroker->radius,
 
954
                              stroker->angle_out + rotate );
 
955
        delta.x += stroker->center.x;
 
956
        delta.y += stroker->center.y;
 
957
 
 
958
        error = ft_stroke_border_lineto( border, &delta, 1 );
 
959
      }
 
960
    }
 
961
 
 
962
  Exit:
 
963
    return error;
 
964
  }
 
965
 
 
966
 
 
967
  static FT_Error
 
968
  ft_stroker_process_corner( FT_Stroker  stroker )
 
969
  {
 
970
    FT_Error  error = 0;
 
971
    FT_Angle  turn;
 
972
    FT_Int    inside_side;
 
973
 
 
974
 
 
975
    turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
 
976
 
 
977
    /* no specific corner processing is required if the turn is 0 */
 
978
    if ( turn == 0 )
 
979
      goto Exit;
 
980
 
 
981
    /* when we turn to the right, the inside side is 0 */
 
982
    inside_side = 0;
 
983
 
 
984
    /* otherwise, the inside side is 1 */
 
985
    if ( turn < 0 )
 
986
      inside_side = 1;
 
987
 
 
988
    /* process the inside side */
 
989
    error = ft_stroker_inside( stroker, inside_side );
 
990
    if ( error )
 
991
      goto Exit;
 
992
 
 
993
    /* process the outside side */
 
994
    error = ft_stroker_outside( stroker, 1 - inside_side );
 
995
 
 
996
  Exit:
 
997
    return error;
 
998
  }
 
999
 
 
1000
 
 
1001
  /* add two points to the left and right borders corresponding to the */
 
1002
  /* start of the subpath..                                            */
 
1003
  static FT_Error
 
1004
  ft_stroker_subpath_start( FT_Stroker  stroker,
 
1005
                            FT_Angle    start_angle )
 
1006
  {
 
1007
    FT_Vector        delta;
 
1008
    FT_Vector        point;
 
1009
    FT_Error         error;
 
1010
    FT_StrokeBorder  border;
 
1011
 
 
1012
 
 
1013
    FT_Vector_From_Polar( &delta, stroker->radius,
 
1014
                          start_angle + FT_ANGLE_PI2 );
 
1015
 
 
1016
    point.x = stroker->center.x + delta.x;
 
1017
    point.y = stroker->center.y + delta.y;
 
1018
 
 
1019
    border = stroker->borders;
 
1020
    error = ft_stroke_border_moveto( border, &point );
 
1021
    if ( error )
 
1022
      goto Exit;
 
1023
 
 
1024
    point.x = stroker->center.x - delta.x;
 
1025
    point.y = stroker->center.y - delta.y;
 
1026
 
 
1027
    border++;
 
1028
    error = ft_stroke_border_moveto( border, &point );
 
1029
 
 
1030
    /* save angle for last cap */
 
1031
    stroker->subpath_angle = start_angle;
 
1032
    stroker->first_point   = 0;
 
1033
 
 
1034
  Exit:
 
1035
    return error;
 
1036
  }
 
1037
 
 
1038
 
 
1039
  FT_EXPORT_DEF( FT_Error )
 
1040
  FT_Stroker_LineTo( FT_Stroker  stroker,
 
1041
                     FT_Vector*  to )
 
1042
  {
 
1043
    FT_Error         error = 0;
 
1044
    FT_StrokeBorder  border;
 
1045
    FT_Vector        delta;
 
1046
    FT_Angle         angle;
 
1047
    FT_Int           side;
 
1048
 
 
1049
    delta.x = to->x - stroker->center.x;
 
1050
    delta.y = to->y - stroker->center.y;
 
1051
 
 
1052
    angle = FT_Atan2( delta.x, delta.y );
 
1053
    FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
 
1054
 
 
1055
    /* process corner if necessary */
 
1056
    if ( stroker->first_point )
 
1057
    {
 
1058
      /* This is the first segment of a subpath.  We need to     */
 
1059
      /* add a point to each border at their respective starting */
 
1060
      /* point locations.                                        */
 
1061
      error = ft_stroker_subpath_start( stroker, angle );
 
1062
      if ( error )
 
1063
        goto Exit;
 
1064
    }
 
1065
    else
 
1066
    {
 
1067
      /* process the current corner */
 
1068
      stroker->angle_out = angle;
 
1069
      error = ft_stroker_process_corner( stroker );
 
1070
      if ( error )
 
1071
        goto Exit;
 
1072
    }
 
1073
 
 
1074
    /* now add a line segment to both the "inside" and "outside" paths */
 
1075
 
 
1076
    for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
 
1077
    {
 
1078
      FT_Vector  point;
 
1079
 
 
1080
 
 
1081
      point.x = to->x + delta.x;
 
1082
      point.y = to->y + delta.y;
 
1083
 
 
1084
      error = ft_stroke_border_lineto( border, &point, 1 );
 
1085
      if ( error )
 
1086
        goto Exit;
 
1087
 
 
1088
      delta.x = -delta.x;
 
1089
      delta.y = -delta.y;
 
1090
    }
 
1091
 
 
1092
    stroker->angle_in = angle;
 
1093
    stroker->center   = *to;
 
1094
 
 
1095
  Exit:
 
1096
    return error;
 
1097
  }
 
1098
 
 
1099
 
 
1100
  FT_EXPORT_DEF( FT_Error )
 
1101
  FT_Stroker_ConicTo( FT_Stroker  stroker,
 
1102
                      FT_Vector*  control,
 
1103
                      FT_Vector*  to )
 
1104
  {
 
1105
    FT_Error    error = 0;
 
1106
    FT_Vector   bez_stack[34];
 
1107
    FT_Vector*  arc;
 
1108
    FT_Vector*  limit = bez_stack + 30;
 
1109
    FT_Angle    start_angle;
 
1110
    FT_Bool     first_arc = 1;
 
1111
 
 
1112
 
 
1113
    arc    = bez_stack;
 
1114
    arc[0] = *to;
 
1115
    arc[1] = *control;
 
1116
    arc[2] = stroker->center;
 
1117
 
 
1118
    while ( arc >= bez_stack )
 
1119
    {
 
1120
      FT_Angle  angle_in, angle_out;
 
1121
 
 
1122
 
 
1123
      angle_in = angle_out = 0;  /* remove compiler warnings */
 
1124
 
 
1125
      if ( arc < limit                                             &&
 
1126
           !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
 
1127
      {
 
1128
        ft_conic_split( arc );
 
1129
        arc += 2;
 
1130
        continue;
 
1131
      }
 
1132
 
 
1133
      if ( first_arc )
 
1134
      {
 
1135
        first_arc = 0;
 
1136
 
 
1137
        start_angle = angle_in;
 
1138
 
 
1139
        /* process corner if necessary */
 
1140
        if ( stroker->first_point )
 
1141
          error = ft_stroker_subpath_start( stroker, start_angle );
 
1142
        else
 
1143
        {
 
1144
          stroker->angle_out = start_angle;
 
1145
          error = ft_stroker_process_corner( stroker );
 
1146
        }
 
1147
      }
 
1148
 
 
1149
      /* the arc's angle is small enough; we can add it directly to each */
 
1150
      /* border                                                          */
 
1151
      {
 
1152
        FT_Vector  ctrl, end;
 
1153
        FT_Angle   theta, phi, rotate;
 
1154
        FT_Fixed   length;
 
1155
        FT_Int     side;
 
1156
 
 
1157
 
 
1158
        theta  = FT_Angle_Diff( angle_in, angle_out ) / 2;
 
1159
        phi    = angle_in + theta;
 
1160
        length = FT_DivFix( stroker->radius, FT_Cos( theta ) );
 
1161
 
 
1162
        for ( side = 0; side <= 1; side++ )
 
1163
        {
 
1164
          rotate = FT_SIDE_TO_ROTATE( side );
 
1165
 
 
1166
          /* compute control point */
 
1167
          FT_Vector_From_Polar( &ctrl, length, phi + rotate );
 
1168
          ctrl.x += arc[1].x;
 
1169
          ctrl.y += arc[1].y;
 
1170
 
 
1171
          /* compute end point */
 
1172
          FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
 
1173
          end.x += arc[0].x;
 
1174
          end.y += arc[0].y;
 
1175
 
 
1176
          error = ft_stroke_border_conicto( stroker->borders + side,
 
1177
                                            &ctrl, &end );
 
1178
          if ( error )
 
1179
            goto Exit;
 
1180
        }
 
1181
      }
 
1182
 
 
1183
      arc -= 2;
 
1184
 
 
1185
      if ( arc < bez_stack )
 
1186
        stroker->angle_in = angle_out;
 
1187
    }
 
1188
 
 
1189
    stroker->center = *to;
 
1190
 
 
1191
  Exit:
 
1192
    return error;
 
1193
  }
 
1194
 
 
1195
 
 
1196
  FT_EXPORT_DEF( FT_Error )
 
1197
  FT_Stroker_CubicTo( FT_Stroker  stroker,
 
1198
                      FT_Vector*  control1,
 
1199
                      FT_Vector*  control2,
 
1200
                      FT_Vector*  to )
 
1201
  {
 
1202
    FT_Error    error = 0;
 
1203
    FT_Vector   bez_stack[37];
 
1204
    FT_Vector*  arc;
 
1205
    FT_Vector*  limit = bez_stack + 32;
 
1206
    FT_Angle    start_angle;
 
1207
    FT_Bool     first_arc = 1;
 
1208
 
 
1209
 
 
1210
    arc    = bez_stack;
 
1211
    arc[0] = *to;
 
1212
    arc[1] = *control2;
 
1213
    arc[2] = *control1;
 
1214
    arc[3] = stroker->center;
 
1215
 
 
1216
    while ( arc >= bez_stack )
 
1217
    {
 
1218
      FT_Angle  angle_in, angle_mid, angle_out;
 
1219
 
 
1220
 
 
1221
      /* remove compiler warnings */
 
1222
      angle_in = angle_out = angle_mid = 0;
 
1223
 
 
1224
      if ( arc < limit                                         &&
 
1225
           !ft_cubic_is_small_enough( arc, &angle_in,
 
1226
                                      &angle_mid, &angle_out ) )
 
1227
      {
 
1228
        ft_cubic_split( arc );
 
1229
        arc += 3;
 
1230
        continue;
 
1231
      }
 
1232
 
 
1233
      if ( first_arc )
 
1234
      {
 
1235
        first_arc = 0;
 
1236
 
 
1237
        /* process corner if necessary */
 
1238
        start_angle = angle_in;
 
1239
 
 
1240
        if ( stroker->first_point )
 
1241
          error = ft_stroker_subpath_start( stroker, start_angle );
 
1242
        else
 
1243
        {
 
1244
          stroker->angle_out = start_angle;
 
1245
          error = ft_stroker_process_corner( stroker );
 
1246
        }
 
1247
        if ( error )
 
1248
          goto Exit;
 
1249
      }
 
1250
 
 
1251
      /* the arc's angle is small enough; we can add it directly to each */
 
1252
      /* border                                                          */
 
1253
      {
 
1254
        FT_Vector  ctrl1, ctrl2, end;
 
1255
        FT_Angle   theta1, phi1, theta2, phi2, rotate;
 
1256
        FT_Fixed   length1, length2;
 
1257
        FT_Int     side;
 
1258
 
 
1259
 
 
1260
        theta1  = ft_pos_abs( angle_mid - angle_in ) / 2;
 
1261
        theta2  = ft_pos_abs( angle_out - angle_mid ) / 2;
 
1262
        phi1    = (angle_mid + angle_in ) / 2;
 
1263
        phi2    = (angle_mid + angle_out ) / 2;
 
1264
        length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) );
 
1265
        length2 = FT_DivFix( stroker->radius, FT_Cos(theta2) );
 
1266
 
 
1267
        for ( side = 0; side <= 1; side++ )
 
1268
        {
 
1269
          rotate = FT_SIDE_TO_ROTATE( side );
 
1270
 
 
1271
          /* compute control points */
 
1272
          FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
 
1273
          ctrl1.x += arc[2].x;
 
1274
          ctrl1.y += arc[2].y;
 
1275
 
 
1276
          FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
 
1277
          ctrl2.x += arc[1].x;
 
1278
          ctrl2.y += arc[1].y;
 
1279
 
 
1280
          /* compute end point */
 
1281
          FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
 
1282
          end.x += arc[0].x;
 
1283
          end.y += arc[0].y;
 
1284
 
 
1285
          error = ft_stroke_border_cubicto( stroker->borders + side,
 
1286
                                            &ctrl1, &ctrl2, &end );
 
1287
          if ( error )
 
1288
            goto Exit;
 
1289
        }
 
1290
      }
 
1291
 
 
1292
      arc -= 3;
 
1293
      if ( arc < bez_stack )
 
1294
        stroker->angle_in = angle_out;
 
1295
    }
 
1296
 
 
1297
    stroker->center = *to;
 
1298
 
 
1299
  Exit:
 
1300
    return error;
 
1301
  }
 
1302
 
 
1303
 
 
1304
  FT_EXPORT_DEF( FT_Error )
 
1305
  FT_Stroker_BeginSubPath( FT_Stroker  stroker,
 
1306
                           FT_Vector*  to,
 
1307
                           FT_Bool     open )
 
1308
  {
 
1309
    /* We cannot process the first point, because there is not enough      */
 
1310
    /* information regarding its corner/cap.  The latter will be processed */
 
1311
    /* in the "end_subpath" routine.                                       */
 
1312
    /*                                                                     */
 
1313
    stroker->first_point   = 1;
 
1314
    stroker->center        = *to;
 
1315
    stroker->subpath_open  = open;
 
1316
 
 
1317
    /* record the subpath start point index for each border */
 
1318
    stroker->subpath_start = *to;
 
1319
    return 0;
 
1320
  }
 
1321
 
 
1322
 
 
1323
  static FT_Error
 
1324
  ft_stroker_add_reverse_left( FT_Stroker  stroker,
 
1325
                               FT_Bool     open )
 
1326
  {
 
1327
    FT_StrokeBorder  right  = stroker->borders + 0;
 
1328
    FT_StrokeBorder  left   = stroker->borders + 1;
 
1329
    FT_Int           new_points;
 
1330
    FT_Error         error  = 0;
 
1331
 
 
1332
 
 
1333
    FT_ASSERT( left->start >= 0 );
 
1334
 
 
1335
    new_points = left->num_points - left->start;
 
1336
    if ( new_points > 0 )
 
1337
    {
 
1338
      error = ft_stroke_border_grow( right, (FT_UInt)new_points );
 
1339
      if ( error )
 
1340
        goto Exit;
 
1341
 
 
1342
      {
 
1343
        FT_Vector*  dst_point = right->points + right->num_points;
 
1344
        FT_Byte*    dst_tag   = right->tags   + right->num_points;
 
1345
        FT_Vector*  src_point = left->points  + left->num_points - 1;
 
1346
        FT_Byte*    src_tag   = left->tags    + left->num_points - 1;
 
1347
 
 
1348
        while ( src_point >= left->points + left->start )
 
1349
        {
 
1350
          *dst_point = *src_point;
 
1351
          *dst_tag   = *src_tag;
 
1352
 
 
1353
          if ( open )
 
1354
            dst_tag[0] &= ~( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END );
 
1355
          else
 
1356
          {
 
1357
            /* switch begin/end tags if necessary.. */
 
1358
            if ( dst_tag[0] & ( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END ) )
 
1359
              dst_tag[0] ^= ( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END );
 
1360
          }
 
1361
 
 
1362
          src_point--;
 
1363
          src_tag--;
 
1364
          dst_point++;
 
1365
          dst_tag++;
 
1366
        }
 
1367
      }
 
1368
 
 
1369
      left->num_points   = left->start;
 
1370
      right->num_points += new_points;
 
1371
 
 
1372
      right->movable = 0;
 
1373
      left->movable  = 0;
 
1374
    }
 
1375
 
 
1376
  Exit:
 
1377
    return error;
 
1378
  }
 
1379
 
 
1380
 
 
1381
  /* there's a lot of magic in this function! */
 
1382
  FT_EXPORT_DEF( FT_Error )
 
1383
  FT_Stroker_EndSubPath( FT_Stroker  stroker )
 
1384
  {
 
1385
    FT_Error  error  = 0;
 
1386
 
 
1387
    if ( stroker->subpath_open )
 
1388
    {
 
1389
      FT_StrokeBorder  right = stroker->borders;
 
1390
 
 
1391
      /* All right, this is an opened path, we need to add a cap between */
 
1392
      /* right & left, add the reverse of left, then add a final cap     */
 
1393
      /* between left & right.                                           */
 
1394
      error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
 
1395
      if ( error )
 
1396
        goto Exit;
 
1397
 
 
1398
      /* add reversed points from "left" to "right" */
 
1399
      error = ft_stroker_add_reverse_left( stroker, 1 );
 
1400
      if ( error )
 
1401
        goto Exit;
 
1402
 
 
1403
      /* now add the final cap */
 
1404
      stroker->center = stroker->subpath_start;
 
1405
      error = ft_stroker_cap( stroker,
 
1406
                              stroker->subpath_angle + FT_ANGLE_PI, 0 );
 
1407
      if ( error )
 
1408
        goto Exit;
 
1409
 
 
1410
      /* Now end the right subpath accordingly.  The left one is */
 
1411
      /* rewind and doesn't need further processing.             */
 
1412
      ft_stroke_border_close( right );
 
1413
    }
 
1414
    else
 
1415
    {
 
1416
      FT_Angle  turn;
 
1417
      FT_Int    inside_side;
 
1418
 
 
1419
      /* close the path if needed */
 
1420
      if ( stroker->center.x != stroker->subpath_start.x ||
 
1421
           stroker->center.y != stroker->subpath_start.y )
 
1422
      {
 
1423
        error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
 
1424
        if ( error )
 
1425
          goto Exit;
 
1426
      }
 
1427
 
 
1428
      /* process the corner */
 
1429
      stroker->angle_out = stroker->subpath_angle;
 
1430
      turn               = FT_Angle_Diff( stroker->angle_in,
 
1431
                                          stroker->angle_out );
 
1432
 
 
1433
      /* no specific corner processing is required if the turn is 0 */
 
1434
      if ( turn != 0 )
 
1435
      {
 
1436
        /* when we turn to the right, the inside side is 0 */
 
1437
        inside_side = 0;
 
1438
 
 
1439
        /* otherwise, the inside side is 1 */
 
1440
        if ( turn < 0 )
 
1441
          inside_side = 1;
 
1442
 
 
1443
        /* IMPORTANT: WE DO NOT PROCESS THE INSIDE BORDER HERE! */
 
1444
        /* process the inside side                              */
 
1445
        /* error = ft_stroker_inside( stroker, inside_side );   */
 
1446
        /* if ( error )                                         */
 
1447
        /*   goto Exit;                                         */
 
1448
 
 
1449
        /* process the outside side */
 
1450
        error = ft_stroker_outside( stroker, 1 - inside_side );
 
1451
        if ( error )
 
1452
          goto Exit;
 
1453
      }
 
1454
 
 
1455
      /* then end our two subpaths */
 
1456
      ft_stroke_border_close( stroker->borders + 0 );
 
1457
      ft_stroke_border_close( stroker->borders + 1 );
 
1458
    }
 
1459
 
 
1460
  Exit:
 
1461
    return error;
 
1462
  }
 
1463
 
 
1464
 
 
1465
  FT_EXPORT_DEF( FT_Error )
 
1466
  FT_Stroker_GetBorderCounts( FT_Stroker        stroker,
 
1467
                              FT_StrokerBorder  border,
 
1468
                              FT_UInt          *anum_points,
 
1469
                              FT_UInt          *anum_contours )
 
1470
  {
 
1471
    FT_UInt   num_points = 0, num_contours = 0;
 
1472
    FT_Error  error;
 
1473
 
 
1474
 
 
1475
    if ( !stroker || border > 1 )
 
1476
    {
 
1477
      error = FT_Err_Invalid_Argument;
 
1478
      goto Exit;
 
1479
    }
 
1480
 
 
1481
    error = ft_stroke_border_get_counts( stroker->borders + border,
 
1482
                                         &num_points, &num_contours );
 
1483
  Exit:
 
1484
    if ( anum_points )
 
1485
      *anum_points = num_points;
 
1486
 
 
1487
    if ( anum_contours )
 
1488
      *anum_contours = num_contours;
 
1489
 
 
1490
    return error;
 
1491
  }
 
1492
 
 
1493
 
 
1494
  FT_EXPORT_DEF( FT_Error )
 
1495
  FT_Stroker_GetCounts( FT_Stroker  stroker,
 
1496
                        FT_UInt    *anum_points,
 
1497
                        FT_UInt    *anum_contours )
 
1498
  {
 
1499
    FT_UInt   count1, count2, num_points   = 0;
 
1500
    FT_UInt   count3, count4, num_contours = 0;
 
1501
    FT_Error  error;
 
1502
 
 
1503
 
 
1504
    error = ft_stroke_border_get_counts( stroker->borders + 0,
 
1505
                                         &count1, &count2 );
 
1506
    if ( error )
 
1507
      goto Exit;
 
1508
 
 
1509
    error = ft_stroke_border_get_counts( stroker->borders + 1,
 
1510
                                         &count3, &count4 );
 
1511
    if ( error )
 
1512
      goto Exit;
 
1513
 
 
1514
    num_points   = count1 + count3;
 
1515
    num_contours = count2 + count4;
 
1516
 
 
1517
  Exit:
 
1518
    *anum_points   = num_points;
 
1519
    *anum_contours = num_contours;
 
1520
    return error;
 
1521
  }
 
1522
 
 
1523
 
 
1524
  FT_EXPORT_DEF( void )
 
1525
  FT_Stroker_ExportBorder( FT_Stroker        stroker,
 
1526
                           FT_StrokerBorder  border,
 
1527
                           FT_Outline*       outline )
 
1528
  {
 
1529
    if ( border == FT_STROKER_BORDER_LEFT  ||
 
1530
         border == FT_STROKER_BORDER_RIGHT )
 
1531
    {
 
1532
      FT_StrokeBorder  sborder = & stroker->borders[border];
 
1533
 
 
1534
 
 
1535
      if ( sborder->valid )
 
1536
        ft_stroke_border_export( sborder, outline );
 
1537
    }
 
1538
  }
 
1539
 
 
1540
 
 
1541
  FT_EXPORT_DEF( void )
 
1542
  FT_Stroker_Export( FT_Stroker   stroker,
 
1543
                     FT_Outline*  outline )
 
1544
  {
 
1545
    FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
 
1546
    FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
 
1547
  }
 
1548
 
 
1549
 
 
1550
  /*
 
1551
   *  The following is very similar to FT_Outline_Decompose, except
 
1552
   *  that we do support opened paths, and do not scale the outline.
 
1553
   */
 
1554
  FT_EXPORT_DEF( FT_Error )
 
1555
  FT_Stroker_ParseOutline( FT_Stroker   stroker,
 
1556
                           FT_Outline*  outline,
 
1557
                           FT_Bool      opened )
 
1558
  {
 
1559
    FT_Vector   v_last;
 
1560
    FT_Vector   v_control;
 
1561
    FT_Vector   v_start;
 
1562
 
 
1563
    FT_Vector*  point;
 
1564
    FT_Vector*  limit;
 
1565
    char*       tags;
 
1566
 
 
1567
    FT_Error    error;
 
1568
 
 
1569
    FT_Int   n;         /* index of contour in outline     */
 
1570
    FT_UInt  first;     /* index of first point in contour */
 
1571
    FT_Int   tag;       /* current point's state           */
 
1572
 
 
1573
 
 
1574
    if ( !outline || !stroker )
 
1575
      return FT_Err_Invalid_Argument;
 
1576
 
 
1577
    FT_Stroker_Rewind( stroker );
 
1578
 
 
1579
    first = 0;
 
1580
 
 
1581
    for ( n = 0; n < outline->n_contours; n++ )
 
1582
    {
 
1583
      FT_Int  last;  /* index of last point in contour */
 
1584
 
 
1585
 
 
1586
      last  = outline->contours[n];
 
1587
      limit = outline->points + last;
 
1588
 
 
1589
      v_start = outline->points[first];
 
1590
      v_last  = outline->points[last];
 
1591
 
 
1592
      v_control = v_start;
 
1593
 
 
1594
      point = outline->points + first;
 
1595
      tags  = outline->tags  + first;
 
1596
      tag   = FT_CURVE_TAG( tags[0] );
 
1597
 
 
1598
      /* A contour cannot start with a cubic control point! */
 
1599
      if ( tag == FT_CURVE_TAG_CUBIC )
 
1600
        goto Invalid_Outline;
 
1601
 
 
1602
      /* check first point to determine origin */
 
1603
      if ( tag == FT_CURVE_TAG_CONIC )
 
1604
      {
 
1605
        /* First point is conic control.  Yes, this happens. */
 
1606
        if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
 
1607
        {
 
1608
          /* start at last point if it is on the curve */
 
1609
          v_start = v_last;
 
1610
          limit--;
 
1611
        }
 
1612
        else
 
1613
        {
 
1614
          /* if both first and last points are conic,         */
 
1615
          /* start at their middle and record its position    */
 
1616
          /* for closure                                      */
 
1617
          v_start.x = ( v_start.x + v_last.x ) / 2;
 
1618
          v_start.y = ( v_start.y + v_last.y ) / 2;
 
1619
 
 
1620
          v_last = v_start;
 
1621
        }
 
1622
        point--;
 
1623
        tags--;
 
1624
      }
 
1625
 
 
1626
      error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
 
1627
      if ( error )
 
1628
        goto Exit;
 
1629
 
 
1630
      while ( point < limit )
 
1631
      {
 
1632
        point++;
 
1633
        tags++;
 
1634
 
 
1635
        tag = FT_CURVE_TAG( tags[0] );
 
1636
        switch ( tag )
 
1637
        {
 
1638
        case FT_CURVE_TAG_ON:  /* emit a single line_to */
 
1639
          {
 
1640
            FT_Vector  vec;
 
1641
 
 
1642
 
 
1643
            vec.x = point->x;
 
1644
            vec.y = point->y;
 
1645
 
 
1646
            error = FT_Stroker_LineTo( stroker, &vec );
 
1647
            if ( error )
 
1648
              goto Exit;
 
1649
            continue;
 
1650
          }
 
1651
 
 
1652
        case FT_CURVE_TAG_CONIC:  /* consume conic arcs */
 
1653
          v_control.x = point->x;
 
1654
          v_control.y = point->y;
 
1655
 
 
1656
        Do_Conic:
 
1657
          if ( point < limit )
 
1658
          {
 
1659
            FT_Vector  vec;
 
1660
            FT_Vector  v_middle;
 
1661
 
 
1662
 
 
1663
            point++;
 
1664
            tags++;
 
1665
            tag = FT_CURVE_TAG( tags[0] );
 
1666
 
 
1667
            vec = point[0];
 
1668
 
 
1669
            if ( tag == FT_CURVE_TAG_ON )
 
1670
            {
 
1671
              error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
 
1672
              if ( error )
 
1673
                goto Exit;
 
1674
              continue;
 
1675
            }
 
1676
 
 
1677
            if ( tag != FT_CURVE_TAG_CONIC )
 
1678
              goto Invalid_Outline;
 
1679
 
 
1680
            v_middle.x = ( v_control.x + vec.x ) / 2;
 
1681
            v_middle.y = ( v_control.y + vec.y ) / 2;
 
1682
 
 
1683
            error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
 
1684
            if ( error )
 
1685
              goto Exit;
 
1686
 
 
1687
            v_control = vec;
 
1688
            goto Do_Conic;
 
1689
          }
 
1690
 
 
1691
          error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
 
1692
          goto Close;
 
1693
 
 
1694
        default:  /* FT_CURVE_TAG_CUBIC */
 
1695
          {
 
1696
            FT_Vector  vec1, vec2;
 
1697
 
 
1698
 
 
1699
            if ( point + 1 > limit                             ||
 
1700
                 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
 
1701
              goto Invalid_Outline;
 
1702
 
 
1703
            point += 2;
 
1704
            tags  += 2;
 
1705
 
 
1706
            vec1 = point[-2];
 
1707
            vec2 = point[-1];
 
1708
 
 
1709
            if ( point <= limit )
 
1710
            {
 
1711
              FT_Vector  vec;
 
1712
 
 
1713
 
 
1714
              vec = point[0];
 
1715
 
 
1716
              error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
 
1717
              if ( error )
 
1718
                goto Exit;
 
1719
              continue;
 
1720
            }
 
1721
 
 
1722
            error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
 
1723
            goto Close;
 
1724
          }
 
1725
        }
 
1726
      }
 
1727
 
 
1728
    Close:
 
1729
      if ( error )
 
1730
        goto Exit;
 
1731
 
 
1732
      error = FT_Stroker_EndSubPath( stroker );
 
1733
      if ( error )
 
1734
        goto Exit;
 
1735
 
 
1736
      first = last + 1;
 
1737
    }
 
1738
 
 
1739
    return 0;
 
1740
 
 
1741
  Exit:
 
1742
    return error;
 
1743
 
 
1744
  Invalid_Outline:
 
1745
    return FT_Err_Invalid_Outline;
 
1746
  }
 
1747
 
 
1748
 
 
1749
  extern const FT_Glyph_Class  ft_outline_glyph_class;
 
1750
 
 
1751
 
 
1752
  FT_EXPORT_DEF( FT_Error )
 
1753
  FT_Glyph_Stroke( FT_Glyph    *pglyph,
 
1754
                   FT_Stroker   stroker,
 
1755
                   FT_Bool      destroy )
 
1756
  {
 
1757
    FT_Error  error = FT_Err_Invalid_Argument;
 
1758
    FT_Glyph  glyph = NULL;
 
1759
 
 
1760
 
 
1761
    if ( pglyph == NULL )
 
1762
      goto Exit;
 
1763
 
 
1764
    glyph = *pglyph;
 
1765
    if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class )
 
1766
      goto Exit;
 
1767
 
 
1768
    {
 
1769
      FT_Glyph  copy;
 
1770
 
 
1771
 
 
1772
      error = FT_Glyph_Copy( glyph, &copy );
 
1773
      if ( error )
 
1774
        goto Exit;
 
1775
 
 
1776
      glyph = copy;
 
1777
    }
 
1778
 
 
1779
    {
 
1780
      FT_OutlineGlyph  oglyph  = (FT_OutlineGlyph) glyph;
 
1781
      FT_Outline*      outline = &oglyph->outline;
 
1782
      FT_UInt          num_points, num_contours;
 
1783
 
 
1784
 
 
1785
      error = FT_Stroker_ParseOutline( stroker, outline, 0 );
 
1786
      if ( error )
 
1787
        goto Fail;
 
1788
 
 
1789
      (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
 
1790
 
 
1791
      FT_Outline_Done( glyph->library, outline );
 
1792
 
 
1793
      error = FT_Outline_New( glyph->library,
 
1794
                              num_points, num_contours, outline );
 
1795
      if ( error )
 
1796
        goto Fail;
 
1797
 
 
1798
      outline->n_points   = 0;
 
1799
      outline->n_contours = 0;
 
1800
 
 
1801
      FT_Stroker_Export( stroker, outline );
 
1802
    }
 
1803
 
 
1804
    if ( destroy )
 
1805
      FT_Done_Glyph( *pglyph );
 
1806
 
 
1807
    *pglyph = glyph;
 
1808
    goto Exit;
 
1809
 
 
1810
  Fail:
 
1811
    FT_Done_Glyph( glyph );
 
1812
    glyph = NULL;
 
1813
 
 
1814
    if ( !destroy )
 
1815
      *pglyph = NULL;
 
1816
 
 
1817
  Exit:
 
1818
    return error;
 
1819
  }
 
1820
 
 
1821
 
 
1822
  FT_EXPORT_DEF( FT_Error )
 
1823
  FT_Glyph_StrokeBorder( FT_Glyph    *pglyph,
 
1824
                         FT_Stroker   stroker,
 
1825
                         FT_Bool      inside,
 
1826
                         FT_Bool      destroy )
 
1827
  {
 
1828
    FT_Error  error = FT_Err_Invalid_Argument;
 
1829
    FT_Glyph  glyph = NULL;
 
1830
 
 
1831
 
 
1832
    if ( pglyph == NULL )
 
1833
      goto Exit;
 
1834
 
 
1835
    glyph = *pglyph;
 
1836
    if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class )
 
1837
      goto Exit;
 
1838
 
 
1839
    {
 
1840
      FT_Glyph  copy;
 
1841
 
 
1842
 
 
1843
      error = FT_Glyph_Copy( glyph, &copy );
 
1844
      if ( error )
 
1845
        goto Exit;
 
1846
 
 
1847
      glyph = copy;
 
1848
    }
 
1849
 
 
1850
    {
 
1851
      FT_OutlineGlyph   oglyph  = (FT_OutlineGlyph) glyph;
 
1852
      FT_StrokerBorder  border;
 
1853
      FT_Outline*       outline = &oglyph->outline;
 
1854
      FT_UInt           num_points, num_contours;
 
1855
 
 
1856
 
 
1857
      border = FT_Outline_GetOutsideBorder( outline );
 
1858
      if ( inside )
 
1859
      {
 
1860
        if ( border == FT_STROKER_BORDER_LEFT )
 
1861
          border = FT_STROKER_BORDER_RIGHT;
 
1862
        else
 
1863
          border = FT_STROKER_BORDER_LEFT;
 
1864
      }
 
1865
 
 
1866
      error = FT_Stroker_ParseOutline( stroker, outline, 0 );
 
1867
      if ( error )
 
1868
        goto Fail;
 
1869
 
 
1870
      (void)FT_Stroker_GetBorderCounts( stroker, border,
 
1871
                                        &num_points, &num_contours );
 
1872
 
 
1873
      FT_Outline_Done( glyph->library, outline );
 
1874
 
 
1875
      error = FT_Outline_New( glyph->library,
 
1876
                              num_points,
 
1877
                              num_contours,
 
1878
                              outline );
 
1879
      if ( error )
 
1880
        goto Fail;
 
1881
 
 
1882
      outline->n_points   = 0;
 
1883
      outline->n_contours = 0;
 
1884
 
 
1885
      FT_Stroker_ExportBorder( stroker, border, outline );
 
1886
    }
 
1887
 
 
1888
    if ( destroy )
 
1889
      FT_Done_Glyph( *pglyph );
 
1890
 
 
1891
    *pglyph = glyph;
 
1892
    goto Exit;
 
1893
 
 
1894
  Fail:
 
1895
    FT_Done_Glyph( glyph );
 
1896
    glyph = NULL;
 
1897
 
 
1898
    if ( !destroy )
 
1899
      *pglyph = NULL;
 
1900
 
 
1901
  Exit:
 
1902
    return error;
 
1903
  }
 
1904
 
 
1905
 
 
1906
/* END */