unsafe internal static void CopyGlyphBitmap(FontGlyph fontGlyph) { FT_Bitmap* ftBmp = (FT_Bitmap*)fontGlyph.glyphMatrix.bitmap; //image is 8 bits grayscale int h = ftBmp->rows; int w = ftBmp->width; int stride = ftBmp->pitch; int size = stride * h; //copy it to array //bmp glyph is bottom up //so .. invert it... byte[] buff = new byte[size]; //------------------------------------------------ byte* currentSrc = ftBmp->buffer; int srcpos = size; int targetpos = 0; for (int r = 1; r <= h; ++r) { srcpos -= stride; currentSrc = ftBmp->buffer + srcpos; for (int c = 0; c < stride; ++c) { buff[targetpos] = *(currentSrc + c); targetpos++; } } ////------------------------------------------------ //IntPtr bmpPtr = (IntPtr)ftBmp->buffer; //Marshal.Copy((IntPtr)ftBmp->buffer, buff, 0, size); ////------------------------------------------------ //------------------------------------------------ fontGlyph.glyImgBuffer8 = buff; //convert to 32bpp //make gray value as alpha channel color value ActualImage actualImage = new ActualImage(w, h, Agg.PixelFormat.ARGB32); byte[] newBmp32Buffer = ActualImage.GetBuffer(actualImage); int src_p = 0; int target_p = 0; for (int r = 0; r < h; ++r) { for (int c = 0; c < w; ++c) { byte srcColor = buff[src_p + c]; //expand to 4 channel newBmp32Buffer[target_p] = 0; newBmp32Buffer[target_p + 1] = 0; newBmp32Buffer[target_p + 2] = 0; newBmp32Buffer[target_p + 3] = srcColor; //A target_p += 4; } src_p += stride; } fontGlyph.glyphImage32 = actualImage; }
public override FontGlyph GetGlyphByIndex(ushort glyphIndex) { //1. FontGlyph fontGlyph = new FontGlyph(); fontGlyph.flattenVxs = GetGlyphVxs(glyphIndex); fontGlyph.horiz_adv_x = _typeFace.GetHAdvanceWidthFromGlyphIndex(glyphIndex); return(fontGlyph); }
public void ReadSVG(string svgContent) { int startIndex = 0; String fontElementString = GetSubString(svgContent, "<font", ">", ref startIndex); fontId = GetStringValue(fontElementString, "id"); GetIntValue(fontElementString, "horiz-adv-x", out horiz_adv_x); String fontFaceString = GetSubString(svgContent, "<font-face", "/>", ref startIndex); fontFamily = GetStringValue(fontFaceString, "font-family"); GetIntValue(fontFaceString, "font-weight", out font_weight); font_stretch = GetStringValue(fontFaceString, "font-stretch"); GetIntValue(fontFaceString, "units-per-em", out unitsPerEm); panose_1 = new Panos_1(GetStringValue(fontFaceString, "panose-1")); GetIntValue(fontFaceString, "ascent", out ascent); GetIntValue(fontFaceString, "descent", out descent); GetIntValue(fontFaceString, "x-height", out x_height); GetIntValue(fontFaceString, "cap-height", out cap_height); String bboxString = GetStringValue(fontFaceString, "bbox"); String[] valuesString = bboxString.Split(' '); int.TryParse(valuesString[0], out boundingBox.Left); int.TryParse(valuesString[1], out boundingBox.Bottom); int.TryParse(valuesString[2], out boundingBox.Right); int.TryParse(valuesString[3], out boundingBox.Top); GetIntValue(fontFaceString, "underline-thickness", out underline_thickness); GetIntValue(fontFaceString, "underline-position", out underline_position); unicode_range = GetStringValue(fontFaceString, "unicode-range"); String missingGlyphString = GetSubString(svgContent, "<missing-glyph", "/>", ref startIndex); missingGlyph = CreateGlyphFromSVGGlyphData(missingGlyphString); String nextGlyphString = GetSubString(svgContent, "<glyph", "/>", ref startIndex); while (nextGlyphString != null) { // get the data and put it in the glyph dictionary FontGlyph newGlyph = CreateGlyphFromSVGGlyphData(nextGlyphString); if (newGlyph.unicode > 0) { originalGlyphs.Add(newGlyph.unicode, newGlyph); } nextGlyphString = GetSubString(svgContent, "<glyph", "/>", ref startIndex); } }
public FontGlyph GetGlyph(char c) { FontGlyph glyph; if (!cachedGlyphs.TryGetValue(c, out glyph)) { //create font glyph for this font size var originalGlyph = fontface.GetGlyphForCharacter(c); VertexStore characterGlyph = new VertexStore(); scaleTx.TransformToVxs(originalGlyph.originalVxs, characterGlyph); glyph = new FontGlyph(); glyph.horiz_adv_x = originalGlyph.horiz_adv_x; glyph.originalVxs = characterGlyph; //then flatten it glyph.flattenVxs = curveFlattner.MakeVxs(characterGlyph, new VertexStore()); cachedGlyphs.Add(c, glyph); } return(glyph); }
public FontGlyph GetGlyphByIndex(uint glyphIndex) { FontGlyph glyph; //temp if (!cachedGlyphsByIndex.TryGetValue(glyphIndex, out glyph)) { //create font glyph for this font size FontGlyph originalGlyph = fontface.GetGlyphByIndex((int)glyphIndex); VertexStore characterGlyph = new VertexStore(); scaleTx.TransformToVxs(originalGlyph.originalVxs, characterGlyph); glyph = new FontGlyph(); glyph.originalVxs = characterGlyph; //then flatten it glyph.flattenVxs = curveFlattner.MakeVxs(characterGlyph, new VertexStore()); glyph.horiz_adv_x = originalGlyph.horiz_adv_x; cachedGlyphsByIndex.Add(glyphIndex, glyph); } return(glyph); }
FontGlyph CreateGlyphFromSVGGlyphData(string svgGlyphData) { FontGlyph newGlyph = new FontGlyph(); if (!GetIntValue(svgGlyphData, "horiz-adv-x", out newGlyph.horiz_adv_x)) { newGlyph.horiz_adv_x = horiz_adv_x; } newGlyph.glyphName = GetStringValue(svgGlyphData, "glyph-name"); String unicodeString = GetStringValue(svgGlyphData, "unicode"); if (unicodeString != null) { if (unicodeString.Length == 1) { newGlyph.unicode = (int)unicodeString[0]; } else { if (unicodeString.Split(';').Length > 1 && unicodeString.Split(';')[1].Length > 0) { throw new NotImplementedException("We do not currently support glyphs longer than one character. You need to wirite the seach so that it will find them if you want to support this"); } if (int.TryParse(unicodeString, NumberStyles.Number, null, out newGlyph.unicode) == false) { // see if it is a unicode String hexNumber = GetSubString(unicodeString, "&#x", ";"); int.TryParse(hexNumber, NumberStyles.HexNumber, null, out newGlyph.unicode); } } } String dString = GetStringValue(svgGlyphData, "d"); int parseIndex = 0; int polyStartVertexSourceIndex = 0; Vector2 lastXY = new Vector2(0, 0); double px = 0; double py = 0; PathWriter gyphPath = new PathWriter(); newGlyph.originalVxs = gyphPath.Vxs; if (dString == null || dString.Length == 0) { return(newGlyph); } while (parseIndex < dString.Length) { char command = dString[parseIndex]; switch (command) { case 'M': { parseIndex++; // svg fonts are stored cw and agg expects its shapes to be ccw. cw shapes are holes. // so we store the position of the start of this polygon so we can flip it when we colse it. polyStartVertexSourceIndex = gyphPath.Count; px = GetNextNumber(dString, ref parseIndex); py = GetNextNumber(dString, ref parseIndex); gyphPath.MoveTo(px, py); } break; case 'v': { parseIndex++; py = GetNextNumber(dString, ref parseIndex); gyphPath.VerticalLineToRel(py); } break; case 'V': { parseIndex++; py = GetNextNumber(dString, ref parseIndex); gyphPath.VerticalLineTo(py); } break; case 'h': { parseIndex++; px = GetNextNumber(dString, ref parseIndex); gyphPath.HorizontalLineToRel(px); } break; case 'H': { parseIndex++; px = GetNextNumber(dString, ref parseIndex); gyphPath.HorizontalLineTo(px); } break; case 'l': { parseIndex++; px = GetNextNumber(dString, ref parseIndex); py = GetNextNumber(dString, ref parseIndex); gyphPath.LineToRel(px, py); } break; case 'L': { parseIndex++; px = GetNextNumber(dString, ref parseIndex); py = GetNextNumber(dString, ref parseIndex); gyphPath.LineTo(px, py); } break; case 'q': { //Curve3 parseIndex++; double p2x = GetNextNumber(dString, ref parseIndex); double p2y = GetNextNumber(dString, ref parseIndex); px = GetNextNumber(dString, ref parseIndex); py = GetNextNumber(dString, ref parseIndex); gyphPath.Curve3Rel(p2x, p2y, px, py); } break; case 'Q': { //Curve3 parseIndex++; double p2x = GetNextNumber(dString, ref parseIndex); double p2y = GetNextNumber(dString, ref parseIndex); px = GetNextNumber(dString, ref parseIndex); py = GetNextNumber(dString, ref parseIndex); gyphPath.Curve3(p2x, p2y, px, py); } break; case 't': { //svg smooth curve3 parseIndex++; px = GetNextNumber(dString, ref parseIndex); py = GetNextNumber(dString, ref parseIndex); gyphPath.SmoothCurve3Rel(px, py); } break; case 'T': { parseIndex++; px = GetNextNumber(dString, ref parseIndex); py = GetNextNumber(dString, ref parseIndex); gyphPath.SmoothCurve3(px, py); } break; case 'z': case 'Z': { parseIndex++; //curXY = lastXY; // value not used this is to remove an error. //newGlyph.glyphData.ClosePathStorage(); gyphPath.CloseFigure(); // svg fonts are stored cw and agg expects its shapes to be ccw. cw shapes are holes. // We stored the position of the start of this polygon, no we flip it as we close it. //newGlyph.glyphData.InvertPolygon(polyStartVertexSourceIndex); // VertexHelper.InvertPolygon(gyphPath.Vxs, polyStartVertexSourceIndex); } break; case ' ': case '\n': // some white space we need to skip case '\r': { parseIndex++; } break; default: throw new NotImplementedException("unrecognized d command '" + command + "'."); } } return(newGlyph); }
public override FontGlyph GetGlyphByIndex(uint glyphIndex) { //1. FontGlyph fontGlyph = new FontGlyph(); fontGlyph.flattenVxs = GetGlyphVxs(glyphIndex); fontGlyph.horiz_adv_x = typeFace.GetAdvanceWidthFromGlyphIndex((int)glyphIndex); return fontGlyph; }
//--------------------------------------------------------------------------- public static GlyphImage BuildMsdfFontImage(FontGlyph fontGlyph) { return(NativeFontGlyphBuilder.BuildMsdfFontImage((NativeFontGlyph)fontGlyph)); }
void BuildOutlineGlyph(FontGlyph fontGlyph, int pxsize) { NativeFontGlyphBuilder.BuildGlyphOutline(fontGlyph); Agg.VertexStore vxs = new Agg.VertexStore(); NativeFontGlyphBuilder.FlattenVxs(fontGlyph.originalVxs, vxs); fontGlyph.flattenVxs = vxs; }
void BuildBitmapGlyph(FontGlyph fontGlyph, int pxsize) { NativeFontGlyphBuilder.CopyGlyphBitmap(fontGlyph); }
internal FontGlyph ReloadGlyphFromChar(char unicodeChar, int pixelSize) { if (currentFacePixelSize != pixelSize) { currentFacePixelSize = pixelSize; NativeMyFontsLib.MyFtSetPixelSizes(this.ftFaceHandle, pixelSize); } //-------------------------------------------------- var fontGlyph = new FontGlyph(); NativeMyFontsLib.MyFtLoadChar(ftFaceHandle, unicodeChar, out fontGlyph.glyphMatrix); BuildOutlineGlyph(fontGlyph, pixelSize); return fontGlyph; }
unsafe internal static void BuildGlyphOutline(FontGlyph fontGlyph) { FT_Outline outline = (*(FT_Outline*)fontGlyph.glyphMatrix.outline); //outline version //------------------------------ int npoints = outline.n_points; int startContour = 0; int cpoint_index = 0; int todoContourCount = outline.n_contours; PixelFarm.Agg.VertexSource.PathWriter ps = new Agg.VertexSource.PathWriter(); fontGlyph.originalVxs = ps.Vxs; int controlPointCount = 0; while (todoContourCount > 0) { int nextContour = outline.contours[startContour] + 1; bool isFirstPoint = true; FtVec2 secondControlPoint = new FtVec2(); FtVec2 thirdControlPoint = new FtVec2(); bool justFromCurveMode = false; //FT_Vector vpoint = new FT_Vector(); for (; cpoint_index < nextContour; ++cpoint_index) { FT_Vector vpoint = outline.points[cpoint_index]; byte vtag = outline.tags[cpoint_index]; bool has_dropout = (((vtag >> 2) & 0x1) != 0); int dropoutMode = vtag >> 3; Console.WriteLine(vpoint.ToString() + " " + vtag); if ((vtag & 0x1) != 0) { //on curve if (justFromCurveMode) { switch (controlPointCount) { case 1: { ps.Curve3(secondControlPoint.x / FT_RESIZE, secondControlPoint.y / FT_RESIZE, vpoint.x / FT_RESIZE, vpoint.y / FT_RESIZE); } break; case 2: { ps.Curve4(secondControlPoint.x / FT_RESIZE, secondControlPoint.y / FT_RESIZE, thirdControlPoint.x / FT_RESIZE, thirdControlPoint.y / FT_RESIZE, vpoint.x / FT_RESIZE, vpoint.y / FT_RESIZE); } break; default: { throw new NotSupportedException(); } } controlPointCount = 0; justFromCurveMode = false; } else { if (isFirstPoint) { isFirstPoint = false; ps.MoveTo(vpoint.x / FT_RESIZE, vpoint.y / FT_RESIZE); } else { ps.LineTo(vpoint.x / FT_RESIZE, vpoint.y / FT_RESIZE); } if (has_dropout) { //printf("[%d] on,dropoutMode=%d: %d,y:%d \n", mm, dropoutMode, vpoint.x, vpoint.y); } else { //printf("[%d] on,x: %d,y:%d \n", mm, vpoint.x, vpoint.y); } } } else { switch (controlPointCount) { case 0: { //bit 1 set=> off curve, this is a control point //if this is a 2nd order or 3rd order control point if (((vtag >> 1) & 0x1) != 0) { //printf("[%d] bzc3rd, x: %d,y:%d \n", mm, vpoint.x, vpoint.y); thirdControlPoint = new FtVec2(vpoint); } else { //printf("[%d] bzc2nd, x: %d,y:%d \n", mm, vpoint.x, vpoint.y); secondControlPoint = new FtVec2(vpoint); } } break; case 1: { if (((vtag >> 1) & 0x1) != 0) { //printf("[%d] bzc3rd, x: %d,y:%d \n", mm, vpoint.x, vpoint.y); thirdControlPoint = new FtVec2(vpoint); } else { //we already have prev second control point //so auto calculate line to //between 2 point FtVec2 mid = GetMidPoint(secondControlPoint, vpoint); //---------- //generate curve3 ps.Curve3(secondControlPoint.x / FT_RESIZE, secondControlPoint.y / FT_RESIZE, mid.x / FT_RESIZE, mid.y / FT_RESIZE); //------------------------ controlPointCount--; //------------------------ //printf("[%d] bzc2nd, x: %d,y:%d \n", mm, vpoint.x, vpoint.y); secondControlPoint = new FtVec2(vpoint); } } break; default: { throw new NotSupportedException(); } break; } controlPointCount++; justFromCurveMode = true; } } //-------- //close figure //if in curve mode if (justFromCurveMode) { switch (controlPointCount) { case 0: break; case 1: { ps.Curve3(secondControlPoint.x / FT_RESIZE, secondControlPoint.y / FT_RESIZE, ps.LastMoveX, ps.LastMoveY); } break; case 2: { ps.Curve4(secondControlPoint.x / FT_RESIZE, secondControlPoint.y / FT_RESIZE, thirdControlPoint.x / FT_RESIZE, thirdControlPoint.y / FT_RESIZE, ps.LastMoveX, ps.LastMoveY); } break; default: { throw new NotSupportedException(); } } justFromCurveMode = false; controlPointCount = 0; } ps.CloseFigure(); //-------- startContour++; todoContourCount--; } }
//--------------------------------------------------------------------------- public static GlyphImage BuildMsdfFontImage(FontGlyph fontGlyph) { return NativeFontGlyphBuilder.BuildMsdfFontImage(fontGlyph); }
const double FT_RESIZE = 64; //essential to be floating point internal unsafe static GlyphImage BuildMsdfFontImage(FontGlyph fontGlyph) { IntPtr shape = MyFtLib.CreateShape(); FT_Outline outline = (*(FT_Outline*)fontGlyph.glyphMatrix.outline); //outline version //------------------------------ int npoints = outline.n_points; List<PixelFarm.VectorMath.Vector2> points = new List<PixelFarm.VectorMath.Vector2>(npoints); int startContour = 0; int cpoint_index = 0; int todoContourCount = outline.n_contours; int controlPointCount = 0; while (todoContourCount > 0) { int nextContour = outline.contours[startContour] + 1; bool isFirstPoint = true; //--------------- //create contour IntPtr cnt = MyFtLib.ShapeAddBlankContour(shape); FtVec2 secondControlPoint = new FtVec2(); FtVec2 thirdControlPoint = new FtVec2(); bool justFromCurveMode = false; FtVec2 lastMoveTo = new FtVec2(); FtVec2 lastPoint = new FtVec2(); FtVec2 current_point = new Fonts.FtVec2(); for (; cpoint_index < nextContour; ++cpoint_index) { FT_Vector xvpoint = outline.points[cpoint_index]; current_point = new FtVec2(xvpoint.x / FT_RESIZE, xvpoint.y / FT_RESIZE); //Console.WriteLine(xvpoint.x.ToString() + "," + xvpoint.y); byte vtag = outline.tags[cpoint_index]; bool has_dropout = (((vtag >> 2) & 0x1) != 0); int dropoutMode = vtag >> 3; if ((vtag & 0x1) != 0) { if (justFromCurveMode) { switch (controlPointCount) { case 1: { MyFtLib.ContourAddQuadraticSegment(cnt, lastPoint.x, lastPoint.y, secondControlPoint.x, secondControlPoint.y, current_point.x, current_point.y); lastPoint = current_point; } break; case 2: { MyFtLib.ContourAddCubicSegment(cnt, lastPoint.x, lastPoint.y, secondControlPoint.x, secondControlPoint.y, thirdControlPoint.x, thirdControlPoint.y, current_point.x, current_point.y); lastPoint = current_point; } break; default: { throw new NotSupportedException(); } } controlPointCount = 0; justFromCurveMode = false; } else { //line mode if (isFirstPoint) { isFirstPoint = false; lastMoveTo = lastPoint = current_point; } else { MyFtLib.ContourAddLinearSegment(cnt, lastPoint.x, lastPoint.y, current_point.x, current_point.y); lastPoint = current_point; } } } else { if (isFirstPoint) { isFirstPoint = false; lastMoveTo = lastPoint = current_point; } switch (controlPointCount) { case 0: { //bit 1 set=> off curve, this is a control point //if this is a 2nd order or 3rd order control point if (((vtag >> 1) & 0x1) != 0) { ////printf("[%d] bzc3rd, x: %d,y:%d \n", mm, vpoint.x, vpoint.y); thirdControlPoint = new FtVec2(current_point.x, current_point.y); } else { ////printf("[%d] bzc2nd, x: %d,y:%d \n", mm, vpoint.x, vpoint.y); secondControlPoint = new FtVec2(current_point.x, current_point.y); } } break; case 1: { if (((vtag >> 1) & 0x1) != 0) { ////printf("[%d] bzc3rd, x: %d,y:%d \n", mm, vpoint.x, vpoint.y); thirdControlPoint = new FtVec2(current_point.x, current_point.y); } else { //we already have prev second control point //so auto calculate line to //between 2 point FtVec2 mid = GetMidPoint(secondControlPoint, current_point); MyFtLib.ContourAddQuadraticSegment(cnt, lastPoint.x, lastPoint.y, secondControlPoint.x, secondControlPoint.y, mid.x, mid.y); lastPoint = mid; //------------------------ controlPointCount--; //------------------------ //printf("[%d] bzc2nd, x: %d,y:%d \n", mm, vpoint.x, vpoint.y); secondControlPoint = current_point; } } break; default: { throw new NotSupportedException(); } } controlPointCount++; justFromCurveMode = true; } } //-------- //close figure //if in curve mode if (justFromCurveMode) { switch (controlPointCount) { case 0: break; case 1: { MyFtLib.ContourAddQuadraticSegment(cnt, lastPoint.x, lastPoint.y, secondControlPoint.x, secondControlPoint.y, lastMoveTo.x, lastMoveTo.y); lastPoint = current_point; } break; case 2: { MyFtLib.ContourAddCubicSegment(cnt, lastPoint.x, lastPoint.y, secondControlPoint.x, secondControlPoint.y, thirdControlPoint.x, thirdControlPoint.y, lastMoveTo.x, lastMoveTo.y); lastPoint = current_point; } break; default: { throw new NotSupportedException(); } } justFromCurveMode = false; controlPointCount = 0; } else { MyFtLib.ContourAddLinearSegment(cnt, current_point.x, current_point.y, lastMoveTo.x, lastMoveTo.y); lastPoint = current_point; } //ps.CloseFigure(); //-------- startContour++; todoContourCount--; } //------------ double s_left, s_bottom, s_right, s_top; MyFtLib.ShapeFindBounds(shape, out s_left, out s_bottom, out s_right, out s_top); RectangleF glyphBounds = new RectangleF((float)s_left, (float)s_top, (float)(s_right - s_left), (float)(s_top - s_bottom)); //then create msdf texture if (!MyFtLib.ShapeValidate(shape)) { throw new NotSupportedException(); } MyFtLib.ShapeNormalize(shape); int borderXY = 0; int w = (int)Math.Ceiling(glyphBounds.Width) + (borderXY + borderXY); int h = (int)(Math.Ceiling(glyphBounds.Height)) + (borderXY + borderXY); if (w == 0) { w = 5; h = 5; } int[] outputBuffer = new int[w * h]; GlyphImage glyphImage = new GlyphImage(w, h); glyphImage.BorderXY = borderXY; glyphImage.OriginalGlyphBounds = glyphBounds; unsafe { fixed (int* output_header = &outputBuffer[0]) { float dx = 0; float dy = 0; if (s_left < 0) { //make it positive dx = (float)-s_left; } else if (s_left > 0) { } if (s_bottom < 0) { //make it positive dy = (float)-s_bottom; } else if (s_bottom > 0) { } //this glyph image has border (for msdf to render correctly) MyFtLib.MyFtGenerateMsdf(shape, w, h, 4, 1, dx + borderXY, dy + borderXY, -1, 3, output_header); MyFtLib.DeleteUnmanagedObj(shape); } glyphImage.SetImageBuffer(outputBuffer, true); } return glyphImage; }
public void AddGlyph(int codePoint, char c, FontGlyph fontGlyph, GlyphImage glyphImage) { glyphs[codePoint] = new GlyphData(codePoint, c, fontGlyph, glyphImage); }
public GlyphData(int codePoint, char c, FontGlyph fontGlyph, GlyphImage glyphImage) { this.codePoint = codePoint; this.character = c; this.fontGlyph = fontGlyph; this.glyphImage = glyphImage; }