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, ¶ms ); |
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 | |