1/****************************************************************************
2 *
3 * ftoutln.c
4 *
5 * FreeType outline management (body).
6 *
7 * Copyright (C) 1996-2019 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_OUTLINE_H
21#include FT_INTERNAL_OBJECTS_H
22#include FT_INTERNAL_CALC_H
23#include FT_INTERNAL_DEBUG_H
24#include FT_TRIGONOMETRY_H
25
26
27 /**************************************************************************
28 *
29 * The macro FT_COMPONENT is used in trace mode. It is an implicit
30 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
31 * messages during execution.
32 */
33#undef FT_COMPONENT
34#define FT_COMPONENT outline
35
36
37 static
38 const FT_Outline null_outline = { 0, 0, NULL, NULL, NULL, 0 };
39
40
41 /* documentation is in ftoutln.h */
42
43 FT_EXPORT_DEF( FT_Error )
44 FT_Outline_Decompose( FT_Outline* outline,
45 const FT_Outline_Funcs* func_interface,
46 void* user )
47 {
48#undef SCALED
49#define SCALED( x ) ( ( (x) < 0 ? -( -(x) << shift ) \
50 : ( (x) << shift ) ) - delta )
51
52 FT_Vector v_last;
53 FT_Vector v_control;
54 FT_Vector v_start;
55
56 FT_Vector* point;
57 FT_Vector* limit;
58 char* tags;
59
60 FT_Error error;
61
62 FT_Int n; /* index of contour in outline */
63 FT_UInt first; /* index of first point in contour */
64 FT_Int tag; /* current point's state */
65
66 FT_Int shift;
67 FT_Pos delta;
68
69
70 if ( !outline )
71 return FT_THROW( Invalid_Outline );
72
73 if ( !func_interface )
74 return FT_THROW( Invalid_Argument );
75
76 shift = func_interface->shift;
77 delta = func_interface->delta;
78 first = 0;
79
80 for ( n = 0; n < outline->n_contours; n++ )
81 {
82 FT_Int last; /* index of last point in contour */
83
84
85 FT_TRACE5(( "FT_Outline_Decompose: Outline %d\n", n ));
86
87 last = outline->contours[n];
88 if ( last < 0 )
89 goto Invalid_Outline;
90 limit = outline->points + last;
91
92 v_start = outline->points[first];
93 v_start.x = SCALED( v_start.x );
94 v_start.y = SCALED( v_start.y );
95
96 v_last = outline->points[last];
97 v_last.x = SCALED( v_last.x );
98 v_last.y = SCALED( v_last.y );
99
100 v_control = v_start;
101
102 point = outline->points + first;
103 tags = outline->tags + first;
104 tag = FT_CURVE_TAG( tags[0] );
105
106 /* A contour cannot start with a cubic control point! */
107 if ( tag == FT_CURVE_TAG_CUBIC )
108 goto Invalid_Outline;
109
110 /* check first point to determine origin */
111 if ( tag == FT_CURVE_TAG_CONIC )
112 {
113 /* first point is conic control. Yes, this happens. */
114 if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
115 {
116 /* start at last point if it is on the curve */
117 v_start = v_last;
118 limit--;
119 }
120 else
121 {
122 /* if both first and last points are conic, */
123 /* start at their middle and record its position */
124 /* for closure */
125 v_start.x = ( v_start.x + v_last.x ) / 2;
126 v_start.y = ( v_start.y + v_last.y ) / 2;
127
128 /* v_last = v_start; */
129 }
130 point--;
131 tags--;
132 }
133
134 FT_TRACE5(( " move to (%.2f, %.2f)\n",
135 v_start.x / 64.0, v_start.y / 64.0 ));
136 error = func_interface->move_to( &v_start, user );
137 if ( error )
138 goto Exit;
139
140 while ( point < limit )
141 {
142 point++;
143 tags++;
144
145 tag = FT_CURVE_TAG( tags[0] );
146 switch ( tag )
147 {
148 case FT_CURVE_TAG_ON: /* emit a single line_to */
149 {
150 FT_Vector vec;
151
152
153 vec.x = SCALED( point->x );
154 vec.y = SCALED( point->y );
155
156 FT_TRACE5(( " line to (%.2f, %.2f)\n",
157 vec.x / 64.0, vec.y / 64.0 ));
158 error = func_interface->line_to( &vec, user );
159 if ( error )
160 goto Exit;
161 continue;
162 }
163
164 case FT_CURVE_TAG_CONIC: /* consume conic arcs */
165 v_control.x = SCALED( point->x );
166 v_control.y = SCALED( point->y );
167
168 Do_Conic:
169 if ( point < limit )
170 {
171 FT_Vector vec;
172 FT_Vector v_middle;
173
174
175 point++;
176 tags++;
177 tag = FT_CURVE_TAG( tags[0] );
178
179 vec.x = SCALED( point->x );
180 vec.y = SCALED( point->y );
181
182 if ( tag == FT_CURVE_TAG_ON )
183 {
184 FT_TRACE5(( " conic to (%.2f, %.2f)"
185 " with control (%.2f, %.2f)\n",
186 vec.x / 64.0, vec.y / 64.0,
187 v_control.x / 64.0, v_control.y / 64.0 ));
188 error = func_interface->conic_to( &v_control, &vec, user );
189 if ( error )
190 goto Exit;
191 continue;
192 }
193
194 if ( tag != FT_CURVE_TAG_CONIC )
195 goto Invalid_Outline;
196
197 v_middle.x = ( v_control.x + vec.x ) / 2;
198 v_middle.y = ( v_control.y + vec.y ) / 2;
199
200 FT_TRACE5(( " conic to (%.2f, %.2f)"
201 " with control (%.2f, %.2f)\n",
202 v_middle.x / 64.0, v_middle.y / 64.0,
203 v_control.x / 64.0, v_control.y / 64.0 ));
204 error = func_interface->conic_to( &v_control, &v_middle, user );
205 if ( error )
206 goto Exit;
207
208 v_control = vec;
209 goto Do_Conic;
210 }
211
212 FT_TRACE5(( " conic to (%.2f, %.2f)"
213 " with control (%.2f, %.2f)\n",
214 v_start.x / 64.0, v_start.y / 64.0,
215 v_control.x / 64.0, v_control.y / 64.0 ));
216 error = func_interface->conic_to( &v_control, &v_start, user );
217 goto Close;
218
219 default: /* FT_CURVE_TAG_CUBIC */
220 {
221 FT_Vector vec1, vec2;
222
223
224 if ( point + 1 > limit ||
225 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
226 goto Invalid_Outline;
227
228 point += 2;
229 tags += 2;
230
231 vec1.x = SCALED( point[-2].x );
232 vec1.y = SCALED( point[-2].y );
233
234 vec2.x = SCALED( point[-1].x );
235 vec2.y = SCALED( point[-1].y );
236
237 if ( point <= limit )
238 {
239 FT_Vector vec;
240
241
242 vec.x = SCALED( point->x );
243 vec.y = SCALED( point->y );
244
245 FT_TRACE5(( " cubic to (%.2f, %.2f)"
246 " with controls (%.2f, %.2f) and (%.2f, %.2f)\n",
247 vec.x / 64.0, vec.y / 64.0,
248 vec1.x / 64.0, vec1.y / 64.0,
249 vec2.x / 64.0, vec2.y / 64.0 ));
250 error = func_interface->cubic_to( &vec1, &vec2, &vec, user );
251 if ( error )
252 goto Exit;
253 continue;
254 }
255
256 FT_TRACE5(( " cubic to (%.2f, %.2f)"
257 " with controls (%.2f, %.2f) and (%.2f, %.2f)\n",
258 v_start.x / 64.0, v_start.y / 64.0,
259 vec1.x / 64.0, vec1.y / 64.0,
260 vec2.x / 64.0, vec2.y / 64.0 ));
261 error = func_interface->cubic_to( &vec1, &vec2, &v_start, user );
262 goto Close;
263 }
264 }
265 }
266
267 /* close the contour with a line segment */
268 FT_TRACE5(( " line to (%.2f, %.2f)\n",
269 v_start.x / 64.0, v_start.y / 64.0 ));
270 error = func_interface->line_to( &v_start, user );
271
272 Close:
273 if ( error )
274 goto Exit;
275
276 first = (FT_UInt)last + 1;
277 }
278
279 FT_TRACE5(( "FT_Outline_Decompose: Done\n", n ));
280 return FT_Err_Ok;
281
282 Invalid_Outline:
283 error = FT_THROW( Invalid_Outline );
284 /* fall through */
285
286 Exit:
287 FT_TRACE5(( "FT_Outline_Decompose: Error 0x%x\n", error ));
288 return error;
289 }
290
291
292 /* documentation is in ftoutln.h */
293
294 FT_EXPORT_DEF( FT_Error )
295 FT_Outline_New( FT_Library library,
296 FT_UInt numPoints,
297 FT_Int numContours,
298 FT_Outline *anoutline )
299 {
300 FT_Error error;
301 FT_Memory memory;
302
303
304 if ( !library )
305 return FT_THROW( Invalid_Library_Handle );
306
307 memory = library->memory;
308
309 if ( !anoutline || !memory )
310 return FT_THROW( Invalid_Argument );
311
312 *anoutline = null_outline;
313
314 if ( numContours < 0 ||
315 (FT_UInt)numContours > numPoints )
316 return FT_THROW( Invalid_Argument );
317
318 if ( numPoints > FT_OUTLINE_POINTS_MAX )
319 return FT_THROW( Array_Too_Large );
320
321 if ( FT_NEW_ARRAY( anoutline->points, numPoints ) ||
322 FT_NEW_ARRAY( anoutline->tags, numPoints ) ||
323 FT_NEW_ARRAY( anoutline->contours, numContours ) )
324 goto Fail;
325
326 anoutline->n_points = (FT_Short)numPoints;
327 anoutline->n_contours = (FT_Short)numContours;
328 anoutline->flags |= FT_OUTLINE_OWNER;
329
330 return FT_Err_Ok;
331
332 Fail:
333 anoutline->flags |= FT_OUTLINE_OWNER;
334 FT_Outline_Done( library, anoutline );
335
336 return error;
337 }
338
339
340 /* documentation is in ftoutln.h */
341
342 FT_EXPORT_DEF( FT_Error )
343 FT_Outline_Check( FT_Outline* outline )
344 {
345 if ( outline )
346 {
347 FT_Int n_points = outline->n_points;
348 FT_Int n_contours = outline->n_contours;
349 FT_Int end0, end;
350 FT_Int n;
351
352
353 /* empty glyph? */
354 if ( n_points == 0 && n_contours == 0 )
355 return FT_Err_Ok;
356
357 /* check point and contour counts */
358 if ( n_points <= 0 || n_contours <= 0 )
359 goto Bad;
360
361 end0 = end = -1;
362 for ( n = 0; n < n_contours; n++ )
363 {
364 end = outline->contours[n];
365
366 /* note that we don't accept empty contours */
367 if ( end <= end0 || end >= n_points )
368 goto Bad;
369
370 end0 = end;
371 }
372
373 if ( end != n_points - 1 )
374 goto Bad;
375
376 /* XXX: check the tags array */
377 return FT_Err_Ok;
378 }
379
380 Bad:
381 return FT_THROW( Invalid_Argument );
382 }
383
384
385 /* documentation is in ftoutln.h */
386
387 FT_EXPORT_DEF( FT_Error )
388 FT_Outline_Copy( const FT_Outline* source,
389 FT_Outline *target )
390 {
391 FT_Int is_owner;
392
393
394 if ( !source || !target )
395 return FT_THROW( Invalid_Outline );
396
397 if ( source->n_points != target->n_points ||
398 source->n_contours != target->n_contours )
399 return FT_THROW( Invalid_Argument );
400
401 if ( source == target )
402 return FT_Err_Ok;
403
404 if ( source->n_points )
405 {
406 FT_ARRAY_COPY( target->points, source->points, source->n_points );
407 FT_ARRAY_COPY( target->tags, source->tags, source->n_points );
408 }
409
410 if ( source->n_contours )
411 FT_ARRAY_COPY( target->contours, source->contours, source->n_contours );
412
413 /* copy all flags, except the `FT_OUTLINE_OWNER' one */
414 is_owner = target->flags & FT_OUTLINE_OWNER;
415 target->flags = source->flags;
416
417 target->flags &= ~FT_OUTLINE_OWNER;
418 target->flags |= is_owner;
419
420 return FT_Err_Ok;
421 }
422
423
424 /* documentation is in ftoutln.h */
425
426 FT_EXPORT_DEF( FT_Error )
427 FT_Outline_Done( FT_Library library,
428 FT_Outline* outline )
429 {
430 FT_Memory memory;
431
432
433 if ( !library )
434 return FT_THROW( Invalid_Library_Handle );
435
436 if ( !outline )
437 return FT_THROW( Invalid_Outline );
438
439 memory = library->memory;
440
441 if ( !memory )
442 return FT_THROW( Invalid_Argument );
443
444 if ( outline->flags & FT_OUTLINE_OWNER )
445 {
446 FT_FREE( outline->points );
447 FT_FREE( outline->tags );
448 FT_FREE( outline->contours );
449 }
450 *outline = null_outline;
451
452 return FT_Err_Ok;
453 }
454
455
456 /* documentation is in ftoutln.h */
457
458 FT_EXPORT_DEF( void )
459 FT_Outline_Get_CBox( const FT_Outline* outline,
460 FT_BBox *acbox )
461 {
462 FT_Pos xMin, yMin, xMax, yMax;
463
464
465 if ( outline && acbox )
466 {
467 if ( outline->n_points == 0 )
468 {
469 xMin = 0;
470 yMin = 0;
471 xMax = 0;
472 yMax = 0;
473 }
474 else
475 {
476 FT_Vector* vec = outline->points;
477 FT_Vector* limit = vec + outline->n_points;
478
479
480 xMin = xMax = vec->x;
481 yMin = yMax = vec->y;
482 vec++;
483
484 for ( ; vec < limit; vec++ )
485 {
486 FT_Pos x, y;
487
488
489 x = vec->x;
490 if ( x < xMin ) xMin = x;
491 if ( x > xMax ) xMax = x;
492
493 y = vec->y;
494 if ( y < yMin ) yMin = y;
495 if ( y > yMax ) yMax = y;
496 }
497 }
498 acbox->xMin = xMin;
499 acbox->xMax = xMax;
500 acbox->yMin = yMin;
501 acbox->yMax = yMax;
502 }
503 }
504
505
506 /* documentation is in ftoutln.h */
507
508 FT_EXPORT_DEF( void )
509 FT_Outline_Translate( const FT_Outline* outline,
510 FT_Pos xOffset,
511 FT_Pos yOffset )
512 {
513 FT_UShort n;
514 FT_Vector* vec;
515
516
517 if ( !outline )
518 return;
519
520 vec = outline->points;
521
522 for ( n = 0; n < outline->n_points; n++ )
523 {
524 vec->x = ADD_LONG( vec->x, xOffset );
525 vec->y = ADD_LONG( vec->y, yOffset );
526 vec++;
527 }
528 }
529
530
531 /* documentation is in ftoutln.h */
532
533 FT_EXPORT_DEF( void )
534 FT_Outline_Reverse( FT_Outline* outline )
535 {
536 FT_UShort n;
537 FT_Int first, last;
538
539
540 if ( !outline )
541 return;
542
543 first = 0;
544
545 for ( n = 0; n < outline->n_contours; n++ )
546 {
547 last = outline->contours[n];
548
549 /* reverse point table */
550 {
551 FT_Vector* p = outline->points + first;
552 FT_Vector* q = outline->points + last;
553 FT_Vector swap;
554
555
556 while ( p < q )
557 {
558 swap = *p;
559 *p = *q;
560 *q = swap;
561 p++;
562 q--;
563 }
564 }
565
566 /* reverse tags table */
567 {
568 char* p = outline->tags + first;
569 char* q = outline->tags + last;
570
571
572 while ( p < q )
573 {
574 char swap;
575
576
577 swap = *p;
578 *p = *q;
579 *q = swap;
580 p++;
581 q--;
582 }
583 }
584
585 first = last + 1;
586 }
587
588 outline->flags ^= FT_OUTLINE_REVERSE_FILL;
589 }
590
591
592 /* documentation is in ftoutln.h */
593
594 FT_EXPORT_DEF( FT_Error )
595 FT_Outline_Render( FT_Library library,
596 FT_Outline* outline,
597 FT_Raster_Params* params )
598 {
599 FT_Error error;
600 FT_Renderer renderer;
601 FT_ListNode node;
602 FT_BBox cbox;
603
604
605 if ( !library )
606 return FT_THROW( Invalid_Library_Handle );
607
608 if ( !outline )
609 return FT_THROW( Invalid_Outline );
610
611 if ( !params )
612 return FT_THROW( Invalid_Argument );
613
614 FT_Outline_Get_CBox( outline, &cbox );
615 if ( cbox.xMin < -0x1000000L || cbox.yMin < -0x1000000L ||
616 cbox.xMax > 0x1000000L || cbox.yMax > 0x1000000L )
617 return FT_THROW( Invalid_Outline );
618
619 renderer = library->cur_renderer;
620 node = library->renderers.head;
621
622 params->source = (void*)outline;
623
624 error = FT_ERR( Cannot_Render_Glyph );
625 while ( renderer )
626 {
627 error = renderer->raster_render( renderer->raster, params );
628 if ( !error || FT_ERR_NEQ( error, Cannot_Render_Glyph ) )
629 break;
630
631 /* FT_Err_Cannot_Render_Glyph is returned if the render mode */
632 /* is unsupported by the current renderer for this glyph image */
633 /* format */
634
635 /* now, look for another renderer that supports the same */
636 /* format */
637 renderer = FT_Lookup_Renderer( library, FT_GLYPH_FORMAT_OUTLINE,
638 &node );
639 }
640
641 return error;
642 }
643
644
645 /* documentation is in ftoutln.h */
646
647 FT_EXPORT_DEF( FT_Error )
648 FT_Outline_Get_Bitmap( FT_Library library,
649 FT_Outline* outline,
650 const FT_Bitmap *abitmap )
651 {
652 FT_Raster_Params params;
653
654
655 if ( !abitmap )
656 return FT_THROW( Invalid_Argument );
657
658 /* other checks are delayed to `FT_Outline_Render' */
659
660 params.target = abitmap;
661 params.flags = 0;
662
663 if ( abitmap->pixel_mode == FT_PIXEL_MODE_GRAY ||
664 abitmap->pixel_mode == FT_PIXEL_MODE_LCD ||
665 abitmap->pixel_mode == FT_PIXEL_MODE_LCD_V )
666 params.flags |= FT_RASTER_FLAG_AA;
667
668 return FT_Outline_Render( library, outline, &params );
669 }
670
671
672 /* documentation is in freetype.h */
673
674 FT_EXPORT_DEF( void )
675 FT_Vector_Transform( FT_Vector* vector,
676 const FT_Matrix* matrix )
677 {
678 FT_Pos xz, yz;
679
680
681 if ( !vector || !matrix )
682 return;
683
684 xz = FT_MulFix( vector->x, matrix->xx ) +
685 FT_MulFix( vector->y, matrix->xy );
686
687 yz = FT_MulFix( vector->x, matrix->yx ) +
688 FT_MulFix( vector->y, matrix->yy );
689
690 vector->x = xz;
691 vector->y = yz;
692 }
693
694
695 /* documentation is in ftoutln.h */
696
697 FT_EXPORT_DEF( void )
698 FT_Outline_Transform( const FT_Outline* outline,
699 const FT_Matrix* matrix )
700 {
701 FT_Vector* vec;
702 FT_Vector* limit;
703
704
705 if ( !outline || !matrix )
706 return;
707
708 vec = outline->points;
709 limit = vec + outline->n_points;
710
711 for ( ; vec < limit; vec++ )
712 FT_Vector_Transform( vec, matrix );
713 }
714
715
716#if 0
717
718#define FT_OUTLINE_GET_CONTOUR( outline, c, first, last ) \
719 do \
720 { \
721 (first) = ( c > 0 ) ? (outline)->points + \
722 (outline)->contours[c - 1] + 1 \
723 : (outline)->points; \
724 (last) = (outline)->points + (outline)->contours[c]; \
725 } while ( 0 )
726
727
728 /* Is a point in some contour? */
729 /* */
730 /* We treat every point of the contour as if it */
731 /* it were ON. That is, we allow false positives, */
732 /* but disallow false negatives. (XXX really?) */
733 static FT_Bool
734 ft_contour_has( FT_Outline* outline,
735 FT_Short c,
736 FT_Vector* point )
737 {
738 FT_Vector* first;
739 FT_Vector* last;
740 FT_Vector* a;
741 FT_Vector* b;
742 FT_UInt n = 0;
743
744
745 FT_OUTLINE_GET_CONTOUR( outline, c, first, last );
746
747 for ( a = first; a <= last; a++ )
748 {
749 FT_Pos x;
750 FT_Int intersect;
751
752
753 b = ( a == last ) ? first : a + 1;
754
755 intersect = ( a->y - point->y ) ^ ( b->y - point->y );
756
757 /* a and b are on the same side */
758 if ( intersect >= 0 )
759 {
760 if ( intersect == 0 && a->y == point->y )
761 {
762 if ( ( a->x <= point->x && b->x >= point->x ) ||
763 ( a->x >= point->x && b->x <= point->x ) )
764 return 1;
765 }
766
767 continue;
768 }
769
770 x = a->x + ( b->x - a->x ) * (point->y - a->y ) / ( b->y - a->y );
771
772 if ( x < point->x )
773 n++;
774 else if ( x == point->x )
775 return 1;
776 }
777
778 return n & 1;
779 }
780
781
782 static FT_Bool
783 ft_contour_enclosed( FT_Outline* outline,
784 FT_UShort c )
785 {
786 FT_Vector* first;
787 FT_Vector* last;
788 FT_Short i;
789
790
791 FT_OUTLINE_GET_CONTOUR( outline, c, first, last );
792
793 for ( i = 0; i < outline->n_contours; i++ )
794 {
795 if ( i != c && ft_contour_has( outline, i, first ) )
796 {
797 FT_Vector* pt;
798
799
800 for ( pt = first + 1; pt <= last; pt++ )
801 if ( !ft_contour_has( outline, i, pt ) )
802 return 0;
803
804 return 1;
805 }
806 }
807
808 return 0;
809 }
810
811
812 /* This version differs from the public one in that each */
813 /* part (contour not enclosed in another contour) of the */
814 /* outline is checked for orientation. This is */
815 /* necessary for some buggy CJK fonts. */
816 static FT_Orientation
817 ft_outline_get_orientation( FT_Outline* outline )
818 {
819 FT_Short i;
820 FT_Vector* first;
821 FT_Vector* last;
822 FT_Orientation orient = FT_ORIENTATION_NONE;
823
824
825 first = outline->points;
826 for ( i = 0; i < outline->n_contours; i++, first = last + 1 )
827 {
828 FT_Vector* point;
829 FT_Vector* xmin_point;
830 FT_Pos xmin;
831
832
833 last = outline->points + outline->contours[i];
834
835 /* skip degenerate contours */
836 if ( last < first + 2 )
837 continue;
838
839 if ( ft_contour_enclosed( outline, i ) )
840 continue;
841
842 xmin = first->x;
843 xmin_point = first;
844
845 for ( point = first + 1; point <= last; point++ )
846 {
847 if ( point->x < xmin )
848 {
849 xmin = point->x;
850 xmin_point = point;
851 }
852 }
853
854 /* check the orientation of the contour */
855 {
856 FT_Vector* prev;
857 FT_Vector* next;
858 FT_Orientation o;
859
860
861 prev = ( xmin_point == first ) ? last : xmin_point - 1;
862 next = ( xmin_point == last ) ? first : xmin_point + 1;
863
864 if ( FT_Atan2( prev->x - xmin_point->x, prev->y - xmin_point->y ) >
865 FT_Atan2( next->x - xmin_point->x, next->y - xmin_point->y ) )
866 o = FT_ORIENTATION_POSTSCRIPT;
867 else
868 o = FT_ORIENTATION_TRUETYPE;
869
870 if ( orient == FT_ORIENTATION_NONE )
871 orient = o;
872 else if ( orient != o )
873 return FT_ORIENTATION_NONE;
874 }
875 }
876
877 return orient;
878 }
879
880#endif /* 0 */
881
882
883 /* documentation is in ftoutln.h */
884
885 FT_EXPORT_DEF( FT_Error )
886 FT_Outline_Embolden( FT_Outline* outline,
887 FT_Pos strength )
888 {
889 return FT_Outline_EmboldenXY( outline, strength, strength );
890 }
891
892
893 /* documentation is in ftoutln.h */
894
895 FT_EXPORT_DEF( FT_Error )
896 FT_Outline_EmboldenXY( FT_Outline* outline,
897 FT_Pos xstrength,
898 FT_Pos ystrength )
899 {
900 FT_Vector* points;
901 FT_Int c, first, last;
902 FT_Orientation orientation;
903
904
905 if ( !outline )
906 return FT_THROW( Invalid_Outline );
907
908 xstrength /= 2;
909 ystrength /= 2;
910 if ( xstrength == 0 && ystrength == 0 )
911 return FT_Err_Ok;
912
913 orientation = FT_Outline_Get_Orientation( outline );
914 if ( orientation == FT_ORIENTATION_NONE )
915 {
916 if ( outline->n_contours )
917 return FT_THROW( Invalid_Argument );
918 else
919 return FT_Err_Ok;
920 }
921
922 points = outline->points;
923
924 first = 0;
925 for ( c = 0; c < outline->n_contours; c++ )
926 {
927 FT_Vector in, out, anchor, shift;
928 FT_Fixed l_in, l_out, l_anchor = 0, l, q, d;
929 FT_Int i, j, k;
930
931
932 l_in = 0;
933 last = outline->contours[c];
934
935 /* pacify compiler */
936 in.x = in.y = anchor.x = anchor.y = 0;
937
938 /* Counter j cycles though the points; counter i advances only */
939 /* when points are moved; anchor k marks the first moved point. */
940 for ( i = last, j = first, k = -1;
941 j != i && i != k;
942 j = j < last ? j + 1 : first )
943 {
944 if ( j != k )
945 {
946 out.x = points[j].x - points[i].x;
947 out.y = points[j].y - points[i].y;
948 l_out = (FT_Fixed)FT_Vector_NormLen( &out );
949
950 if ( l_out == 0 )
951 continue;
952 }
953 else
954 {
955 out = anchor;
956 l_out = l_anchor;
957 }
958
959 if ( l_in != 0 )
960 {
961 if ( k < 0 )
962 {
963 k = i;
964 anchor = in;
965 l_anchor = l_in;
966 }
967
968 d = FT_MulFix( in.x, out.x ) + FT_MulFix( in.y, out.y );
969
970 /* shift only if turn is less than ~160 degrees */
971 if ( d > -0xF000L )
972 {
973 d = d + 0x10000L;
974
975 /* shift components along lateral bisector in proper orientation */
976 shift.x = in.y + out.y;
977 shift.y = in.x + out.x;
978
979 if ( orientation == FT_ORIENTATION_TRUETYPE )
980 shift.x = -shift.x;
981 else
982 shift.y = -shift.y;
983
984 /* restrict shift magnitude to better handle collapsing segments */
985 q = FT_MulFix( out.x, in.y ) - FT_MulFix( out.y, in.x );
986 if ( orientation == FT_ORIENTATION_TRUETYPE )
987 q = -q;
988
989 l = FT_MIN( l_in, l_out );
990
991 /* non-strict inequalities avoid divide-by-zero when q == l == 0 */
992 if ( FT_MulFix( xstrength, q ) <= FT_MulFix( l, d ) )
993 shift.x = FT_MulDiv( shift.x, xstrength, d );
994 else
995 shift.x = FT_MulDiv( shift.x, l, q );
996
997
998 if ( FT_MulFix( ystrength, q ) <= FT_MulFix( l, d ) )
999 shift.y = FT_MulDiv( shift.y, ystrength, d );
1000 else
1001 shift.y = FT_MulDiv( shift.y, l, q );
1002 }
1003 else
1004 shift.x = shift.y = 0;
1005
1006 for ( ;
1007 i != j;
1008 i = i < last ? i + 1 : first )
1009 {
1010 points[i].x += xstrength + shift.x;
1011 points[i].y += ystrength + shift.y;
1012 }
1013 }
1014 else
1015 i = j;
1016
1017 in = out;
1018 l_in = l_out;
1019 }
1020
1021 first = last + 1;
1022 }
1023
1024 return FT_Err_Ok;
1025 }
1026
1027
1028 /* documentation is in ftoutln.h */
1029
1030 FT_EXPORT_DEF( FT_Orientation )
1031 FT_Outline_Get_Orientation( FT_Outline* outline )
1032 {
1033 FT_BBox cbox = { 0, 0, 0, 0 };
1034 FT_Int xshift, yshift;
1035 FT_Vector* points;
1036 FT_Vector v_prev, v_cur;
1037 FT_Int c, n, first;
1038 FT_Pos area = 0;
1039
1040
1041 if ( !outline || outline->n_points <= 0 )
1042 return FT_ORIENTATION_TRUETYPE;
1043
1044 /* We use the nonzero winding rule to find the orientation. */
1045 /* Since glyph outlines behave much more `regular' than arbitrary */
1046 /* cubic or quadratic curves, this test deals with the polygon */
1047 /* only that is spanned up by the control points. */
1048
1049 FT_Outline_Get_CBox( outline, &cbox );
1050
1051 /* Handle collapsed outlines to avoid undefined FT_MSB. */
1052 if ( cbox.xMin == cbox.xMax || cbox.yMin == cbox.yMax )
1053 return FT_ORIENTATION_NONE;
1054
1055 xshift = FT_MSB( (FT_UInt32)( FT_ABS( cbox.xMax ) |
1056 FT_ABS( cbox.xMin ) ) ) - 14;
1057 xshift = FT_MAX( xshift, 0 );
1058
1059 yshift = FT_MSB( (FT_UInt32)( cbox.yMax - cbox.yMin ) ) - 14;
1060 yshift = FT_MAX( yshift, 0 );
1061
1062 points = outline->points;
1063
1064 first = 0;
1065 for ( c = 0; c < outline->n_contours; c++ )
1066 {
1067 FT_Int last = outline->contours[c];
1068
1069
1070 v_prev.x = points[last].x >> xshift;
1071 v_prev.y = points[last].y >> yshift;
1072
1073 for ( n = first; n <= last; n++ )
1074 {
1075 v_cur.x = points[n].x >> xshift;
1076 v_cur.y = points[n].y >> yshift;
1077
1078 area = ADD_LONG( area,
1079 MUL_LONG( v_cur.y - v_prev.y,
1080 v_cur.x + v_prev.x ) );
1081
1082 v_prev = v_cur;
1083 }
1084
1085 first = last + 1;
1086 }
1087
1088 if ( area > 0 )
1089 return FT_ORIENTATION_POSTSCRIPT;
1090 else if ( area < 0 )
1091 return FT_ORIENTATION_TRUETYPE;
1092 else
1093 return FT_ORIENTATION_NONE;
1094 }
1095
1096
1097/* END */
1098