Index: third_party/freetype/src/cff/cf2hints.c |
diff --git a/core/src/fxge/fx_freetype/fxft2.5.01/src/cff/cf2hints.c b/third_party/freetype/src/cff/cf2hints.c |
similarity index 88% |
rename from core/src/fxge/fx_freetype/fxft2.5.01/src/cff/cf2hints.c |
rename to third_party/freetype/src/cff/cf2hints.c |
index 70926299f3f1e5918a9becc855d58dbb34000775..040d193f3136b1a99628072c10a2c99158c04ab0 100644 |
--- a/core/src/fxge/fx_freetype/fxft2.5.01/src/cff/cf2hints.c |
+++ b/third_party/freetype/src/cff/cf2hints.c |
@@ -4,7 +4,7 @@ |
/* */ |
/* Adobe's code for handling CFF hints (body). */ |
/* */ |
-/* Copyright 2007-2013 Adobe Systems Incorporated. */ |
+/* Copyright 2007-2014 Adobe Systems Incorporated. */ |
/* */ |
/* This software, and all works of authorship, whether in source or */ |
/* object code form as indicated by the copyright notice(s) included */ |
@@ -37,7 +37,7 @@ |
#include "cf2ft.h" |
-#include "../../include/freetype/internal/ftdebug.h" |
+#include FT_INTERNAL_DEBUG_H |
#include "cf2glue.h" |
#include "cf2font.h" |
@@ -304,9 +304,6 @@ |
cf2_hintmap_map( CF2_HintMap hintmap, |
CF2_Fixed csCoord ) |
{ |
- FT_ASSERT( hintmap->isValid ); /* must call Build before Map */ |
- FT_ASSERT( hintmap->lastIndex < CF2_MAX_HINT_EDGES ); |
- |
if ( hintmap->count == 0 || ! hintmap->hinted ) |
{ |
/* there are no hints; use uniform scale and zero offset */ |
@@ -317,6 +314,7 @@ |
/* start linear search from last hit */ |
CF2_UInt i = hintmap->lastIndex; |
+ FT_ASSERT( hintmap->lastIndex < CF2_MAX_HINT_EDGES ); |
/* search up */ |
while ( i < hintmap->count - 1 && |
@@ -596,22 +594,31 @@ |
indexInsert = 0; |
for ( ; indexInsert < hintmap->count; indexInsert++ ) |
{ |
- if ( hintmap->edge[indexInsert].csCoord > firstHintEdge->csCoord ) |
+ if ( hintmap->edge[indexInsert].csCoord >= firstHintEdge->csCoord ) |
break; |
} |
/* |
- * Discard any hints that overlap in character space. Most often, |
- * this is while building the initial map, but in theory, it can also |
- * occur because of darkening. |
+ * Discard any hints that overlap in character space. Most often, this |
+ * is while building the initial map, where captured hints from all |
+ * zones are combined. Define overlap to include hints that `touch' |
+ * (overlap zero). Hiragino Sans/Gothic fonts have numerous hints that |
+ * touch. Some fonts have non-ideographic glyphs that overlap our |
+ * synthetic hints. |
+ * |
+ * Overlap also occurs when darkening stem hints that are close. |
* |
*/ |
if ( indexInsert < hintmap->count ) |
{ |
- /* we are inserting before an existing edge: */ |
+ /* we are inserting before an existing edge: */ |
+ /* verify that an existing edge is not the same */ |
+ if ( hintmap->edge[indexInsert].csCoord == firstHintEdge->csCoord ) |
+ return; /* ignore overlapping stem hint */ |
+ |
/* verify that a new pair does not straddle the next edge */ |
- if ( isPair && |
- hintmap->edge[indexInsert].csCoord < secondHintEdge->csCoord ) |
+ if ( isPair && |
+ hintmap->edge[indexInsert].csCoord <= secondHintEdge->csCoord ) |
return; /* ignore overlapping stem hint */ |
/* verify that we are not inserting between paired edges */ |
@@ -645,8 +652,27 @@ |
firstHintEdge->csCoord ); |
} |
- /* discard any hints that overlap in device space; this can occur */ |
- /* because locked hints have been moved to align with blue zones */ |
+ /* |
+ * Discard any hints that overlap in device space; this can occur |
+ * because locked hints have been moved to align with blue zones. |
+ * |
+ * TODO: Although we might correct this later during adjustment, we |
+ * don't currently have a way to delete a conflicting hint once it has |
+ * been inserted. See v2.030 MinionPro-Regular, 12 ppem darkened, |
+ * initial hint map for second path, glyph 945 (the perispomeni (tilde) |
+ * in U+1F6E, Greek omega with psili and perispomeni). Darkening is |
+ * 25. Pair 667,747 initially conflicts in design space with top edge |
+ * 660. This is because 667 maps to 7.87, and the top edge was |
+ * captured by a zone at 8.0. The pair is later successfully inserted |
+ * in a zone without the top edge. In this zone it is adjusted to 8.0, |
+ * and no longer conflicts with the top edge in design space. This |
+ * means it can be included in yet a later zone which does have the top |
+ * edge hint. This produces a small mismatch between the first and |
+ * last points of this path, even though the hint masks are the same. |
+ * The density map difference is tiny (1/256). |
+ * |
+ */ |
+ |
if ( indexInsert > 0 ) |
{ |
/* we are inserting after an existing edge */ |
@@ -725,7 +751,6 @@ |
FT_Bool initialMap ) |
{ |
FT_Byte* maskPtr; |
- FT_Byte* maskEndPtr; // add by Xiaochuan_Liu |
CF2_Font font = hintmap->font; |
CF2_HintMaskRec tempHintMask; |
@@ -754,6 +779,8 @@ |
cf2_hintmask_setAll( hintMask, |
cf2_arrstack_size( hStemHintArray ) + |
cf2_arrstack_size( vStemHintArray ) ); |
+ if ( !cf2_hintmask_isValid( hintMask ) ) |
+ return; /* too many stem hints */ |
} |
/* begin by clearing the map */ |
@@ -763,13 +790,14 @@ |
/* make a copy of the hint mask so we can modify it */ |
tempHintMask = *hintMask; |
maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask ); |
- maskEndPtr = maskPtr + ( CF2_MAX_HINTS + 7 ) / 8; |
- |
/* use the hStem hints only, which are first in the mask */ |
- /* TODO: compare this to cffhintmaskGetBitCount */ |
bitCount = cf2_arrstack_size( hStemHintArray ); |
+ /* Defense-in-depth. Should never return here. */ |
+ if ( bitCount > hintMask->bitCount ) |
+ return; |
+ |
/* synthetic embox hints get highest priority */ |
if ( font->blues.doEmBoxHints ) |
{ |
@@ -830,11 +858,6 @@ |
{ |
/* move to next mask byte */ |
maskPtr++; |
- if (maskPtr >= maskEndPtr) |
- { |
- break; |
- } |
- |
maskByte = 0x80; |
} |
else |
@@ -895,7 +918,6 @@ |
/* insert remaining hints */ |
maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask ); |
- maskEndPtr = maskPtr + ( CF2_MAX_HINTS + 7 ) / 8; |
for ( i = 0, maskByte = 0x80; i < bitCount; i++ ) |
{ |
@@ -926,10 +948,6 @@ |
{ |
/* move to next mask byte */ |
maskPtr++; |
- if (maskPtr >= maskEndPtr) |
- { |
- break; |
- } |
maskByte = 0x80; |
} |
else |
@@ -1048,6 +1066,7 @@ |
glyphpath->moveIsPending = TRUE; |
glyphpath->pathIsOpen = FALSE; |
+ glyphpath->pathIsClosing = FALSE; |
glyphpath->elemIsQueued = FALSE; |
} |
@@ -1188,12 +1207,16 @@ |
/* |
* Push the cached element (glyphpath->prevElem*) to the outline |
* consumer. When a darkening offset is used, the end point of the |
- * cached element may be adjusted to an intersection point or it may be |
- * connected by a line to the current element. This calculation must |
- * use a HintMap that was valid at the time the element was saved. For |
- * the first point in a subpath, that is a saved HintMap. For most |
- * elements, it just means the caller has delayed building a HintMap |
- * from the current HintMask. |
+ * cached element may be adjusted to an intersection point or we may |
+ * synthesize a connecting line to the current element. If we are |
+ * closing a subpath, we may also generate a connecting line to the start |
+ * point. |
+ * |
+ * This is where Character Space (CS) is converted to Device Space (DS) |
+ * using a hint map. This calculation must use a HintMap that was valid |
+ * at the time the element was saved. For the first point in a subpath, |
+ * that is a saved HintMap. For most elements, it just means the caller |
+ * has delayed building a HintMap from the current HintMask. |
* |
* Transform each point with outerTransform and call the outline |
* callbacks. This is a general 3x3 transform: |
@@ -1263,16 +1286,32 @@ |
params.op = CF2_PathOpLineTo; |
/* note: pt2 and pt3 are unused */ |
- cf2_glyphpath_hintPoint( glyphpath, |
- hintmap, |
- ¶ms.pt1, |
- glyphpath->prevElemP1.x, |
- glyphpath->prevElemP1.y ); |
- glyphpath->callbacks->lineTo( glyphpath->callbacks, ¶ms ); |
+ if ( close ) |
+ { |
+ /* use first hint map if closing */ |
+ cf2_glyphpath_hintPoint( glyphpath, |
+ &glyphpath->firstHintMap, |
+ ¶ms.pt1, |
+ glyphpath->prevElemP1.x, |
+ glyphpath->prevElemP1.y ); |
+ } |
+ else |
+ { |
+ cf2_glyphpath_hintPoint( glyphpath, |
+ hintmap, |
+ ¶ms.pt1, |
+ glyphpath->prevElemP1.x, |
+ glyphpath->prevElemP1.y ); |
+ } |
- glyphpath->currentDS = params.pt1; |
+ /* output only non-zero length lines */ |
+ if ( params.pt0.x != params.pt1.x || params.pt0.y != params.pt1.y ) |
+ { |
+ glyphpath->callbacks->lineTo( glyphpath->callbacks, ¶ms ); |
+ glyphpath->currentDS = params.pt1; |
+ } |
break; |
case CF2_PathOpCubeTo: |
@@ -1309,11 +1348,24 @@ |
/* note: at the end of a subpath, we might do both, so use `nextP0' */ |
/* before we change it, below */ |
- cf2_glyphpath_hintPoint( glyphpath, |
- hintmap, |
- ¶ms.pt1, |
- nextP0->x, |
- nextP0->y ); |
+ if ( close ) |
+ { |
+ /* if we are closing the subpath, then nextP0 is in the first */ |
+ /* hint zone */ |
+ cf2_glyphpath_hintPoint( glyphpath, |
+ &glyphpath->firstHintMap, |
+ ¶ms.pt1, |
+ nextP0->x, |
+ nextP0->y ); |
+ } |
+ else |
+ { |
+ cf2_glyphpath_hintPoint( glyphpath, |
+ hintmap, |
+ ¶ms.pt1, |
+ nextP0->x, |
+ nextP0->y ); |
+ } |
if ( params.pt1.x != glyphpath->currentDS.x || |
params.pt1.y != glyphpath->currentDS.y ) |
@@ -1509,7 +1561,7 @@ |
{ |
/* -y */ |
*x = -glyphpath->xOffset; |
- *y = glyphpath->xOffset; |
+ *y = glyphpath->yOffset; |
} |
else |
{ |
@@ -1524,6 +1576,16 @@ |
} |
+ /* |
+ * The functions cf2_glyphpath_{moveTo,lineTo,curveTo,closeOpenPath} are |
+ * called by the interpreter with Character Space (CS) coordinates. Each |
+ * path element is placed into a queue of length one to await the |
+ * calculation of the following element. At that time, the darkening |
+ * offset of the following element is known and joins can be computed, |
+ * including possible modification of this element, before mapping to |
+ * Device Space (DS) and passing it on to the outline consumer. |
+ * |
+ */ |
FT_LOCAL_DEF( void ) |
cf2_glyphpath_moveTo( CF2_GlyphPath glyphpath, |
CF2_Fixed x, |
@@ -1561,10 +1623,46 @@ |
{ |
CF2_Fixed xOffset, yOffset; |
FT_Vector P0, P1; |
+ FT_Bool newHintMap; |
+ /* |
+ * New hints will be applied after cf2_glyphpath_pushPrevElem has run. |
+ * In case this is a synthesized closing line, any new hints should be |
+ * delayed until this path is closed (`cf2_hintmask_isNew' will be |
+ * called again before the next line or curve). |
+ */ |
- /* can't compute offset of zero length line, so ignore them */ |
- if ( glyphpath->currentCS.x == x && glyphpath->currentCS.y == y ) |
+ /* true if new hint map not on close */ |
+ newHintMap = cf2_hintmask_isNew( glyphpath->hintMask ) && |
+ !glyphpath->pathIsClosing; |
+ |
+ /* |
+ * Zero-length lines may occur in the charstring. Because we cannot |
+ * compute darkening offsets or intersections from zero-length lines, |
+ * it is best to remove them and avoid artifacts. However, zero-length |
+ * lines in CS at the start of a new hint map can generate non-zero |
+ * lines in DS due to hint substitution. We detect a change in hint |
+ * map here and pass those zero-length lines along. |
+ */ |
+ |
+ /* |
+ * Note: Find explicitly closed paths here with a conditional |
+ * breakpoint using |
+ * |
+ * !gp->pathIsClosing && gp->start.x == x && gp->start.y == y |
+ * |
+ */ |
+ |
+ if ( glyphpath->currentCS.x == x && |
+ glyphpath->currentCS.y == y && |
+ !newHintMap ) |
+ /* |
+ * Ignore zero-length lines in CS where the hint map is the same |
+ * because the line in DS will also be zero length. |
+ * |
+ * Ignore zero-length lines when we synthesize a closing line because |
+ * the close will be handled in cf2_glyphPath_pushPrevElem. |
+ */ |
return; |
cf2_glyphpath_computeOffset( glyphpath, |
@@ -1585,7 +1683,6 @@ |
{ |
/* emit offset 1st point as MoveTo */ |
cf2_glyphpath_pushMove( glyphpath, P0 ); |
- if (glyphpath->callbacks && glyphpath->callbacks->error && *glyphpath->callbacks->error) return; |
glyphpath->moveIsPending = FALSE; /* adjust state machine */ |
glyphpath->pathIsOpen = TRUE; |
@@ -1595,14 +1692,14 @@ |
if ( glyphpath->elemIsQueued ) |
{ |
- FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) ); |
+ FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) || |
+ glyphpath->hintMap.count == 0 ); |
cf2_glyphpath_pushPrevElem( glyphpath, |
&glyphpath->hintMap, |
&P0, |
P1, |
FALSE ); |
- if (glyphpath->callbacks && glyphpath->callbacks->error && *glyphpath->callbacks->error) return; |
} |
/* queue the current element with offset points */ |
@@ -1612,7 +1709,7 @@ |
glyphpath->prevElemP1 = P1; |
/* update current map */ |
- if ( cf2_hintmask_isNew( glyphpath->hintMask ) ) |
+ if ( newHintMap ) |
cf2_hintmap_build( &glyphpath->hintMap, |
glyphpath->hStemHintArray, |
glyphpath->vStemHintArray, |
@@ -1673,7 +1770,6 @@ |
{ |
/* emit offset 1st point as MoveTo */ |
cf2_glyphpath_pushMove( glyphpath, P0 ); |
- if (glyphpath->callbacks && glyphpath->callbacks->error && *glyphpath->callbacks->error) return; |
glyphpath->moveIsPending = FALSE; |
glyphpath->pathIsOpen = TRUE; |
@@ -1683,14 +1779,14 @@ |
if ( glyphpath->elemIsQueued ) |
{ |
- FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) ); |
+ FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) || |
+ glyphpath->hintMap.count == 0 ); |
cf2_glyphpath_pushPrevElem( glyphpath, |
&glyphpath->hintMap, |
&P0, |
P1, |
FALSE ); |
- if (glyphpath->callbacks && glyphpath->callbacks->error && *glyphpath->callbacks->error) return; |
} |
/* queue the current element with offset points */ |
@@ -1720,30 +1816,29 @@ |
{ |
if ( glyphpath->pathIsOpen ) |
{ |
- FT_ASSERT( cf2_hintmap_isValid( &glyphpath->firstHintMap ) ); |
+ /* |
+ * A closing line in Character Space line is always generated below |
+ * with `cf2_glyphPath_lineTo'. It may be ignored later if it turns |
+ * out to be zero length in Device Space. |
+ */ |
+ glyphpath->pathIsClosing = TRUE; |
- /* since we need to apply an offset to the implicit lineto, we make */ |
- /* it explicit here */ |
cf2_glyphpath_lineTo( glyphpath, |
glyphpath->start.x, |
glyphpath->start.y ); |
- if (glyphpath->callbacks && glyphpath->callbacks->error && *glyphpath->callbacks->error) return; |
- |
- /* Draw previous element (the explicit LineTo we just created, */ |
- /* above) and connect it to the start point, but with the offset we */ |
- /* saved from the first element. */ |
- /* Use the saved HintMap, too. */ |
- FT_ASSERT( glyphpath->elemIsQueued ); |
- cf2_glyphpath_pushPrevElem( glyphpath, |
- &glyphpath->firstHintMap, |
- &glyphpath->offsetStart0, |
- glyphpath->offsetStart1, |
- TRUE ); |
+ /* empty the final element from the queue and close the path */ |
+ if ( glyphpath->elemIsQueued ) |
+ cf2_glyphpath_pushPrevElem( glyphpath, |
+ &glyphpath->hintMap, |
+ &glyphpath->offsetStart0, |
+ glyphpath->offsetStart1, |
+ TRUE ); |
/* reset state machine */ |
glyphpath->moveIsPending = TRUE; |
glyphpath->pathIsOpen = FALSE; |
+ glyphpath->pathIsClosing = FALSE; |
glyphpath->elemIsQueued = FALSE; |
} |
} |