Exemple #1
0
        VertexStore BuildVxsForGlyph(GlyphOutlineBuilder builder, char character, float size)
        {
            //-----------
            //TODO: review here
            builder.Build(character, size);
            var txToVxs = new GlyphTranslatorToVxs();

            builder.ReadShapes(txToVxs);

            VertexStore v2 = new VertexStore();

            using (Tools.BorrowVxs(out var v0))
                using (Tools.BorrowCurveFlattener(out var flattener))
                {
                    txToVxs.WriteOutput(v0);

                    Q1RectD bounds = v0.GetBoundingRect();

                    AffineMat mat = AffineMat.Iden();
                    mat.Scale(1, -1);//flipY
                    mat.Translate(0, bounds.Height);

                    flattener.MakeVxs(v0, mat, v2);
                }
            return(v2);
        }
Exemple #2
0
        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);
            }
        }
Exemple #3
0
        void RenderWithMsdfImg(Typeface typeface, char testChar, float sizeInPoint)
        {
            _painter.FillColor = PixelFarm.Drawing.Color.Black;
            //p.UseSubPixelRendering = chkLcdTechnique.Checked;
            _painter.Clear(PixelFarm.Drawing.Color.White);
            //----------------------------------------------------
            var builder = new GlyphOutlineBuilder(typeface);

            builder.SetHintTechnique(_glyphRenderOptions.HintTechnique);

            //----------------------------------------------------
            builder.Build(testChar, sizeInPoint);
            //----------------------------------------------------
            var glyphToContour = new ContourBuilder();


            builder.ReadShapes(new GlyphTranslatorToContourBuilder(glyphToContour));
            //glyphToContour.Read(builder.GetOutputPoints(), builder.GetOutputContours());
            Msdfgen.MsdfGenParams genParams = new Msdfgen.MsdfGenParams();
            BitmapAtlasItemSource glyphImg  = MsdfImageGen.CreateMsdfImageV1(glyphToContour, genParams);

            MemBitmap actualImg = MemBitmap.CreateFromCopy(glyphImg.Width, glyphImg.Height, glyphImg.GetImageBuffer());

            _painter.DrawImage(actualImg, 0, 0);

            //using (Bitmap bmp = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
            //{
            //    var bmpdata = bmp.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
            //    System.Runtime.InteropServices.Marshal.Copy(buffer, 0, bmpdata.Scan0, buffer.Length);
            //    bmp.UnlockBits(bmpdata);
            //    bmp.Save("a001_xn2_" + n + ".png");
            //}

            if (_contourAnalysisOpts.ShowGrid)
            {
                //render grid
                RenderGrids(800, 600, _gridSize, _painter);
            }

            //6. use this util to copy image from Agg actual image to System.Drawing.Bitmap
            PixelFarm.CpuBlit.BitmapHelper.CopyToGdiPlusBitmapSameSize(_destImg, _winBmp);
            //---------------
            //7. just render our bitmap
            _g.Clear(Color.White);
            _g.DrawImage(_winBmp, new Point(30, 20));
        }
Exemple #4
0
        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();
        }
Exemple #5
0
        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);
            }
        }
Exemple #6
0
        /// <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));
            }
        }
Exemple #7
0
        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);
            //        }
            //    }
            //}
        }
Exemple #8
0
        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);
                    }
                }
            }
        }
        public BitmapAtlasItemSource CreateAtlasItem(GlyphOutlineBuilder builder, float pxscale)
        {
            _txToVxs.Reset();
            //1. builder read shape and translate it with _txToVxs
            builder.ReadShapes(_txToVxs);

            using (Tools.BorrowVxs(out var glyphVxs, out var vxs2))
            {
                //2. write translated data (in the _txToVxs) to glyphVxs

                _txToVxs.WriteOutput(glyphVxs, pxscale);

                Q1RectD bounds = glyphVxs.GetBoundingRect();

                //--------------------------------------------
                int w = (int)System.Math.Ceiling(bounds.Width);
                int h = (int)System.Math.Ceiling(bounds.Height);
                if (w < 5)
                {
                    w = 5;
                }
                if (h < 5)
                {
                    h = 5;
                }
                //we need some margin
                int horizontal_margin = 1;
                int vertical_margin   = 1;

                //translate to positive quadrant and use minimum space

                int dx = (int)Math.Ceiling((bounds.Left < 0) ? -bounds.Left : 0);
                int dy = 0;

                //vertical adjust =>since we need to move it, then move it with integer value
                if (bounds.Bottom < 0)
                {
                    dy = (int)Math.Ceiling(-bounds.Bottom);
                }
                else if (bounds.Bottom > 0)
                {
                    dy = (int)Math.Floor(-bounds.Bottom);
                }
                dx += horizontal_margin; //margin left
                dy += vertical_margin;
                //--------------------------------------------
                w = dx + w + horizontal_margin;            //+right margin

                h = vertical_margin + h + vertical_margin; //+bottom margin
                AggPainter painter = Painter;
                if (TextureKind == TextureKind.StencilLcdEffect)
                {
                    glyphVxs.TranslateToNewVxs(dx + 0.33f, dy, vxs2); //offset to proper x of subpixel rendering  ***
                    glyphVxs = vxs2;

                    Q1RectD bounds2 = vxs2.GetBoundingRect();
                    if (w < bounds2.Right)
                    {
                        w = (int)Math.Ceiling(bounds2.Right);
                    }
                    //
                    painter.UseLcdEffectSubPixelRendering = true;
                    //we use white glyph on black bg for this texture


                    painter.Clear(Color.Black);
                    painter.FillColor = Color.White;


                    //painter.Clear(Color.FromArgb(0, 255, 255, 255)); //white -transparent
                    //painter.FillColor = Color.Black;


                    //painter.Clear(Color.White);
                    //painter.FillColor = Color.Black;

                    //painter.Clear(Color.Empty);
                    //painter.FillColor = Color.Black;

                    painter.Fill(glyphVxs);

                    //apply sharpen filter
                    //painter.DoFilter(new RectInt(0, h, w, 0), 2);
                    //painter.DoFilter(new RectInt(0, h, w, 0), 2); //?
                }
                else
                {
                    glyphVxs.TranslateToNewVxs(dx, dy, vxs2);
                    glyphVxs = vxs2;

                    painter.UseLcdEffectSubPixelRendering = false;

                    if (TextureKind == TextureKind.StencilGreyScale)
                    {
                        painter.Clear(Color.Empty);
                        painter.FillColor = Color.Black;
                    }
                    else
                    {
                        painter.Clear(BackGroundColor);
                        painter.FillColor = this.GlyphColor;
                    }
                    painter.Fill(glyphVxs);
                }
                //


                if (w > painter.RenderSurface.DestBitmap.Width)
                {
                    w = painter.RenderSurface.DestBitmap.Width;
                }
                if (h > painter.RenderSurface.DestBitmap.Height)
                {
                    h = painter.RenderSurface.DestBitmap.Height;
                }

                var glyphImage = new BitmapAtlasItemSource(w, h);

#if DEBUG
                if (dx < short.MinValue || dx > short.MaxValue)
                {
                    throw new NotSupportedException();
                }
                if (dy < short.MinValue || dy > short.MaxValue)
                {
                    throw new NotSupportedException();
                }
#endif

                glyphImage.TextureXOffset = (short)dx;
                glyphImage.TextureYOffset = (short)dy;

                glyphImage.SetImageBuffer(MemBitmapExt.CopyImgBuffer(painter.RenderSurface.DestBitmap, w, h), false);
                //copy data from agg canvas to glyph image
                return(glyphImage);
            }
        }