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