OLD | NEW |
1 /***************************************************************************/ | 1 /***************************************************************************/ |
2 /* */ | 2 /* */ |
3 /* cf2hints.c */ | 3 /* cf2hints.c */ |
4 /* */ | 4 /* */ |
5 /* Adobe's code for handling CFF hints (body). */ | 5 /* Adobe's code for handling CFF hints (body). */ |
6 /* */ | 6 /* */ |
7 /* Copyright 2007-2013 Adobe Systems Incorporated. */ | 7 /* Copyright 2007-2014 Adobe Systems Incorporated. */ |
8 /* */ | 8 /* */ |
9 /* This software, and all works of authorship, whether in source or */ | 9 /* This software, and all works of authorship, whether in source or */ |
10 /* object code form as indicated by the copyright notice(s) included */ | 10 /* object code form as indicated by the copyright notice(s) included */ |
11 /* herein (collectively, the "Work") is made available, and may only be */ | 11 /* herein (collectively, the "Work") is made available, and may only be */ |
12 /* used, modified, and distributed under the FreeType Project License, */ | 12 /* used, modified, and distributed under the FreeType Project License, */ |
13 /* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ | 13 /* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ |
14 /* FreeType Project License, each contributor to the Work hereby grants */ | 14 /* FreeType Project License, each contributor to the Work hereby grants */ |
15 /* to any individual or legal entity exercising permissions granted by */ | 15 /* to any individual or legal entity exercising permissions granted by */ |
16 /* the FreeType Project License and this section (hereafter, "You" or */ | 16 /* the FreeType Project License and this section (hereafter, "You" or */ |
17 /* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ | 17 /* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ |
(...skipping 12 matching lines...) Expand all Loading... |
30 /* */ | 30 /* */ |
31 /* By using, modifying, or distributing the Work you indicate that you */ | 31 /* By using, modifying, or distributing the Work you indicate that you */ |
32 /* have read and understood the terms and conditions of the */ | 32 /* have read and understood the terms and conditions of the */ |
33 /* FreeType Project License as well as those provided in this section, */ | 33 /* FreeType Project License as well as those provided in this section, */ |
34 /* and you accept them fully. */ | 34 /* and you accept them fully. */ |
35 /* */ | 35 /* */ |
36 /***************************************************************************/ | 36 /***************************************************************************/ |
37 | 37 |
38 | 38 |
39 #include "cf2ft.h" | 39 #include "cf2ft.h" |
40 #include "../../include/freetype/internal/ftdebug.h" | 40 #include FT_INTERNAL_DEBUG_H |
41 | 41 |
42 #include "cf2glue.h" | 42 #include "cf2glue.h" |
43 #include "cf2font.h" | 43 #include "cf2font.h" |
44 #include "cf2hints.h" | 44 #include "cf2hints.h" |
45 #include "cf2intrp.h" | 45 #include "cf2intrp.h" |
46 | 46 |
47 | 47 |
48 /*************************************************************************/ | 48 /*************************************************************************/ |
49 /* */ | 49 /* */ |
50 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ | 50 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ |
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
297 { | 297 { |
298 return hintmap->isValid; | 298 return hintmap->isValid; |
299 } | 299 } |
300 | 300 |
301 | 301 |
302 /* transform character space coordinate to device space using hint map */ | 302 /* transform character space coordinate to device space using hint map */ |
303 static CF2_Fixed | 303 static CF2_Fixed |
304 cf2_hintmap_map( CF2_HintMap hintmap, | 304 cf2_hintmap_map( CF2_HintMap hintmap, |
305 CF2_Fixed csCoord ) | 305 CF2_Fixed csCoord ) |
306 { | 306 { |
307 FT_ASSERT( hintmap->isValid ); /* must call Build before Map */ | |
308 FT_ASSERT( hintmap->lastIndex < CF2_MAX_HINT_EDGES ); | |
309 | |
310 if ( hintmap->count == 0 || ! hintmap->hinted ) | 307 if ( hintmap->count == 0 || ! hintmap->hinted ) |
311 { | 308 { |
312 /* there are no hints; use uniform scale and zero offset */ | 309 /* there are no hints; use uniform scale and zero offset */ |
313 return FT_MulFix( csCoord, hintmap->scale ); | 310 return FT_MulFix( csCoord, hintmap->scale ); |
314 } | 311 } |
315 else | 312 else |
316 { | 313 { |
317 /* start linear search from last hit */ | 314 /* start linear search from last hit */ |
318 CF2_UInt i = hintmap->lastIndex; | 315 CF2_UInt i = hintmap->lastIndex; |
319 | 316 |
| 317 FT_ASSERT( hintmap->lastIndex < CF2_MAX_HINT_EDGES ); |
320 | 318 |
321 /* search up */ | 319 /* search up */ |
322 while ( i < hintmap->count - 1 && | 320 while ( i < hintmap->count - 1 && |
323 csCoord >= hintmap->edge[i + 1].csCoord ) | 321 csCoord >= hintmap->edge[i + 1].csCoord ) |
324 i += 1; | 322 i += 1; |
325 | 323 |
326 /* search down */ | 324 /* search down */ |
327 while ( i > 0 && csCoord < hintmap->edge[i].csCoord ) | 325 while ( i > 0 && csCoord < hintmap->edge[i].csCoord ) |
328 i -= 1; | 326 i -= 1; |
329 | 327 |
(...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
589 } | 587 } |
590 | 588 |
591 /* paired edges must be in proper order */ | 589 /* paired edges must be in proper order */ |
592 FT_ASSERT( !isPair || | 590 FT_ASSERT( !isPair || |
593 topHintEdge->csCoord >= bottomHintEdge->csCoord ); | 591 topHintEdge->csCoord >= bottomHintEdge->csCoord ); |
594 | 592 |
595 /* linear search to find index value of insertion point */ | 593 /* linear search to find index value of insertion point */ |
596 indexInsert = 0; | 594 indexInsert = 0; |
597 for ( ; indexInsert < hintmap->count; indexInsert++ ) | 595 for ( ; indexInsert < hintmap->count; indexInsert++ ) |
598 { | 596 { |
599 if ( hintmap->edge[indexInsert].csCoord > firstHintEdge->csCoord ) | 597 if ( hintmap->edge[indexInsert].csCoord >= firstHintEdge->csCoord ) |
600 break; | 598 break; |
601 } | 599 } |
602 | 600 |
603 /* | 601 /* |
604 * Discard any hints that overlap in character space. Most often, | 602 * Discard any hints that overlap in character space. Most often, this |
605 * this is while building the initial map, but in theory, it can also | 603 * is while building the initial map, where captured hints from all |
606 * occur because of darkening. | 604 * zones are combined. Define overlap to include hints that `touch' |
| 605 * (overlap zero). Hiragino Sans/Gothic fonts have numerous hints that |
| 606 * touch. Some fonts have non-ideographic glyphs that overlap our |
| 607 * synthetic hints. |
| 608 * |
| 609 * Overlap also occurs when darkening stem hints that are close. |
607 * | 610 * |
608 */ | 611 */ |
609 if ( indexInsert < hintmap->count ) | 612 if ( indexInsert < hintmap->count ) |
610 { | 613 { |
611 /* we are inserting before an existing edge: */ | 614 /* we are inserting before an existing edge: */ |
| 615 /* verify that an existing edge is not the same */ |
| 616 if ( hintmap->edge[indexInsert].csCoord == firstHintEdge->csCoord ) |
| 617 return; /* ignore overlapping stem hint */ |
| 618 |
612 /* verify that a new pair does not straddle the next edge */ | 619 /* verify that a new pair does not straddle the next edge */ |
613 if ( isPair && | 620 if ( isPair && |
614 hintmap->edge[indexInsert].csCoord < secondHintEdge->csCoord ) | 621 hintmap->edge[indexInsert].csCoord <= secondHintEdge->csCoord ) |
615 return; /* ignore overlapping stem hint */ | 622 return; /* ignore overlapping stem hint */ |
616 | 623 |
617 /* verify that we are not inserting between paired edges */ | 624 /* verify that we are not inserting between paired edges */ |
618 if ( cf2_hint_isPairTop( &hintmap->edge[indexInsert] ) ) | 625 if ( cf2_hint_isPairTop( &hintmap->edge[indexInsert] ) ) |
619 return; /* ignore overlapping stem hint */ | 626 return; /* ignore overlapping stem hint */ |
620 } | 627 } |
621 | 628 |
622 /* recompute device space locations using initial hint map */ | 629 /* recompute device space locations using initial hint map */ |
623 if ( cf2_hintmap_isValid( hintmap->initialHintMap ) && | 630 if ( cf2_hintmap_isValid( hintmap->initialHintMap ) && |
624 !cf2_hint_isLocked( firstHintEdge ) ) | 631 !cf2_hint_isLocked( firstHintEdge ) ) |
(...skipping 13 matching lines...) Expand all Loading... |
638 | 645 |
639 | 646 |
640 firstHintEdge->dsCoord = midpoint - halfWidth; | 647 firstHintEdge->dsCoord = midpoint - halfWidth; |
641 secondHintEdge->dsCoord = midpoint + halfWidth; | 648 secondHintEdge->dsCoord = midpoint + halfWidth; |
642 } | 649 } |
643 else | 650 else |
644 firstHintEdge->dsCoord = cf2_hintmap_map( hintmap->initialHintMap, | 651 firstHintEdge->dsCoord = cf2_hintmap_map( hintmap->initialHintMap, |
645 firstHintEdge->csCoord ); | 652 firstHintEdge->csCoord ); |
646 } | 653 } |
647 | 654 |
648 /* discard any hints that overlap in device space; this can occur */ | 655 /* |
649 /* because locked hints have been moved to align with blue zones */ | 656 * Discard any hints that overlap in device space; this can occur |
| 657 * because locked hints have been moved to align with blue zones. |
| 658 * |
| 659 * TODO: Although we might correct this later during adjustment, we |
| 660 * don't currently have a way to delete a conflicting hint once it has |
| 661 * been inserted. See v2.030 MinionPro-Regular, 12 ppem darkened, |
| 662 * initial hint map for second path, glyph 945 (the perispomeni (tilde) |
| 663 * in U+1F6E, Greek omega with psili and perispomeni). Darkening is |
| 664 * 25. Pair 667,747 initially conflicts in design space with top edge |
| 665 * 660. This is because 667 maps to 7.87, and the top edge was |
| 666 * captured by a zone at 8.0. The pair is later successfully inserted |
| 667 * in a zone without the top edge. In this zone it is adjusted to 8.0, |
| 668 * and no longer conflicts with the top edge in design space. This |
| 669 * means it can be included in yet a later zone which does have the top |
| 670 * edge hint. This produces a small mismatch between the first and |
| 671 * last points of this path, even though the hint masks are the same. |
| 672 * The density map difference is tiny (1/256). |
| 673 * |
| 674 */ |
| 675 |
650 if ( indexInsert > 0 ) | 676 if ( indexInsert > 0 ) |
651 { | 677 { |
652 /* we are inserting after an existing edge */ | 678 /* we are inserting after an existing edge */ |
653 if ( firstHintEdge->dsCoord < hintmap->edge[indexInsert - 1].dsCoord ) | 679 if ( firstHintEdge->dsCoord < hintmap->edge[indexInsert - 1].dsCoord ) |
654 return; | 680 return; |
655 } | 681 } |
656 | 682 |
657 if ( indexInsert < hintmap->count ) | 683 if ( indexInsert < hintmap->count ) |
658 { | 684 { |
659 /* we are inserting before an existing edge */ | 685 /* we are inserting before an existing edge */ |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
718 */ | 744 */ |
719 FT_LOCAL_DEF( void ) | 745 FT_LOCAL_DEF( void ) |
720 cf2_hintmap_build( CF2_HintMap hintmap, | 746 cf2_hintmap_build( CF2_HintMap hintmap, |
721 CF2_ArrStack hStemHintArray, | 747 CF2_ArrStack hStemHintArray, |
722 CF2_ArrStack vStemHintArray, | 748 CF2_ArrStack vStemHintArray, |
723 CF2_HintMask hintMask, | 749 CF2_HintMask hintMask, |
724 CF2_Fixed hintOrigin, | 750 CF2_Fixed hintOrigin, |
725 FT_Bool initialMap ) | 751 FT_Bool initialMap ) |
726 { | 752 { |
727 FT_Byte* maskPtr; | 753 FT_Byte* maskPtr; |
728 FT_Byte* maskEndPtr; // add by Xiaochuan_Liu | |
729 | 754 |
730 CF2_Font font = hintmap->font; | 755 CF2_Font font = hintmap->font; |
731 CF2_HintMaskRec tempHintMask; | 756 CF2_HintMaskRec tempHintMask; |
732 | 757 |
733 size_t bitCount, i; | 758 size_t bitCount, i; |
734 FT_Byte maskByte; | 759 FT_Byte maskByte; |
735 | 760 |
736 | 761 |
737 /* check whether initial map is constructed */ | 762 /* check whether initial map is constructed */ |
738 if ( !initialMap && !cf2_hintmap_isValid( hintmap->initialHintMap ) ) | 763 if ( !initialMap && !cf2_hintmap_isValid( hintmap->initialHintMap ) ) |
739 { | 764 { |
740 /* make recursive call with initialHintMap and temporary mask; */ | 765 /* make recursive call with initialHintMap and temporary mask; */ |
741 /* temporary mask will get all bits set, below */ | 766 /* temporary mask will get all bits set, below */ |
742 cf2_hintmask_init( &tempHintMask, hintMask->error ); | 767 cf2_hintmask_init( &tempHintMask, hintMask->error ); |
743 cf2_hintmap_build( hintmap->initialHintMap, | 768 cf2_hintmap_build( hintmap->initialHintMap, |
744 hStemHintArray, | 769 hStemHintArray, |
745 vStemHintArray, | 770 vStemHintArray, |
746 &tempHintMask, | 771 &tempHintMask, |
747 hintOrigin, | 772 hintOrigin, |
748 TRUE ); | 773 TRUE ); |
749 } | 774 } |
750 | 775 |
751 if ( !cf2_hintmask_isValid( hintMask ) ) | 776 if ( !cf2_hintmask_isValid( hintMask ) ) |
752 { | 777 { |
753 /* without a hint mask, assume all hints are active */ | 778 /* without a hint mask, assume all hints are active */ |
754 cf2_hintmask_setAll( hintMask, | 779 cf2_hintmask_setAll( hintMask, |
755 cf2_arrstack_size( hStemHintArray ) + | 780 cf2_arrstack_size( hStemHintArray ) + |
756 cf2_arrstack_size( vStemHintArray ) ); | 781 cf2_arrstack_size( vStemHintArray ) ); |
| 782 if ( !cf2_hintmask_isValid( hintMask ) ) |
| 783 return; /* too many stem hints */ |
757 } | 784 } |
758 | 785 |
759 /* begin by clearing the map */ | 786 /* begin by clearing the map */ |
760 hintmap->count = 0; | 787 hintmap->count = 0; |
761 hintmap->lastIndex = 0; | 788 hintmap->lastIndex = 0; |
762 | 789 |
763 /* make a copy of the hint mask so we can modify it */ | 790 /* make a copy of the hint mask so we can modify it */ |
764 tempHintMask = *hintMask; | 791 tempHintMask = *hintMask; |
765 maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask ); | 792 maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask ); |
766 maskEndPtr = maskPtr + ( CF2_MAX_HINTS + 7 ) / 8; | |
767 | |
768 | 793 |
769 /* use the hStem hints only, which are first in the mask */ | 794 /* use the hStem hints only, which are first in the mask */ |
770 /* TODO: compare this to cffhintmaskGetBitCount */ | |
771 bitCount = cf2_arrstack_size( hStemHintArray ); | 795 bitCount = cf2_arrstack_size( hStemHintArray ); |
772 | 796 |
| 797 /* Defense-in-depth. Should never return here. */ |
| 798 if ( bitCount > hintMask->bitCount ) |
| 799 return; |
| 800 |
773 /* synthetic embox hints get highest priority */ | 801 /* synthetic embox hints get highest priority */ |
774 if ( font->blues.doEmBoxHints ) | 802 if ( font->blues.doEmBoxHints ) |
775 { | 803 { |
776 CF2_HintRec dummy; | 804 CF2_HintRec dummy; |
777 | 805 |
778 | 806 |
779 cf2_hint_initZero( &dummy ); /* invalid hint map element */ | 807 cf2_hint_initZero( &dummy ); /* invalid hint map element */ |
780 | 808 |
781 /* ghost bottom */ | 809 /* ghost bottom */ |
782 cf2_hintmap_insertHint( hintmap, | 810 cf2_hintmap_insertHint( hintmap, |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
823 cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge ); | 851 cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge ); |
824 | 852 |
825 *maskPtr &= ~maskByte; /* turn off the bit for this hint */ | 853 *maskPtr &= ~maskByte; /* turn off the bit for this hint */ |
826 } | 854 } |
827 } | 855 } |
828 | 856 |
829 if ( ( i & 7 ) == 7 ) | 857 if ( ( i & 7 ) == 7 ) |
830 { | 858 { |
831 /* move to next mask byte */ | 859 /* move to next mask byte */ |
832 maskPtr++; | 860 maskPtr++; |
833 if (maskPtr >= maskEndPtr) | |
834 { | |
835 break; | |
836 } | |
837 | |
838 maskByte = 0x80; | 861 maskByte = 0x80; |
839 } | 862 } |
840 else | 863 else |
841 maskByte >>= 1; | 864 maskByte >>= 1; |
842 } | 865 } |
843 | 866 |
844 /* initial hint map includes only captured hints plus maybe one at 0 */ | 867 /* initial hint map includes only captured hints plus maybe one at 0 */ |
845 | 868 |
846 /* | 869 /* |
847 * TODO: There is a problem here because we are trying to build a | 870 * TODO: There is a problem here because we are trying to build a |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
888 | 911 |
889 cf2_hint_initZero( &invalid ); | 912 cf2_hint_initZero( &invalid ); |
890 cf2_hintmap_insertHint( hintmap, &edge, &invalid ); | 913 cf2_hintmap_insertHint( hintmap, &edge, &invalid ); |
891 } | 914 } |
892 } | 915 } |
893 else | 916 else |
894 { | 917 { |
895 /* insert remaining hints */ | 918 /* insert remaining hints */ |
896 | 919 |
897 maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask ); | 920 maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask ); |
898 maskEndPtr = maskPtr + ( CF2_MAX_HINTS + 7 ) / 8; | |
899 | 921 |
900 for ( i = 0, maskByte = 0x80; i < bitCount; i++ ) | 922 for ( i = 0, maskByte = 0x80; i < bitCount; i++ ) |
901 { | 923 { |
902 if ( maskByte & *maskPtr ) | 924 if ( maskByte & *maskPtr ) |
903 { | 925 { |
904 CF2_HintRec bottomHintEdge, topHintEdge; | 926 CF2_HintRec bottomHintEdge, topHintEdge; |
905 | 927 |
906 | 928 |
907 cf2_hint_init( &bottomHintEdge, | 929 cf2_hint_init( &bottomHintEdge, |
908 hStemHintArray, | 930 hStemHintArray, |
(...skipping 10 matching lines...) Expand all Loading... |
919 hintmap->scale, | 941 hintmap->scale, |
920 FALSE /* top */ ); | 942 FALSE /* top */ ); |
921 | 943 |
922 cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge ); | 944 cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge ); |
923 } | 945 } |
924 | 946 |
925 if ( ( i & 7 ) == 7 ) | 947 if ( ( i & 7 ) == 7 ) |
926 { | 948 { |
927 /* move to next mask byte */ | 949 /* move to next mask byte */ |
928 maskPtr++; | 950 maskPtr++; |
929 if (maskPtr >= maskEndPtr) | |
930 { | |
931 break; | |
932 } | |
933 maskByte = 0x80; | 951 maskByte = 0x80; |
934 } | 952 } |
935 else | 953 else |
936 maskByte >>= 1; | 954 maskByte >>= 1; |
937 } | 955 } |
938 } | 956 } |
939 | 957 |
940 /* | 958 /* |
941 * Note: The following line is a convenient place to break when | 959 * Note: The following line is a convenient place to break when |
942 * debugging hinting. Examine `hintmap->edge' for the list of | 960 * debugging hinting. Examine `hintmap->edge' for the list of |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1041 glyphpath->yOffset = font->darkenY; | 1059 glyphpath->yOffset = font->darkenY; |
1042 glyphpath->miterLimit = 2 * FT_MAX( | 1060 glyphpath->miterLimit = 2 * FT_MAX( |
1043 cf2_fixedAbs( glyphpath->xOffset ), | 1061 cf2_fixedAbs( glyphpath->xOffset ), |
1044 cf2_fixedAbs( glyphpath->yOffset ) ); | 1062 cf2_fixedAbs( glyphpath->yOffset ) ); |
1045 | 1063 |
1046 /* .1 character space unit */ | 1064 /* .1 character space unit */ |
1047 glyphpath->snapThreshold = cf2_floatToFixed( 0.1f ); | 1065 glyphpath->snapThreshold = cf2_floatToFixed( 0.1f ); |
1048 | 1066 |
1049 glyphpath->moveIsPending = TRUE; | 1067 glyphpath->moveIsPending = TRUE; |
1050 glyphpath->pathIsOpen = FALSE; | 1068 glyphpath->pathIsOpen = FALSE; |
| 1069 glyphpath->pathIsClosing = FALSE; |
1051 glyphpath->elemIsQueued = FALSE; | 1070 glyphpath->elemIsQueued = FALSE; |
1052 } | 1071 } |
1053 | 1072 |
1054 | 1073 |
1055 FT_LOCAL_DEF( void ) | 1074 FT_LOCAL_DEF( void ) |
1056 cf2_glyphpath_finalize( CF2_GlyphPath glyphpath ) | 1075 cf2_glyphpath_finalize( CF2_GlyphPath glyphpath ) |
1057 { | 1076 { |
1058 cf2_arrstack_finalize( &glyphpath->hintMoves ); | 1077 cf2_arrstack_finalize( &glyphpath->hintMoves ); |
1059 } | 1078 } |
1060 | 1079 |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1181 glyphpath->miterLimit ) | 1200 glyphpath->miterLimit ) |
1182 return FALSE; | 1201 return FALSE; |
1183 | 1202 |
1184 return TRUE; | 1203 return TRUE; |
1185 } | 1204 } |
1186 | 1205 |
1187 | 1206 |
1188 /* | 1207 /* |
1189 * Push the cached element (glyphpath->prevElem*) to the outline | 1208 * Push the cached element (glyphpath->prevElem*) to the outline |
1190 * consumer. When a darkening offset is used, the end point of the | 1209 * consumer. When a darkening offset is used, the end point of the |
1191 * cached element may be adjusted to an intersection point or it may be | 1210 * cached element may be adjusted to an intersection point or we may |
1192 * connected by a line to the current element. This calculation must | 1211 * synthesize a connecting line to the current element. If we are |
1193 * use a HintMap that was valid at the time the element was saved. For | 1212 * closing a subpath, we may also generate a connecting line to the start |
1194 * the first point in a subpath, that is a saved HintMap. For most | 1213 * point. |
1195 * elements, it just means the caller has delayed building a HintMap | 1214 * |
1196 * from the current HintMask. | 1215 * This is where Character Space (CS) is converted to Device Space (DS) |
| 1216 * using a hint map. This calculation must use a HintMap that was valid |
| 1217 * at the time the element was saved. For the first point in a subpath, |
| 1218 * that is a saved HintMap. For most elements, it just means the caller |
| 1219 * has delayed building a HintMap from the current HintMask. |
1197 * | 1220 * |
1198 * Transform each point with outerTransform and call the outline | 1221 * Transform each point with outerTransform and call the outline |
1199 * callbacks. This is a general 3x3 transform: | 1222 * callbacks. This is a general 3x3 transform: |
1200 * | 1223 * |
1201 * x' = a*x + c*y + tx, y' = b*x + d*y + ty | 1224 * x' = a*x + c*y + tx, y' = b*x + d*y + ty |
1202 * | 1225 * |
1203 * but it uses 4 elements from CF2_Font and the translation part | 1226 * but it uses 4 elements from CF2_Font and the translation part |
1204 * from CF2_GlyphPath. | 1227 * from CF2_GlyphPath. |
1205 * | 1228 * |
1206 */ | 1229 */ |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1256 } | 1279 } |
1257 | 1280 |
1258 params.pt0 = glyphpath->currentDS; | 1281 params.pt0 = glyphpath->currentDS; |
1259 | 1282 |
1260 switch( glyphpath->prevElemOp ) | 1283 switch( glyphpath->prevElemOp ) |
1261 { | 1284 { |
1262 case CF2_PathOpLineTo: | 1285 case CF2_PathOpLineTo: |
1263 params.op = CF2_PathOpLineTo; | 1286 params.op = CF2_PathOpLineTo; |
1264 | 1287 |
1265 /* note: pt2 and pt3 are unused */ | 1288 /* note: pt2 and pt3 are unused */ |
1266 cf2_glyphpath_hintPoint( glyphpath, | |
1267 hintmap, | |
1268 ¶ms.pt1, | |
1269 glyphpath->prevElemP1.x, | |
1270 glyphpath->prevElemP1.y ); | |
1271 | 1289 |
1272 glyphpath->callbacks->lineTo( glyphpath->callbacks, ¶ms ); | 1290 if ( close ) |
| 1291 { |
| 1292 /* use first hint map if closing */ |
| 1293 cf2_glyphpath_hintPoint( glyphpath, |
| 1294 &glyphpath->firstHintMap, |
| 1295 ¶ms.pt1, |
| 1296 glyphpath->prevElemP1.x, |
| 1297 glyphpath->prevElemP1.y ); |
| 1298 } |
| 1299 else |
| 1300 { |
| 1301 cf2_glyphpath_hintPoint( glyphpath, |
| 1302 hintmap, |
| 1303 ¶ms.pt1, |
| 1304 glyphpath->prevElemP1.x, |
| 1305 glyphpath->prevElemP1.y ); |
| 1306 } |
1273 | 1307 |
1274 glyphpath->currentDS = params.pt1; | 1308 /* output only non-zero length lines */ |
| 1309 if ( params.pt0.x != params.pt1.x || params.pt0.y != params.pt1.y ) |
| 1310 { |
| 1311 glyphpath->callbacks->lineTo( glyphpath->callbacks, ¶ms ); |
1275 | 1312 |
| 1313 glyphpath->currentDS = params.pt1; |
| 1314 } |
1276 break; | 1315 break; |
1277 | 1316 |
1278 case CF2_PathOpCubeTo: | 1317 case CF2_PathOpCubeTo: |
1279 params.op = CF2_PathOpCubeTo; | 1318 params.op = CF2_PathOpCubeTo; |
1280 | 1319 |
1281 /* TODO: should we intersect the interior joins (p1-p2 and p2-p3)? */ | 1320 /* TODO: should we intersect the interior joins (p1-p2 and p2-p3)? */ |
1282 cf2_glyphpath_hintPoint( glyphpath, | 1321 cf2_glyphpath_hintPoint( glyphpath, |
1283 hintmap, | 1322 hintmap, |
1284 ¶ms.pt1, | 1323 ¶ms.pt1, |
1285 glyphpath->prevElemP1.x, | 1324 glyphpath->prevElemP1.x, |
(...skipping 16 matching lines...) Expand all Loading... |
1302 break; | 1341 break; |
1303 } | 1342 } |
1304 | 1343 |
1305 if ( !useIntersection || close ) | 1344 if ( !useIntersection || close ) |
1306 { | 1345 { |
1307 /* insert connecting line between end of previous element and start */ | 1346 /* insert connecting line between end of previous element and start */ |
1308 /* of current one */ | 1347 /* of current one */ |
1309 /* note: at the end of a subpath, we might do both, so use `nextP0' */ | 1348 /* note: at the end of a subpath, we might do both, so use `nextP0' */ |
1310 /* before we change it, below */ | 1349 /* before we change it, below */ |
1311 | 1350 |
1312 cf2_glyphpath_hintPoint( glyphpath, | 1351 if ( close ) |
1313 hintmap, | 1352 { |
1314 ¶ms.pt1, | 1353 /* if we are closing the subpath, then nextP0 is in the first */ |
1315 nextP0->x, | 1354 /* hint zone */ |
1316 nextP0->y ); | 1355 cf2_glyphpath_hintPoint( glyphpath, |
| 1356 &glyphpath->firstHintMap, |
| 1357 ¶ms.pt1, |
| 1358 nextP0->x, |
| 1359 nextP0->y ); |
| 1360 } |
| 1361 else |
| 1362 { |
| 1363 cf2_glyphpath_hintPoint( glyphpath, |
| 1364 hintmap, |
| 1365 ¶ms.pt1, |
| 1366 nextP0->x, |
| 1367 nextP0->y ); |
| 1368 } |
1317 | 1369 |
1318 if ( params.pt1.x != glyphpath->currentDS.x || | 1370 if ( params.pt1.x != glyphpath->currentDS.x || |
1319 params.pt1.y != glyphpath->currentDS.y ) | 1371 params.pt1.y != glyphpath->currentDS.y ) |
1320 { | 1372 { |
1321 /* length is nonzero */ | 1373 /* length is nonzero */ |
1322 params.op = CF2_PathOpLineTo; | 1374 params.op = CF2_PathOpLineTo; |
1323 params.pt0 = glyphpath->currentDS; | 1375 params.pt0 = glyphpath->currentDS; |
1324 | 1376 |
1325 /* note: pt2 and pt3 are unused */ | 1377 /* note: pt2 and pt3 are unused */ |
1326 glyphpath->callbacks->lineTo( glyphpath->callbacks, ¶ms ); | 1378 glyphpath->callbacks->lineTo( glyphpath->callbacks, ¶ms ); |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1502 if ( -dx > -2 * dy ) | 1554 if ( -dx > -2 * dy ) |
1503 { | 1555 { |
1504 /* -x */ | 1556 /* -x */ |
1505 *x = 0; | 1557 *x = 0; |
1506 *y = 2 * glyphpath->yOffset; | 1558 *y = 2 * glyphpath->yOffset; |
1507 } | 1559 } |
1508 else if ( -dy > -2 * dx ) | 1560 else if ( -dy > -2 * dx ) |
1509 { | 1561 { |
1510 /* -y */ | 1562 /* -y */ |
1511 *x = -glyphpath->xOffset; | 1563 *x = -glyphpath->xOffset; |
1512 *y = glyphpath->xOffset; | 1564 *y = glyphpath->yOffset; |
1513 } | 1565 } |
1514 else | 1566 else |
1515 { | 1567 { |
1516 /* -x -y */ | 1568 /* -x -y */ |
1517 *x = FT_MulFix( cf2_floatToFixed( -0.7 ), | 1569 *x = FT_MulFix( cf2_floatToFixed( -0.7 ), |
1518 glyphpath->xOffset ); | 1570 glyphpath->xOffset ); |
1519 *y = FT_MulFix( cf2_floatToFixed( 1.0 + 0.7 ), | 1571 *y = FT_MulFix( cf2_floatToFixed( 1.0 + 0.7 ), |
1520 glyphpath->yOffset ); | 1572 glyphpath->yOffset ); |
1521 } | 1573 } |
1522 } | 1574 } |
1523 } | 1575 } |
1524 } | 1576 } |
1525 | 1577 |
1526 | 1578 |
| 1579 /* |
| 1580 * The functions cf2_glyphpath_{moveTo,lineTo,curveTo,closeOpenPath} are |
| 1581 * called by the interpreter with Character Space (CS) coordinates. Each |
| 1582 * path element is placed into a queue of length one to await the |
| 1583 * calculation of the following element. At that time, the darkening |
| 1584 * offset of the following element is known and joins can be computed, |
| 1585 * including possible modification of this element, before mapping to |
| 1586 * Device Space (DS) and passing it on to the outline consumer. |
| 1587 * |
| 1588 */ |
1527 FT_LOCAL_DEF( void ) | 1589 FT_LOCAL_DEF( void ) |
1528 cf2_glyphpath_moveTo( CF2_GlyphPath glyphpath, | 1590 cf2_glyphpath_moveTo( CF2_GlyphPath glyphpath, |
1529 CF2_Fixed x, | 1591 CF2_Fixed x, |
1530 CF2_Fixed y ) | 1592 CF2_Fixed y ) |
1531 { | 1593 { |
1532 cf2_glyphpath_closeOpenPath( glyphpath ); | 1594 cf2_glyphpath_closeOpenPath( glyphpath ); |
1533 | 1595 |
1534 /* save the parameters of the move for later, when we'll know how to */ | 1596 /* save the parameters of the move for later, when we'll know how to */ |
1535 /* offset it; */ | 1597 /* offset it; */ |
1536 /* also save last move point */ | 1598 /* also save last move point */ |
(...skipping 17 matching lines...) Expand all Loading... |
1554 } | 1616 } |
1555 | 1617 |
1556 | 1618 |
1557 FT_LOCAL_DEF( void ) | 1619 FT_LOCAL_DEF( void ) |
1558 cf2_glyphpath_lineTo( CF2_GlyphPath glyphpath, | 1620 cf2_glyphpath_lineTo( CF2_GlyphPath glyphpath, |
1559 CF2_Fixed x, | 1621 CF2_Fixed x, |
1560 CF2_Fixed y ) | 1622 CF2_Fixed y ) |
1561 { | 1623 { |
1562 CF2_Fixed xOffset, yOffset; | 1624 CF2_Fixed xOffset, yOffset; |
1563 FT_Vector P0, P1; | 1625 FT_Vector P0, P1; |
| 1626 FT_Bool newHintMap; |
1564 | 1627 |
| 1628 /* |
| 1629 * New hints will be applied after cf2_glyphpath_pushPrevElem has run. |
| 1630 * In case this is a synthesized closing line, any new hints should be |
| 1631 * delayed until this path is closed (`cf2_hintmask_isNew' will be |
| 1632 * called again before the next line or curve). |
| 1633 */ |
1565 | 1634 |
1566 /* can't compute offset of zero length line, so ignore them */ | 1635 /* true if new hint map not on close */ |
1567 if ( glyphpath->currentCS.x == x && glyphpath->currentCS.y == y ) | 1636 newHintMap = cf2_hintmask_isNew( glyphpath->hintMask ) && |
| 1637 !glyphpath->pathIsClosing; |
| 1638 |
| 1639 /* |
| 1640 * Zero-length lines may occur in the charstring. Because we cannot |
| 1641 * compute darkening offsets or intersections from zero-length lines, |
| 1642 * it is best to remove them and avoid artifacts. However, zero-length |
| 1643 * lines in CS at the start of a new hint map can generate non-zero |
| 1644 * lines in DS due to hint substitution. We detect a change in hint |
| 1645 * map here and pass those zero-length lines along. |
| 1646 */ |
| 1647 |
| 1648 /* |
| 1649 * Note: Find explicitly closed paths here with a conditional |
| 1650 * breakpoint using |
| 1651 * |
| 1652 * !gp->pathIsClosing && gp->start.x == x && gp->start.y == y |
| 1653 * |
| 1654 */ |
| 1655 |
| 1656 if ( glyphpath->currentCS.x == x && |
| 1657 glyphpath->currentCS.y == y && |
| 1658 !newHintMap ) |
| 1659 /* |
| 1660 * Ignore zero-length lines in CS where the hint map is the same |
| 1661 * because the line in DS will also be zero length. |
| 1662 * |
| 1663 * Ignore zero-length lines when we synthesize a closing line because |
| 1664 * the close will be handled in cf2_glyphPath_pushPrevElem. |
| 1665 */ |
1568 return; | 1666 return; |
1569 | 1667 |
1570 cf2_glyphpath_computeOffset( glyphpath, | 1668 cf2_glyphpath_computeOffset( glyphpath, |
1571 glyphpath->currentCS.x, | 1669 glyphpath->currentCS.x, |
1572 glyphpath->currentCS.y, | 1670 glyphpath->currentCS.y, |
1573 x, | 1671 x, |
1574 y, | 1672 y, |
1575 &xOffset, | 1673 &xOffset, |
1576 &yOffset ); | 1674 &yOffset ); |
1577 | 1675 |
1578 /* construct offset points */ | 1676 /* construct offset points */ |
1579 P0.x = glyphpath->currentCS.x + xOffset; | 1677 P0.x = glyphpath->currentCS.x + xOffset; |
1580 P0.y = glyphpath->currentCS.y + yOffset; | 1678 P0.y = glyphpath->currentCS.y + yOffset; |
1581 P1.x = x + xOffset; | 1679 P1.x = x + xOffset; |
1582 P1.y = y + yOffset; | 1680 P1.y = y + yOffset; |
1583 | 1681 |
1584 if ( glyphpath->moveIsPending ) | 1682 if ( glyphpath->moveIsPending ) |
1585 { | 1683 { |
1586 /* emit offset 1st point as MoveTo */ | 1684 /* emit offset 1st point as MoveTo */ |
1587 cf2_glyphpath_pushMove( glyphpath, P0 ); | 1685 cf2_glyphpath_pushMove( glyphpath, P0 ); |
1588 if (glyphpath->callbacks && glyphpath->callbacks->error && *glyphpath-
>callbacks->error) return; | |
1589 | 1686 |
1590 glyphpath->moveIsPending = FALSE; /* adjust state machine */ | 1687 glyphpath->moveIsPending = FALSE; /* adjust state machine */ |
1591 glyphpath->pathIsOpen = TRUE; | 1688 glyphpath->pathIsOpen = TRUE; |
1592 | 1689 |
1593 glyphpath->offsetStart1 = P1; /* record second point */ | 1690 glyphpath->offsetStart1 = P1; /* record second point */ |
1594 } | 1691 } |
1595 | 1692 |
1596 if ( glyphpath->elemIsQueued ) | 1693 if ( glyphpath->elemIsQueued ) |
1597 { | 1694 { |
1598 FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) ); | 1695 FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) || |
| 1696 glyphpath->hintMap.count == 0 ); |
1599 | 1697 |
1600 cf2_glyphpath_pushPrevElem( glyphpath, | 1698 cf2_glyphpath_pushPrevElem( glyphpath, |
1601 &glyphpath->hintMap, | 1699 &glyphpath->hintMap, |
1602 &P0, | 1700 &P0, |
1603 P1, | 1701 P1, |
1604 FALSE ); | 1702 FALSE ); |
1605 if (glyphpath->callbacks && glyphpath->callbacks->error && *glyphpath-
>callbacks->error) return; | |
1606 } | 1703 } |
1607 | 1704 |
1608 /* queue the current element with offset points */ | 1705 /* queue the current element with offset points */ |
1609 glyphpath->elemIsQueued = TRUE; | 1706 glyphpath->elemIsQueued = TRUE; |
1610 glyphpath->prevElemOp = CF2_PathOpLineTo; | 1707 glyphpath->prevElemOp = CF2_PathOpLineTo; |
1611 glyphpath->prevElemP0 = P0; | 1708 glyphpath->prevElemP0 = P0; |
1612 glyphpath->prevElemP1 = P1; | 1709 glyphpath->prevElemP1 = P1; |
1613 | 1710 |
1614 /* update current map */ | 1711 /* update current map */ |
1615 if ( cf2_hintmask_isNew( glyphpath->hintMask ) ) | 1712 if ( newHintMap ) |
1616 cf2_hintmap_build( &glyphpath->hintMap, | 1713 cf2_hintmap_build( &glyphpath->hintMap, |
1617 glyphpath->hStemHintArray, | 1714 glyphpath->hStemHintArray, |
1618 glyphpath->vStemHintArray, | 1715 glyphpath->vStemHintArray, |
1619 glyphpath->hintMask, | 1716 glyphpath->hintMask, |
1620 glyphpath->hintOriginY, | 1717 glyphpath->hintOriginY, |
1621 FALSE ); | 1718 FALSE ); |
1622 | 1719 |
1623 glyphpath->currentCS.x = x; /* pre-offset current point */ | 1720 glyphpath->currentCS.x = x; /* pre-offset current point */ |
1624 glyphpath->currentCS.y = y; | 1721 glyphpath->currentCS.y = y; |
1625 } | 1722 } |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1666 /* note: preserve angle of final segment by using offset3 at both ends */ | 1763 /* note: preserve angle of final segment by using offset3 at both ends */ |
1667 P2.x = x2 + xOffset3; | 1764 P2.x = x2 + xOffset3; |
1668 P2.y = y2 + yOffset3; | 1765 P2.y = y2 + yOffset3; |
1669 P3.x = x3 + xOffset3; | 1766 P3.x = x3 + xOffset3; |
1670 P3.y = y3 + yOffset3; | 1767 P3.y = y3 + yOffset3; |
1671 | 1768 |
1672 if ( glyphpath->moveIsPending ) | 1769 if ( glyphpath->moveIsPending ) |
1673 { | 1770 { |
1674 /* emit offset 1st point as MoveTo */ | 1771 /* emit offset 1st point as MoveTo */ |
1675 cf2_glyphpath_pushMove( glyphpath, P0 ); | 1772 cf2_glyphpath_pushMove( glyphpath, P0 ); |
1676 if (glyphpath->callbacks && glyphpath->callbacks->error && *glyphpath-
>callbacks->error) return; | |
1677 | 1773 |
1678 glyphpath->moveIsPending = FALSE; | 1774 glyphpath->moveIsPending = FALSE; |
1679 glyphpath->pathIsOpen = TRUE; | 1775 glyphpath->pathIsOpen = TRUE; |
1680 | 1776 |
1681 glyphpath->offsetStart1 = P1; /* record second point */ | 1777 glyphpath->offsetStart1 = P1; /* record second point */ |
1682 } | 1778 } |
1683 | 1779 |
1684 if ( glyphpath->elemIsQueued ) | 1780 if ( glyphpath->elemIsQueued ) |
1685 { | 1781 { |
1686 FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) ); | 1782 FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) || |
| 1783 glyphpath->hintMap.count == 0 ); |
1687 | 1784 |
1688 cf2_glyphpath_pushPrevElem( glyphpath, | 1785 cf2_glyphpath_pushPrevElem( glyphpath, |
1689 &glyphpath->hintMap, | 1786 &glyphpath->hintMap, |
1690 &P0, | 1787 &P0, |
1691 P1, | 1788 P1, |
1692 FALSE ); | 1789 FALSE ); |
1693 if (glyphpath->callbacks && glyphpath->callbacks->error && *glyphpath-
>callbacks->error) return; | |
1694 } | 1790 } |
1695 | 1791 |
1696 /* queue the current element with offset points */ | 1792 /* queue the current element with offset points */ |
1697 glyphpath->elemIsQueued = TRUE; | 1793 glyphpath->elemIsQueued = TRUE; |
1698 glyphpath->prevElemOp = CF2_PathOpCubeTo; | 1794 glyphpath->prevElemOp = CF2_PathOpCubeTo; |
1699 glyphpath->prevElemP0 = P0; | 1795 glyphpath->prevElemP0 = P0; |
1700 glyphpath->prevElemP1 = P1; | 1796 glyphpath->prevElemP1 = P1; |
1701 glyphpath->prevElemP2 = P2; | 1797 glyphpath->prevElemP2 = P2; |
1702 glyphpath->prevElemP3 = P3; | 1798 glyphpath->prevElemP3 = P3; |
1703 | 1799 |
1704 /* update current map */ | 1800 /* update current map */ |
1705 if ( cf2_hintmask_isNew( glyphpath->hintMask ) ) | 1801 if ( cf2_hintmask_isNew( glyphpath->hintMask ) ) |
1706 cf2_hintmap_build( &glyphpath->hintMap, | 1802 cf2_hintmap_build( &glyphpath->hintMap, |
1707 glyphpath->hStemHintArray, | 1803 glyphpath->hStemHintArray, |
1708 glyphpath->vStemHintArray, | 1804 glyphpath->vStemHintArray, |
1709 glyphpath->hintMask, | 1805 glyphpath->hintMask, |
1710 glyphpath->hintOriginY, | 1806 glyphpath->hintOriginY, |
1711 FALSE ); | 1807 FALSE ); |
1712 | 1808 |
1713 glyphpath->currentCS.x = x3; /* pre-offset current point */ | 1809 glyphpath->currentCS.x = x3; /* pre-offset current point */ |
1714 glyphpath->currentCS.y = y3; | 1810 glyphpath->currentCS.y = y3; |
1715 } | 1811 } |
1716 | 1812 |
1717 | 1813 |
1718 FT_LOCAL_DEF( void ) | 1814 FT_LOCAL_DEF( void ) |
1719 cf2_glyphpath_closeOpenPath( CF2_GlyphPath glyphpath ) | 1815 cf2_glyphpath_closeOpenPath( CF2_GlyphPath glyphpath ) |
1720 { | 1816 { |
1721 if ( glyphpath->pathIsOpen ) | 1817 if ( glyphpath->pathIsOpen ) |
1722 { | 1818 { |
1723 FT_ASSERT( cf2_hintmap_isValid( &glyphpath->firstHintMap ) ); | 1819 /* |
| 1820 * A closing line in Character Space line is always generated below |
| 1821 * with `cf2_glyphPath_lineTo'. It may be ignored later if it turns |
| 1822 * out to be zero length in Device Space. |
| 1823 */ |
| 1824 glyphpath->pathIsClosing = TRUE; |
1724 | 1825 |
1725 /* since we need to apply an offset to the implicit lineto, we make */ | |
1726 /* it explicit here */ | |
1727 cf2_glyphpath_lineTo( glyphpath, | 1826 cf2_glyphpath_lineTo( glyphpath, |
1728 glyphpath->start.x, | 1827 glyphpath->start.x, |
1729 glyphpath->start.y ); | 1828 glyphpath->start.y ); |
1730 if (glyphpath->callbacks && glyphpath->callbacks->error && *glyphpath-
>callbacks->error) return; | |
1731 | 1829 |
1732 /* Draw previous element (the explicit LineTo we just created, */ | 1830 /* empty the final element from the queue and close the path */ |
1733 /* above) and connect it to the start point, but with the offset we */ | 1831 if ( glyphpath->elemIsQueued ) |
1734 /* saved from the first element. */ | 1832 cf2_glyphpath_pushPrevElem( glyphpath, |
1735 /* Use the saved HintMap, too. */ | 1833 &glyphpath->hintMap, |
1736 FT_ASSERT( glyphpath->elemIsQueued ); | 1834 &glyphpath->offsetStart0, |
1737 | 1835 glyphpath->offsetStart1, |
1738 cf2_glyphpath_pushPrevElem( glyphpath, | 1836 TRUE ); |
1739 &glyphpath->firstHintMap, | |
1740 &glyphpath->offsetStart0, | |
1741 glyphpath->offsetStart1, | |
1742 TRUE ); | |
1743 | 1837 |
1744 /* reset state machine */ | 1838 /* reset state machine */ |
1745 glyphpath->moveIsPending = TRUE; | 1839 glyphpath->moveIsPending = TRUE; |
1746 glyphpath->pathIsOpen = FALSE; | 1840 glyphpath->pathIsOpen = FALSE; |
| 1841 glyphpath->pathIsClosing = FALSE; |
1747 glyphpath->elemIsQueued = FALSE; | 1842 glyphpath->elemIsQueued = FALSE; |
1748 } | 1843 } |
1749 } | 1844 } |
1750 | 1845 |
1751 | 1846 |
1752 /* END */ | 1847 /* END */ |
OLD | NEW |