public override void DrawFromGlyphPlans(GlyphPlanSequence seq, int startAt, int len, float x, float y) { UpdateVisualOutputSettings(); //draw data in glyph plan //3. render each glyph float sizeInPoints = this.FontSizeInPoints; float pxscale = _currentTypeface.CalculateScaleToPixelFromPointSize(sizeInPoints); // _glyphMeshCollections.SetCacheInfo(this.Typeface, sizeInPoints, this.HintTechnique); //this draw a single line text span*** Graphics g = this.TargetGraphics; float cx = 0; float cy = 0; float baseline = y; var snapToPxScale = new GlyphPlanSequenceSnapPixelScaleLayout(seq, startAt, len, pxscale); while (snapToPxScale.Read()) { if (!_glyphMeshCollections.TryGetCacheGlyph(snapToPxScale.CurrentGlyphIndex, out GraphicsPath foundPath)) { //if not found then create a new one _currentGlyphPathBuilder.BuildFromGlyphIndex(snapToPxScale.CurrentGlyphIndex, sizeInPoints); _txToGdiPath.Reset(); _currentGlyphPathBuilder.ReadShapes(_txToGdiPath); foundPath = _txToGdiPath.ResultGraphicsPath; //register _glyphMeshCollections.RegisterCachedGlyph(snapToPxScale.CurrentGlyphIndex, foundPath); } //------ //then move pen point to the position we want to draw a glyph cx = (float)Math.Round(snapToPxScale.ExactX + x); cy = (float)Math.Floor(snapToPxScale.ExactY + baseline); g.TranslateTransform(cx, cy); if (FillBackground) { g.FillPath(_fillBrush, foundPath); } if (DrawOutline) { g.DrawPath(_outlinePen, foundPath); } //and then we reset back *** g.TranslateTransform(-cx, -cy); } }
public void HandleNotFoundGlyph(ushort glyphIndex) { #if DEBUG System.Diagnostics.Debug.WriteLine("bitmap texture,create fallback glyph:" + glyphIndex); #endif //NESTED //draw a glyph into tmpMemBmp and then copy to a GlyphImage //build glyph _outlineBuilder.BuildFromGlyphIndex(glyphIndex, _sizeInPoints); BitmapAtlasItemSource glyphImg = _aggTextureGen.CreateAtlasItem(_outlineBuilder, 1); glyphImg.UniqueInt16Name = glyphIndex; _onEachGlyphDel?.Invoke(glyphImg); _atlasBuilder.AddItemSource(glyphImg); }
} // End Function UpdateVisualOutputSettings protected SvgPath GetExistingOrCreateGraphicsPath(ushort glyphIndex) { if (!_glyphMeshCollections.TryGetCacheGlyph(glyphIndex, out SvgPath path)) { _txToGdiPath.Reset(); //clear //if not found then create a new one _currentGlyphPathBuilder.BuildFromGlyphIndex(glyphIndex, this.FontSizeInPoints, _txToGdiPath); path = _txToGdiPath.ResultGraphicsPath; //register _glyphMeshCollections.RegisterCachedGlyph(glyphIndex, path); } return(path); } // End Function GetExistingOrCreateGraphicsPath
private void button2_Click(object sender, EventArgs e) { //EXAMPLE, low-level //this show how to render a glyph on screen //read font file LoadFont(); //inside a font //get some glyph by its name //Glyph oneGlyph = _latinModernMathFont.GetGlyphByName("one"); //for get glyph by name ushort glyphIndex = _latinModernMathFont.GetGlyphIndex((int)'1'); //a glyph contains coordinates of line and curves //we transform data inside it to vxs //this is done by GlyphContour builder GlyphTranslatorToVxs glyphTxToVxs = new GlyphTranslatorToVxs(); GlyphOutlineBuilder outlineBuilder = new GlyphOutlineBuilder(_latinModernMathFont); outlineBuilder.BuildFromGlyphIndex(glyphIndex, 20); //read data into outline builder outlineBuilder.ReadShapes(glyphTxToVxs); //translate data inside outline builder to vxs using (Tools.BorrowVxs(out var v1, out var v2)) using (Tools.BorrowAggPainter(_memBmp, out var p)) { glyphTxToVxs.WriteOutput(v1); //original v1 is head-down Q1RectD bounds = v1.GetBoundingRect(); //with this bounds you also know glyph width/height //we want head up, so => flip it AffineMat aff = AffineMat.Iden(); aff.Translate(-bounds.Width / 2, -bounds.Height / 2); aff.Scale(1, -1); aff.Translate(bounds.Width / 2, bounds.Height / 2); aff.TransformToVxs(v1, v2); //copy data //now the glyph data is inside v1 //test paint this glyph p.Clear(PixelFarm.Drawing.Color.White); p.Fill(v2, PixelFarm.Drawing.Color.Black); } //----------- CopyBitmapToScreen(); }
static void CreateSampleMsdfTextureFont(string fontfile, float sizeInPoint, ushort startGlyphIndex, ushort endGlyphIndex, string outputFile) { //sample var reader = new OpenFontReader(); Typeface typeface = null; using (var fs = new FileStream(fontfile, FileMode.Open)) { //1. read typeface from font file typeface = reader.Read(fs); } //sample: create sample msdf texture //------------------------------------------------------------- var builder = new GlyphOutlineBuilder(typeface); //builder.UseTrueTypeInterpreter = this.chkTrueTypeHint.Checked; //builder.UseVerticalHinting = this.chkVerticalHinting.Checked; //------------------------------------------------------------- RequestFont reqFont = new RequestFont(typeface.Name, sizeInPoint); var atlasBuilder = new SimpleBitmapAtlasBuilder(); atlasBuilder.FontFilename = System.IO.Path.GetFileName(fontfile); atlasBuilder.FontKey = reqFont.FontKey; //create temp folder for each glyph string tempFolderName = "tmp_msdf"; if (Directory.Exists(tempFolderName)) { //DANGER! Directory.Delete(tempFolderName, true); } Directory.CreateDirectory(tempFolderName); if (endGlyphIndex < 1) { endGlyphIndex = (ushort)(typeface.GlyphCount - 1); } for (ushort gindex = startGlyphIndex; gindex <= endGlyphIndex; ++gindex) { //build glyph builder.BuildFromGlyphIndex(gindex, sizeInPoint); var glyphContourBuilder = new ContourBuilder(); //glyphToContour.Read(builder.GetOutputPoints(), builder.GetOutputContours()); var genParams = new MsdfGenParams(); builder.ReadShapes(new GlyphContourBuilder2(glyphContourBuilder)); //genParams.shapeScale = 1f / 64; //we scale later (as original C++ code use 1/64) BitmapAtlasItemSource glyphImg = MsdfImageGen.CreateMsdfImageV1(glyphContourBuilder, genParams); glyphImg.UniqueInt16Name = gindex; atlasBuilder.AddItemSource(glyphImg); using (Bitmap bmp = new Bitmap(glyphImg.Width, glyphImg.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)) { int[] buffer = glyphImg.GetImageBuffer(); var bmpdata = bmp.LockBits(new System.Drawing.Rectangle(0, 0, glyphImg.Width, glyphImg.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat); System.Runtime.InteropServices.Marshal.Copy(buffer, 0, bmpdata.Scan0, buffer.Length); bmp.UnlockBits(bmpdata); bmp.Save(tempFolderName + "//glyph_" + gindex + ".png"); } } MemBitmap glyphImg2 = atlasBuilder.BuildSingleImage(true); glyphImg2.SaveImage(outputFile); string saveToFile = "a_info.bin"; using (System.IO.FileStream saveFs = new FileStream(saveToFile, FileMode.Create)) { atlasBuilder.SaveAtlasInfo(saveFs); saveFs.Flush(); saveFs.Close(); } // //----------- //test read texture info back var atlasBuilder2 = new SimpleBitmapAtlasBuilder(); using (System.IO.FileStream readFromFs = new FileStream(saveToFile, FileMode.Open)) { var readbackFontAtlas = atlasBuilder2.LoadAtlasInfo(readFromFs); } }
/// <summary> /// generate glyph run into a given textRun /// </summary> /// <param name="outputTextRun"></param> /// <param name="charBuffer"></param> /// <param name="start"></param> /// <param name="len"></param> public void GenerateGlyphRuns(TextRun outputTextRun, char[] charBuffer, int start, int len) { // layout glyphs with selected layout technique float sizeInPoints = this.FontSizeInPoints; outputTextRun.typeface = this.Typeface; outputTextRun.sizeInPoints = sizeInPoints; //in this version we store original glyph into the mesh collection //and then we scale it later, so I just specific font size=0 (you can use any value) _glyphMeshCollection.SetCacheInfo(this.Typeface, 0, this.HintTechnique); GlyphLayoutMan.Typeface = this.Typeface; GlyphLayoutMan.Layout(charBuffer, start, len); float pxscale = this.Typeface.CalculateScaleToPixelFromPointSize(sizeInPoints); _resuableGlyphPlanList.Clear(); GenerateGlyphPlan(charBuffer, 0, charBuffer.Length, _resuableGlyphPlanList); // render each glyph int planCount = _resuableGlyphPlanList.Count; for (var i = 0; i < planCount; ++i) { _pathTranslator.Reset(); //---- //glyph path //---- UnscaledGlyphPlan glyphPlan = _resuableGlyphPlanList[i]; // //1. check if we have this glyph in cache? //if yes, not need to build it again ProcessedGlyph processGlyph; float[] tessData = null; if (!_glyphMeshCollection.TryGetCacheGlyph(glyphPlan.glyphIndex, out processGlyph)) { //if not found the create a new one and register it var writablePath = new WritablePath(); _pathTranslator.SetOutput(writablePath); _currentGlyphPathBuilder.BuildFromGlyphIndex(glyphPlan.glyphIndex, sizeInPoints); _currentGlyphPathBuilder.ReadShapes(_pathTranslator); //------- //do tess int[] endContours; float[] flattenPoints = _curveFlattener.Flatten(writablePath._points, out endContours); tessData = _tessTool.TessAsTriVertexArray(flattenPoints, endContours, out int vertexCount); processGlyph = new ProcessedGlyph(tessData, (ushort)vertexCount); _glyphMeshCollection.RegisterCachedGlyph(glyphPlan.glyphIndex, processGlyph); } outputTextRun.AddGlyph( new GlyphRun(glyphPlan, processGlyph.tessData, processGlyph.vertextCount)); } }
void DrawOutput(Painter painter, Typeface typeface, char selectedChar) { painter.Clear(Color.White); //this is a demo. // float fontSizeInPts = 300; _glyphPathBuilder.BuildFromGlyphIndex(typeface.GetGlyphIndex(selectedChar), fontSizeInPts); var prevColor = painter.StrokeColor; painter.StrokeColor = Color.Black; using (Tools.BorrowVxs(out var v1)) using (Tools.BorrowCurveFlattener(out var flattener)) { _glyphPathBuilder.ReadShapes(_tovxs); //config if (rdoSimpleIncCurveFlattener.Checked) { flattener.ApproximationMethod = CurveApproximationMethod.Inc; if (int.TryParse(txtIncrementalTessStep.Text, out int stepCount)) { if (stepCount < 0) { //auto calculate inc step count flattener.IncUseFixedStep = false; } else { //fix manual inc step count flattener.IncUseFixedStep = true; flattener.IncStepCount = stepCount; } } } else { flattener.ApproximationMethod = CurveApproximationMethod.Div; if (double.TryParse(txtDivAngleTolerenceEpsilon.Text, out double angleTolerance)) { flattener.AngleTolerance = angleTolerance; } if (byte.TryParse(txtDivCurveRecursiveLimit.Text, out byte recursiveLim)) { flattener.RecursiveLimit = recursiveLim; } } _tovxs.WriteOutput(v1, flattener); //write content from GlyphTranslator to v1 painter.Fill(v1, PixelFarm.Drawing.KnownColors.Gray); _tovxs.Reset(); //tess the vxs FigureBuilder figBuilder = new FigureBuilder(); FigureContainer container = figBuilder.Build(v1); TessTriangleTechnique tessTechnique = TessTriangleTechnique.DrawElement; if (container.IsSingleFigure) { Figure figure = container._figure; if (rdoTessSGI.Checked) { //coords of tess triangles switch (tessTechnique) { case TessTriangleTechnique.DrawArray: { DrawTessTriangles(painter, figure.GetAreaTess(_tessTool, _tessTool.WindingRuleType, TessTriangleTechnique.DrawArray)); } break; case TessTriangleTechnique.DrawElement: { float[] tessArea = figure.GetAreaTess(_tessTool, _tessTool.WindingRuleType, TessTriangleTechnique.DrawElement); ushort[] index = figure.GetAreaIndexList(); DrawTessTriangles(painter, tessArea, index); } break; } } else { if (chkShowContourAnalysis.Checked) { ContourAnalyzer analyzer1 = new ContourAnalyzer(); IntermediateOutline outline = analyzer1.CreateIntermediateOutline(v1); var dbugVisualizer = new PixelFarm.GlyphDebugContourVisualizer(); dbugVisualizer.SetPainter(painter); dbugVisualizer.Scale = _typeface.CalculateScaleToPixelFromPointSize(fontSizeInPts); dbugVisualizer.WalkCentroidLine(outline); } else { //Poly2Tri List <Poly2Tri.Polygon> polygons = figure.GetTrianglulatedArea(false); //draw polygon painter.StrokeColor = Color.Red; DrawPoly2TriPolygon(painter, polygons); } } } else { MultiFigures multiFig = container._multiFig; if (rdoTessSGI.Checked) { switch (tessTechnique) { case TessTriangleTechnique.DrawArray: { DrawTessTriangles(painter, multiFig.GetAreaTess(_tessTool, _tessTool.WindingRuleType, TessTriangleTechnique.DrawArray)); } break; case TessTriangleTechnique.DrawElement: { float[] tessArea = multiFig.GetAreaTess(_tessTool, _tessTool.WindingRuleType, TessTriangleTechnique.DrawElement); ushort[] index = multiFig.GetAreaIndexList(); DrawTessTriangles(painter, tessArea, index); } break; } } else { if (chkShowContourAnalysis.Checked) { ContourAnalyzer analyzer1 = new ContourAnalyzer(); IntermediateOutline outline = analyzer1.CreateIntermediateOutline(v1); var dbugVisualizer = new PixelFarm.GlyphDebugContourVisualizer(); dbugVisualizer.SetPainter(painter); dbugVisualizer.Scale = _typeface.CalculateScaleToPixelFromPointSize(fontSizeInPts); dbugVisualizer.WalkCentroidLine(outline); } else { List <Poly2Tri.Polygon> polygons = multiFig.GetTrianglulatedArea(false); painter.StrokeColor = Color.Red; DrawPoly2TriPolygon(painter, polygons); } } } } painter.StrokeColor = prevColor; //------------- //tess //if (rdoTessSGI.Checked) //{ // //SGI Tess Lib // if (!_tessTool.TessPolygon(polygon1, _contourEnds)) // { // return; // } // //1. // List<ushort> indexList = _tessTool.TessIndexList; // //2. // List<TessVertex2d> tempVertexList = _tessTool.TempVertexList; // //3. // int vertexCount = indexList.Count; // //----------------------------- // int orgVertexCount = polygon1.Length / 2; // float[] vtx = new float[vertexCount * 2];//*** // int n = 0; // for (int p = 0; p < vertexCount; ++p) // { // ushort index = indexList[p]; // if (index >= orgVertexCount) // { // //extra coord (newly created) // TessVertex2d extraVertex = tempVertexList[index - orgVertexCount]; // vtx[n] = (float)extraVertex.x; // vtx[n + 1] = (float)extraVertex.y; // } // else // { // //original corrd // vtx[n] = (float)polygon1[index * 2]; // vtx[n + 1] = (float)polygon1[(index * 2) + 1]; // } // n += 2; // } // //----------------------------- // //draw tess result // int j = vtx.Length; // for (int i = 0; i < j;) // { // var p0 = new PointF(vtx[i], vtx[i + 1]); // var p1 = new PointF(vtx[i + 2], vtx[i + 3]); // var p2 = new PointF(vtx[i + 4], vtx[i + 5]); // _g.DrawLine(Pens.Red, p0, p1); // _g.DrawLine(Pens.Red, p1, p2); // _g.DrawLine(Pens.Red, p2, p0); // i += 6; // } //} //else //{ // List<Poly2Tri.Polygon> outputPolygons = new List<Poly2Tri.Polygon>(); // Poly2TriExampleHelper.Triangulate(polygon1, contourEndIndices, flipYAxis, outputPolygons); // foreach (Poly2Tri.Polygon polygon in outputPolygons) // { // foreach (Poly2Tri.DelaunayTriangle tri in polygon.Triangles) // { // Poly2Tri.TriangulationPoint p0 = tri.P0; // Poly2Tri.TriangulationPoint p1 = tri.P1; // Poly2Tri.TriangulationPoint p2 = tri.P2; // _g.DrawLine(Pens.Red, (float)p0.X, (float)p0.Y, (float)p1.X, (float)p1.Y); // _g.DrawLine(Pens.Red, (float)p1.X, (float)p1.Y, (float)p2.X, (float)p2.Y); // _g.DrawLine(Pens.Red, (float)p2.X, (float)p2.Y, (float)p0.X, (float)p0.Y); // } // } //} }
void CreateTextureFontFromGlyphIndices( Typeface typeface, float sizeInPoint, HintTechnique hintTechnique, SimpleBitmapAtlasBuilder atlasBuilder, ushort[] glyphIndices) { //sample: create sample msdf texture //------------------------------------------------------------- var outlineBuilder = new GlyphOutlineBuilder(typeface); outlineBuilder.SetHintTechnique(hintTechnique); // if (atlasBuilder.TextureKind == TextureKind.Msdf) { float pxscale = typeface.CalculateScaleToPixelFromPointSize(sizeInPoint); var msdfGenParams = new Msdfgen.MsdfGenParams(); int j = glyphIndices.Length; if (MsdfGenVersion == 3) { Msdfgen.MsdfGen3 gen3 = new Msdfgen.MsdfGen3(); for (int i = 0; i < j; ++i) { ushort gindex = glyphIndices[i]; //create picture with unscaled version set scale=-1 //(we will create glyph contours and analyze them) outlineBuilder.BuildFromGlyphIndex(gindex, -1); var glyphToVxs = new GlyphTranslatorToVxs(); outlineBuilder.ReadShapes(glyphToVxs); using (Tools.BorrowVxs(out var vxs)) { glyphToVxs.WriteUnFlattenOutput(vxs, pxscale); BitmapAtlasItemSource glyphImg = gen3.GenerateMsdfTexture(vxs); glyphImg.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImg); // atlasBuilder.AddItemSource(glyphImg); } } } else { Msdfgen.MsdfGen3 gen3 = new Msdfgen.MsdfGen3(); for (int i = 0; i < j; ++i) { ushort gindex = glyphIndices[i]; //create picture with unscaled version set scale=-1 //(we will create glyph contours and analyze them) outlineBuilder.BuildFromGlyphIndex(gindex, -1); var glyphToVxs = new GlyphTranslatorToVxs(); outlineBuilder.ReadShapes(glyphToVxs); using (Tools.BorrowVxs(out var vxs)) { glyphToVxs.WriteUnFlattenOutput(vxs, pxscale); BitmapAtlasItemSource glyphImg = gen3.GenerateMsdfTexture(vxs); glyphImg.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImg); atlasBuilder.AddItemSource(glyphImg); } } } } else { AggGlyphTextureGen aggTextureGen = new AggGlyphTextureGen(); aggTextureGen.TextureKind = atlasBuilder.TextureKind; //create reusable agg painter*** //assume each glyph size= 2 * line height //TODO: review here again... int tmpMemBmpHeight = (int)(2 * typeface.CalculateRecommendLineSpacing() * typeface.CalculateScaleToPixelFromPointSize(sizeInPoint)); //create glyph img using (PixelFarm.CpuBlit.MemBitmap tmpMemBmp = new PixelFarm.CpuBlit.MemBitmap(tmpMemBmpHeight, tmpMemBmpHeight)) //square { //draw a glyph into tmpMemBmp and then copy to a GlyphImage aggTextureGen.Painter = PixelFarm.CpuBlit.AggPainter.Create(tmpMemBmp); #if DEBUG tmpMemBmp._dbugNote = "CreateGlyphImage()"; #endif int j = glyphIndices.Length; for (int i = 0; i < j; ++i) { //build glyph ushort gindex = glyphIndices[i]; outlineBuilder.BuildFromGlyphIndex(gindex, sizeInPoint); BitmapAtlasItemSource glyphImg = aggTextureGen.CreateAtlasItem(outlineBuilder, 1); glyphImg.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImg); atlasBuilder.AddItemSource(glyphImg); } } } }
void CreateTextureFontFromGlyphIndices( HintTechnique hintTechnique, SimpleBitmapAtlasBuilder atlasBuilder, ushort[] glyphIndices) { //sample: create sample msdf texture //------------------------------------------------------------- var outlineBuilder = new GlyphOutlineBuilder(_typeface); outlineBuilder.SetHintTechnique(hintTechnique); // AggGlyphTextureGen aggTextureGen = new AggGlyphTextureGen(); GlyphNotFoundHelper glyphNotFoundHelper = new GlyphNotFoundHelper(atlasBuilder, outlineBuilder, _onEachGlyphDel, aggTextureGen, _sizeInPoints); //create reusable agg painter*** //assume each glyph size= 2 * line height //TODO: review here again... //please note that DPI effect glyph size //*** int tmpMemBmpHeight = (int)(2 * _typeface.CalculateRecommendLineSpacing() * _px_scale); // if (atlasBuilder.TextureKind == TextureKind.Msdf) { var msdfGenParams = new Msdfgen.MsdfGenParams(); int j = glyphIndices.Length; if (MsdfGenVersion == 3) { Msdfgen.MsdfGen3 gen3 = new Msdfgen.MsdfGen3(); for (int i = 0; i < j; ++i) { ushort gindex = glyphIndices[i]; //create picture with unscaled version set scale=-1 //(we will create glyph contours and analyze them) var glyphToVxs = new GlyphTranslatorToVxs(); outlineBuilder.BuildFromGlyphIndex(gindex, -1, glyphToVxs); using (Tools.BorrowVxs(out var vxs)) { glyphToVxs.WriteUnFlattenOutput(vxs, _px_scale); BitmapAtlasItemSource glyphImg = gen3.GenerateMsdfTexture(vxs); glyphImg.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImg); // atlasBuilder.AddItemSource(glyphImg); } } } else { //use gen3 Msdfgen.MsdfGen3 gen3 = new Msdfgen.MsdfGen3(); for (int i = 0; i < j; ++i) { ushort gindex = glyphIndices[i]; //create picture with unscaled version set scale=-1 //(we will create glyph contours and analyze them) var glyphToVxs = new GlyphTranslatorToVxs(); outlineBuilder.BuildFromGlyphIndex(gindex, -1, glyphToVxs); using (Tools.BorrowVxs(out var vxs)) { glyphToVxs.WriteUnFlattenOutput(vxs, _px_scale); BitmapAtlasItemSource glyphImg = gen3.GenerateMsdfTexture(vxs); glyphImg.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImg); atlasBuilder.AddItemSource(glyphImg); } } } return; } else if (atlasBuilder.TextureKind == TextureKind.Bitmap) { //generate color bitmap atlas int j = glyphIndices.Length; GlyphMeshStore glyphMeshStore = new GlyphMeshStore(); glyphMeshStore.SetFont(_typeface, _sizeInPoints); aggTextureGen.TextureKind = TextureKind.Bitmap; using (PixelFarm.CpuBlit.MemBitmap tmpMemBmp = new PixelFarm.CpuBlit.MemBitmap(tmpMemBmpHeight, tmpMemBmpHeight)) //square { aggTextureGen.Painter = PixelFarm.CpuBlit.AggPainter.Create(tmpMemBmp); #if DEBUG tmpMemBmp._dbugNote = "CreateGlyphImage()"; #endif if (_typeface.HasColorTable()) { //outline glyph for (int i = 0; i < j; ++i) { ushort gindex = glyphIndices[i]; if (!_typeface.COLRTable.LayerIndices.TryGetValue(gindex, out ushort colorLayerStart)) { //not found, then render as normal //TODO: impl //create glyph img glyphNotFoundHelper.HandleNotFoundGlyph(gindex); } else { //TODO: review this again GlyphBitmap glyphBmp = GetGlyphBitmapFromColorOutlineGlyph(gindex, glyphMeshStore, colorLayerStart); if (glyphBmp == null) { glyphNotFoundHelper.HandleNotFoundGlyph(gindex); } else { int w = glyphBmp.Width; int h = glyphBmp.Height; BitmapAtlasItemSource glyphImage = new BitmapAtlasItemSource(glyphBmp.Width, glyphBmp.Height); glyphImage.TextureXOffset = (short)glyphBmp.ImageStartX; glyphImage.TextureYOffset = (short)glyphBmp.ImageStartY; // glyphImage.SetImageBuffer(MemBitmapExt.CopyImgBuffer(glyphBmp.Bitmap, w, h, true), false); glyphImage.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImage); atlasBuilder.AddItemSource(glyphImage); //clear glyphBmp.Bitmap.Dispose(); glyphBmp.Bitmap = null; } } } } else if (_typeface.IsBitmapFont) { aggTextureGen.TextureKind = TextureKind.Bitmap; //test this with Noto Color Emoji for (int i = 0; i < j; ++i) { ushort gindex = glyphIndices[i]; GlyphBitmap glyphBmp = GetGlyphBitmapFromBitmapFont(gindex); if (glyphBmp == null) { glyphNotFoundHelper.HandleNotFoundGlyph(gindex); } else { int w = glyphBmp.Width; int h = glyphBmp.Height; BitmapAtlasItemSource glyphImage = new BitmapAtlasItemSource(glyphBmp.Width, glyphBmp.Height); glyphImage.TextureXOffset = (short)glyphBmp.ImageStartX; glyphImage.TextureYOffset = (short)glyphBmp.ImageStartY; // glyphImage.SetImageBuffer(MemBitmapExt.CopyImgBuffer(glyphBmp.Bitmap, w, h, true), false); glyphImage.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImage); atlasBuilder.AddItemSource(glyphImage); //clear glyphBmp.Bitmap.Dispose(); glyphBmp.Bitmap = null; } } } else if (_typeface.HasSvgTable()) { aggTextureGen.TextureKind = TextureKind.Bitmap; //test this with TwitterEmoji //generate membitmap from svg #if DEBUG System.Diagnostics.Stopwatch sw1 = new System.Diagnostics.Stopwatch(); sw1.Start(); #endif for (int i = 0; i < j; ++i) { //TODO: add mutli-threads / async version ushort gindex = glyphIndices[i]; GlyphBitmap glyphBmp = GetGlyphBitmapFromSvg(gindex); if (glyphBmp == null) { glyphNotFoundHelper.HandleNotFoundGlyph(gindex); } else { int w = glyphBmp.Width; int h = glyphBmp.Height; BitmapAtlasItemSource glyphImage = new BitmapAtlasItemSource(glyphBmp.Width, glyphBmp.Height); glyphImage.TextureXOffset = (short)glyphBmp.ImageStartX; glyphImage.TextureYOffset = (short)glyphBmp.ImageStartY; // glyphImage.SetImageBuffer(MemBitmapExt.CopyImgBuffer(glyphBmp.Bitmap, w, h, true), false); glyphImage.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImage); atlasBuilder.AddItemSource(glyphImage); //clear glyphBmp.Bitmap.Dispose(); glyphBmp.Bitmap = null; } } #if DEBUG sw1.Stop(); long ms = sw1.ElapsedMilliseconds; #endif } return; //NO go below //*** } //END using } //--------------------------- //OTHERS.... { aggTextureGen.TextureKind = atlasBuilder.TextureKind; //create glyph img using (PixelFarm.CpuBlit.MemBitmap tmpMemBmp = new PixelFarm.CpuBlit.MemBitmap(tmpMemBmpHeight, tmpMemBmpHeight)) //square { //draw a glyph into tmpMemBmp and then copy to a GlyphImage aggTextureGen.Painter = PixelFarm.CpuBlit.AggPainter.Create(tmpMemBmp); #if DEBUG tmpMemBmp._dbugNote = "CreateGlyphImage()"; #endif int j = glyphIndices.Length; for (int i = 0; i < j; ++i) { //build glyph ushort gindex = glyphIndices[i]; outlineBuilder.BuildFromGlyphIndex(gindex, _sizeInPoints); BitmapAtlasItemSource glyphImg = aggTextureGen.CreateAtlasItem(outlineBuilder, 1); glyphImg.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImg); atlasBuilder.AddItemSource(glyphImg); } } } }
void UpdateOutput() { string oneChar = this.textBox1.Text.Trim(); if (string.IsNullOrEmpty(oneChar)) { return; } // char selectedChar = oneChar[0]; // // //selectedChar = 'e'; if (_g == null) { _g = this.panel1.CreateGraphics(); } _g.Clear(Color.White); //------- //string testFont = "c:\\Windows\\Fonts\\Tahoma.ttf"; string testFont = @"Test\SourceSansPro-Regular.ttf"; using (FileStream fs = new FileStream(testFont, FileMode.Open, FileAccess.Read)) { OpenFontReader reader = new OpenFontReader(); Typeface typeface = reader.Read(fs); var writablePath = new WritablePath(); var txToPath = new GlyphTranslatorToPath(); //translator txToPath.SetOutput(writablePath); var builder = new GlyphOutlineBuilder(typeface); builder.BuildFromGlyphIndex(typeface.GetGlyphIndex(selectedChar), 300, txToPath); //------ // //**flatten contour before send to Tess*** var curveFlattener = new SimpleCurveFlattener(); if (rdoSimpleIncCurveFlattener.Checked) { if (int.TryParse(txtIncrementalTessStep.Text, out int incSteps)) { curveFlattener.IncrementalStep = incSteps; } curveFlattener.FlattenMethod = CurveFlattenMethod.Inc; } else { if (double.TryParse(txtDivAngleTolerenceEpsilon.Text, out double angleTolerenceEpsilon)) { //convert degree to rad curveFlattener.DivCurveAngleTolerenceEpsilon = DegToRad(angleTolerenceEpsilon); } if (int.TryParse(txtDivCurveRecursiveLimit.Text, out int recuvesiveLim)) { curveFlattener.DivCurveRecursiveLimit = recuvesiveLim; } curveFlattener.FlattenMethod = CurveFlattenMethod.Div; } _glyphPoints2 = curveFlattener.Flatten(writablePath._points, out _contourEnds); ////-------------------------------------- ////raw glyph points //int j = glyphPoints.Length; //float scale = typeface.CalculateToPixelScaleFromPointSize(256); //glyphPoints2 = new float[j * 2]; //int n = 0; //for (int i = 0; i < j; ++i) //{ // GlyphPointF pp = glyphPoints[i]; // glyphPoints2[n] = pp.X * scale; // n++; // glyphPoints2[n] = pp.Y * scale; // n++; //} ////-------------------------------------- } DrawOutput(); }