--- a/fontservices/freetypefontrasteriser/src/FTRAST2.CPP Wed Jun 09 11:40:52 2010 +0300
+++ b/fontservices/freetypefontrasteriser/src/FTRAST2.CPP Tue Aug 31 17:01:26 2010 +0300
@@ -159,6 +159,8 @@
COpenFont*& aFont, TOpenFontSpec& aActualFontSpec, TInt aMaxHeight);
TBool HasUnicodeCharacterL(TInt aFaceIndex, TInt aCode) const;
TAny* GetTrueTypeTable(TInt& aError, TInt aFaceIndex, TUint32 aTag, TInt* aLength);
+ TInt GetGlyphOutline(TInt aFaceIndex, TUint aCode,
+ TBool aIsGlyphId, TBool, TAny*& aOutline, TInt &aLength);
private:
static void SetGlyphMetrics(FT_GlyphSlot aGlyphSlot, TOpenFontGlyphData* aGlyphData);
@@ -182,7 +184,7 @@
};
NONSHARABLE_CLASS(CFreeTypeFont) : public COpenFont, public MOpenFontShapingExtension,
- public MOpenFontTrueTypeExtension
+ public MOpenFontTrueTypeExtension, public MOpenFontGlyphOutlineExtension
{
public:
static CFreeTypeFont* NewL(
@@ -216,6 +218,8 @@
TAny* GetTrueTypeTable(TInt& aError, TUint32 aTag, TInt* aLength);
TInt ReleaseTrueTypeTable(TAny* aTable);
TBool HasTrueTypeTable(TUint32 aTag);
+ TInt GetGlyphOutline(TUint aCode, TBool aIsGlyphId,
+ TBool aHinted, TAny*& aOutline, TInt &aLength);
inline TAny* operator new(TUint aSize, TAny* aBase) __NO_THROW;
inline TAny* operator new(TUint aSize) __NO_THROW;
@@ -1066,6 +1070,8 @@
aParam = static_cast<MOpenFontShapingExtension*>(this);
else if (aUid == KUidOpenFontTrueTypeExtension)
aParam = static_cast<MOpenFontTrueTypeExtension*>(this);
+ else if (aUid == KUidOpenFontGlyphOutlineExtension)
+ aParam = static_cast<MOpenFontGlyphOutlineExtension*>(this);
else
COpenFont::ExtendedInterface(aUid, aParam);
}
@@ -1169,7 +1175,18 @@
}
-
+TInt CFreeTypeFont::GetGlyphOutline(TUint aCode, TBool aIsGlyphId,
+ TBool aHinted, TAny*& aOutline, TInt& aLength)
+ {
+ CFreeTypeFontFile* file = (CFreeTypeFontFile*)File();
+ if (!file)
+ {
+ return KErrNotFound;
+ }
+ aHinted = EFalse; // to avoid 'unused' warning.
+
+ return file->GetGlyphOutline(FaceIndex(), aCode, aIsGlyphId, aHinted, aOutline, aLength);
+ }
TAny* CFreeTypeFont::GetTrueTypeTable(TInt& aError, TUint32 aTag, TInt* aLength)
{
@@ -1284,6 +1301,483 @@
iContext->TranslateAntiAliasedGlyphBitmap(aFace->Face()->glyph,aGlyphData);
}
+enum vg_commands {
+ VG_CMD_NONE = 0,
+ VG_CMD_MOVETO,
+ VG_CMD_LINETO,
+ VG_CMD_CONICTO,
+ VG_CMD_CUBICTO,
+ VG_CMD_CLOSE
+};
+
+
+enum contour_states
+ {
+ CONTOUR_STATE_NOT_STARTED = 0,
+ CONTOUR_STATE_START,
+ CONTOUR_STATE_CONIC,
+ CONTOUR_STATE_CUBIC1,
+ CONTOUR_STATE_CUBIC2,
+ CONTOUR_STATE_MAX
+ };
+
+static const TInt StateTransitions[CONTOUR_STATE_MAX][3] =
+ {
+ {CONTOUR_STATE_START, -1, -1},
+ {CONTOUR_STATE_START, CONTOUR_STATE_CONIC, CONTOUR_STATE_CUBIC1},
+ {CONTOUR_STATE_START, -1, -1},
+ {-1, -1, CONTOUR_STATE_CUBIC2},
+ {CONTOUR_STATE_START, -1, -1},
+ };
+
+static const TInt OutputCommands[CONTOUR_STATE_MAX][3] =
+ {
+ {VG_CMD_MOVETO, -1, -1},
+ {VG_CMD_LINETO, VG_CMD_NONE, VG_CMD_NONE},
+ {VG_CMD_CONICTO, -1, -1},
+ {-1, -1, VG_CMD_NONE},
+ {VG_CMD_CUBICTO, -1, -1},
+ };
+
+
+class MVGCommandProcessor
+ {
+public:
+ virtual TInt ProcessCommand(TInt8 cmd, FT_Vector &start, FT_Vector &end) = 0;
+ };
+
+class COutlineStringBuilder: public MVGCommandProcessor, public CBase
+ {
+private:
+ TUint8 *iBuffer;
+ TUint8 *iCur;
+ TInt iLen;
+
+
+public:
+ COutlineStringBuilder():iBuffer(0), iLen(2000)
+ {
+ iBuffer = (TUint8 *)User::Alloc(iLen);
+ iCur = iBuffer;
+ }
+
+ ~COutlineStringBuilder()
+ {
+ // ownership of the buffer is transferred to the caller.
+ }
+
+
+ TUint8 *GetBuffer(TInt &aLen)
+ {
+ aLen = iCur - iBuffer;
+ *iCur = '\0';
+ return iBuffer;
+ }
+
+ TInt AppendChar(char ch)
+ {
+ *(iCur++) = ch;
+ return 0;
+ }
+
+ TInt AppendFTPos(FT_Pos n)
+ {
+ int divisor = 1;
+ FT_Pos tmp = (n > 0) ? n : (-n);
+ while (tmp/divisor >= 10) {
+ divisor *= 10;
+ }
+
+ if (n < 0)
+ {
+ AppendChar('-');
+ }
+
+ for ( ; divisor >= 1; divisor /= 10)
+ {
+ AppendChar('0' + tmp/divisor);
+ tmp = tmp % divisor;
+ }
+
+ return 0;
+ }
+
+ TInt AppendCoord(FT_Pos x, FT_Pos y)
+ {
+ AppendFTPos(x);
+ AppendChar(',');
+ AppendFTPos(y);
+ AppendChar(' ');
+ return 0;
+ }
+
+ TInt
+ ProcessCommand(TInt8 cmd, FT_Vector &start, FT_Vector &end)
+ {
+ FT_Vector *st = &start;
+
+ if (iCur + 64 > iBuffer + iLen)
+ {
+ TUint distance = iCur - iBuffer;
+ iLen += 1000;
+ TUint8 *newBuffer = (TUint8 *)User::ReAlloc(iBuffer, iLen);
+ iBuffer = newBuffer;
+ iCur = iBuffer + distance;
+ }
+
+ if (VG_CMD_MOVETO == cmd)
+ {
+ AppendChar('M');
+ AppendCoord(start.x, start.y);
+ }
+ else if (VG_CMD_LINETO == cmd)
+ {
+ AppendChar('L');
+ AppendCoord(end.x, end.y);
+ }
+ else if (VG_CMD_CONICTO == cmd)
+ {
+ AppendChar('Q');
+ AppendCoord((st+1)->x, (st+1)->y);
+ AppendCoord(end.x, end.y);
+ }
+ else if (VG_CMD_CUBICTO == cmd)
+ {
+ AppendChar('Q');
+ AppendCoord((st+1)->x, (st+1)->y);
+ AppendCoord((st+2)->x, (st+2)->y);
+ AppendCoord(end.x, end.y);
+ }
+ else if (VG_CMD_CLOSE == cmd)
+ {
+ AppendChar('Z');
+ AppendChar(' ');
+ }
+
+ return KErrNone;
+ }
+ };
+
+
+class COutlineConvDirector: public CBase {
+private:
+ MVGCommandProcessor *iProcessor;
+ const FT_Outline *iOutline;
+ FT_Outline iNewOutline;
+
+
+private:
+ char
+ GetNextPointType(char aTag)
+ {
+ char ret = FT_CURVE_TAG(aTag);
+ if (FT_CURVE_TAG_ON == ret)
+ {
+ ret = 0;
+ }
+ else if (FT_CURVE_TAG_CONIC == ret)
+ {
+ ret = 1;
+ }
+ else if (FT_CURVE_TAG_CUBIC == ret)
+ {
+ ret = 2;
+ }
+ else
+ {
+ __ASSERT_DEBUG(0, User::Panic(_L("IncorrectState"), -1));
+ }
+ return ret;
+ }
+
+ TInt SwapPoints(const TInt i1, const TInt i2)
+ {
+ FT_Vector tmpVector = iOutline->points[i1];
+ char tmpTags = iOutline->tags[i1];
+ iOutline->points[i1] = iOutline->points[i2];
+ iOutline->tags[i1] = iOutline->tags[i2];
+ iOutline->points[i2] = tmpVector;
+ iOutline->tags[i2] = tmpTags;
+ return 0;
+ }
+
+ TInt MoveFirstOnPointToBeginning(const TInt aStartIndex, const TInt aEndIndex)
+ {
+ /* Contours of three or more points are valid, and single points
+ * (reference points, technically not contours) are also valid as
+ * special cases in TrueType.
+ */
+ char curTag = FT_CURVE_TAG(iOutline->tags[aStartIndex]);
+
+ // so a contour having only one point which is 'off' is invalid!
+ __ASSERT_DEBUG(!(aEndIndex - aStartIndex == 0 && FT_CURVE_TAG_ON != curTag),
+ User::Panic(_L("Contour consisting of 1 'off' point."), -1));
+
+ /* Contours consisting of two points are not a valid configuration. */
+ __ASSERT_DEBUG(aEndIndex - aStartIndex != 1,
+ User::Panic(_L("Contour consisting of two points."), -1));
+
+ if (FT_CURVE_TAG_ON == curTag)
+ {
+ return KErrNone;
+ }
+ TInt firstOnIndex = -1;
+ TInt i = 0;
+ for (i = 1+aStartIndex; i < aEndIndex; ++i)
+ {
+ if (FT_CURVE_TAG_ON == FT_CURVE_TAG(iOutline->tags[i]))
+ {
+ firstOnIndex = i;
+ break;
+ }
+ }
+ __ASSERT_DEBUG(-1 != firstOnIndex,
+ User::Panic(_L("Contour containing no 'on' point."), -1));
+
+ for (i = firstOnIndex-1; i >= aStartIndex; --i)
+ {
+ for (TInt j = i; j < aEndIndex; ++j)
+ {
+ SwapPoints(j, j+1);
+ }
+ }
+
+ return KErrNone;
+ }
+
+ TInt
+ ConvertContour(const TInt aStartIndex, const TInt aEndIndex)
+ {
+ /* Contours consisting of two
+ * points are not a valid configuration.
+ */
+ __ASSERT_DEBUG(aEndIndex - aStartIndex != 1,
+ User::Panic(_L("Contour consisting of two points."), -1));
+
+ TInt state = CONTOUR_STATE_NOT_STARTED, newState = -1;
+ TInt cmdStart = aStartIndex, cmdCur = 0, command = -1;
+
+ char ptype = GetNextPointType(iNewOutline.tags[cmdStart]);
+ __ASSERT_DEBUG(0 == ptype, User::Panic(_L("IncorrectState"), -1));
+ state = CONTOUR_STATE_START;
+ iProcessor->ProcessCommand(VG_CMD_MOVETO,
+ iNewOutline.points[aStartIndex], iNewOutline.points[aStartIndex]);
+
+
+ for (cmdCur = cmdStart + 1; cmdCur <= aEndIndex; ++cmdCur)
+ {
+ ptype = GetNextPointType(iNewOutline.tags[cmdCur]);
+ newState = StateTransitions[state][ptype];
+ __ASSERT_DEBUG(-1 != newState, User::Panic(_L("IncorrectState"), -1));
+ command = OutputCommands[state][ptype];
+ __ASSERT_DEBUG(-1 != command, User::Panic(_L("IncorrectState"), -1));
+
+ if (VG_CMD_NONE != command)
+ {
+ iProcessor->ProcessCommand(command,
+ iNewOutline.points[cmdStart], iNewOutline.points[cmdCur]);
+ cmdStart = cmdCur;
+ }
+ state = newState;
+ }
+
+ if (CONTOUR_STATE_CONIC == state)
+ {
+ iProcessor->ProcessCommand(VG_CMD_CONICTO, iNewOutline.points[cmdStart],
+ iNewOutline.points[aStartIndex]);
+ }
+ else if (CONTOUR_STATE_CUBIC2 == state)
+ {
+ iProcessor->ProcessCommand(VG_CMD_CUBICTO, iNewOutline.points[cmdStart],
+ iNewOutline.points[aStartIndex]);
+ }
+ iProcessor->ProcessCommand(VG_CMD_CLOSE,
+ iNewOutline.points[aStartIndex], iNewOutline.points[aStartIndex]);
+
+ return KErrNone;
+ }
+
+
+ TInt Preprocess()
+ {
+ /* two successive conic "off" points forces the rasterizer to
+ * create (during the scan-line conversion process exclusively) a
+ * virtual "on" point amidst them, at their exact middle.
+ */
+ char prevTag = FT_CURVE_TAG(iOutline->tags[0]), currentTag = 0;
+ TInt numNewPoints = 0;
+ TInt contourIndex = 0;
+
+ iNewOutline.contours = 0;
+ iNewOutline.n_contours = iOutline->n_contours;
+ iNewOutline.contours = (short *)
+ User::Alloc(iNewOutline.n_contours * sizeof(short));
+
+ if (0 == iOutline->contours[0])
+ {
+ iNewOutline.contours[0] = iOutline->contours[0]; // == 0
+ ++contourIndex;
+ }
+ for (TInt i = 1; i < iOutline->n_points; ++i)
+ {
+ currentTag = FT_CURVE_TAG(iOutline->tags[i]);
+ if (FT_CURVE_TAG_CONIC == prevTag && prevTag == currentTag)
+ {
+ numNewPoints++;
+ }
+ prevTag = currentTag;
+ if (i == iOutline->contours[contourIndex])
+ {
+ iNewOutline.contours[contourIndex] =
+ iOutline->contours[contourIndex] + numNewPoints;
+ ++contourIndex;
+ }
+ }
+
+
+ iNewOutline.n_points = iOutline->n_points + numNewPoints;
+ iNewOutline.flags = iOutline->flags;
+
+ iNewOutline.points = 0;
+ iNewOutline.tags = 0;
+
+ iNewOutline.points = (FT_Vector *)
+ User::Alloc(iNewOutline.n_points * sizeof(FT_Vector));
+
+ if (iNewOutline.contours)
+ {
+ iNewOutline.tags = (char *)
+ User::Alloc(iNewOutline.n_points * sizeof(char));
+ }
+
+ // copy the 'points' and 'tags' array, inserting new points
+ // when necessary.
+ TInt oldIndex = 0, newIndex = 0;
+ for ( ; oldIndex < iOutline->n_points; ++oldIndex)
+ {
+ char oldTag = FT_CURVE_TAG(iOutline->tags[oldIndex]);
+ iNewOutline.points[newIndex] = iOutline->points[oldIndex];
+ iNewOutline.tags[newIndex] = iOutline->tags[oldIndex];
+
+ if (FT_CURVE_TAG_CONIC == oldTag &&
+ oldIndex + 1 < iOutline->n_points)
+ {
+ char nextTag = FT_CURVE_TAG(iOutline->tags[oldIndex+1]);
+ // insert a new 'on' point when there are two consecutive
+ // 'conic off' points.
+ if (oldTag == nextTag)
+ {
+ newIndex++;
+ FT_Vector *cur = &(iOutline->points[oldIndex]);
+ FT_Vector *next = &(iOutline->points[oldIndex + 1]);
+ iNewOutline.points[newIndex].x = (cur->x + next->x)/2;
+ iNewOutline.points[newIndex].y = (cur->y + next->y)/2;
+ iNewOutline.tags[newIndex] = FT_CURVE_TAG_ON;
+ }
+ }
+ newIndex++;
+ }
+
+ return 0;
+ }
+
+public:
+ COutlineConvDirector():iProcessor(0), iOutline(0)
+ {
+ // a null constructor
+ iNewOutline.contours = 0;
+ iNewOutline.tags = 0;
+ iNewOutline.points = 0;
+ }
+
+ ~COutlineConvDirector()
+ {
+ User::Free(iNewOutline.contours);
+ User::Free(iNewOutline.points);
+ User::Free(iNewOutline.tags);
+ }
+
+ TInt
+ ConvertOutline(const FT_Outline &aOutline, MVGCommandProcessor *aProcessor)
+ {
+ if (0 != aOutline.n_contours)
+ {
+ iProcessor = aProcessor;
+ iOutline = &aOutline;
+
+ MoveFirstOnPointToBeginning(0, iOutline->contours[0]);
+ TInt i = 0;
+ for (i = 1; i < iOutline->n_contours; ++i)
+ {
+ MoveFirstOnPointToBeginning(iOutline->contours[i-1]+1, iOutline->contours[i]);
+ }
+
+ Preprocess();
+
+ ConvertContour(0, iNewOutline.contours[0]);
+ for (i = 1; i < iNewOutline.n_contours; ++i)
+ {
+ ConvertContour(iNewOutline.contours[i-1]+1, iNewOutline.contours[i]);
+ }
+ }
+ else
+ {
+ RDebug::Printf("Zero contour in outline: missing glyph.");
+ FT_Vector dummyVector;
+ aProcessor->ProcessCommand(VG_CMD_CLOSE, dummyVector, dummyVector);
+ }
+ return KErrNone;
+ }
+};
+
+
+TInt CFreeTypeFontFile::GetGlyphOutline(TInt aFaceIndex, TUint aCode,
+ TBool aIsGlyphId, TBool, TAny*& aOutline, TInt &aLength)
+ {
+ // The 4th param 'aHinted' is ignored in this reference implementation.
+ // Need to add it back and implement accordingly if freetype is used in
+ // production code.
+ CFaceListItem *faceList = LoadFaceL(aFaceIndex);
+ FT_Face face = faceList->Face();
+ TUint code = aCode;
+ if (!aIsGlyphId)
+ {
+ code = FT_Get_Char_Index(face, aCode);
+ if (0 == code)
+ {
+ return KErrNotFound;
+ }
+ }
+
+ TInt ret = FT_Load_Glyph(face, code,
+ FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM);
+
+ if (0 != ret)
+ {
+ return KErrUnknown;
+ }
+
+ COutlineStringBuilder strBuilder;
+ if (0 != strBuilder.GetBuffer(aLength))
+ {
+ FT_Outline &outline = face->glyph->outline;
+
+ COutlineConvDirector d;
+ d.ConvertOutline(outline, &strBuilder);
+ }
+ else
+ {
+ return KErrNoMemory;
+ }
+
+ TUint8 *buf = strBuilder.GetBuffer(aLength);
+ RDebug::Printf("length of buffer is %d\n", aLength);
+ RDebug::Printf("Outline for glyph %d: \n", aCode);
+ RDebug::Printf("%s", buf);
+ aOutline = (TAny*)buf;
+
+ return KErrNone;
+ }
TAny* CFreeTypeFontFile::GetTrueTypeTable(TInt& aError, TInt aFaceIndex,
TUint32 aTag, TInt* aLength)
{