Esempio n. 1
0
 static void SaveImgBufferToFile(BitmapAtlasItemSource glyphImg, string filename)
 {
     using (PixelFarm.CpuBlit.MemBitmap memBmp = PixelFarm.CpuBlit.MemBitmap.CreateFromCopy(
                glyphImg.Width, glyphImg.Height, glyphImg.GetImageBuffer(), false))
     {
         PixelFarm.CpuBlit.MemBitmapExtensions.SaveImage(memBmp, filename, PixelFarm.CpuBlit.MemBitmapIO.OutputImageFormat.Png);
     }
 }
Esempio n. 2
0
            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);
            }
        public MemBitmap BuildSingleImage(bool flipY)
        {
            //1. add to list
            var itemList = new List <BitmapAtlasItemSource>(_items.Values);

            int totalMaxLim  = MaxAtlasWidth;
            int maxRowHeight = 0;
            int currentY     = 0;
            int currentX     = 0;

            switch (this.SpaceCompactOption)
            {
            default:
                throw new System.NotSupportedException();

            case CompactOption.BinPack:
            {
                //2. sort by glyph width
                itemList.Sort((a, b) => a.Width.CompareTo(b.Width));
                //3. layout
                for (int i = itemList.Count - 1; i >= 0; --i)
                {
                    BitmapAtlasItemSource g = itemList[i];
                    if (g.Height > maxRowHeight)
                    {
                        maxRowHeight = g.Height;
                    }
                    if (currentX + g.Width > totalMaxLim)
                    {
                        //start new row
                        currentY += maxRowHeight;
                        currentX  = 0;
                    }
                    //-------------------
                    g.Area    = new Rectangle(currentX, currentY, g.Width, g.Height);
                    currentX += g.Width;
                }
            }
            break;

            case CompactOption.ArrangeByHeight:
            {
                //2. sort by height
                itemList.Sort((a, b) => a.Height.CompareTo(b.Height));

                //3. layout
                int glyphCount = itemList.Count;
                for (int i = 0; i < glyphCount; ++i)
                {
                    BitmapAtlasItemSource g = itemList[i];
                    if (g.Height > maxRowHeight)
                    {
                        maxRowHeight = g.Height;
                    }
                    if (currentX + g.Width > totalMaxLim)
                    {
                        //start new row
                        currentY    += maxRowHeight;
                        currentX     = 0;
                        maxRowHeight = g.Height;        //reset, after start new row
                    }
                    //-------------------
                    g.Area    = new Rectangle(currentX, currentY, g.Width, g.Height);
                    currentX += g.Width;
                }
            }
            break;

            case CompactOption.None:
            {
                //3. layout
                int glyphCount = itemList.Count;
                for (int i = 0; i < glyphCount; ++i)
                {
                    BitmapAtlasItemSource g = itemList[i];
                    if (g.Height > maxRowHeight)
                    {
                        maxRowHeight = g.Height;
                    }
                    if (currentX + g.Width > totalMaxLim)
                    {
                        //start new row
                        currentY    += maxRowHeight;
                        currentX     = 0;
                        maxRowHeight = g.Height;        //reset, after start new row
                    }
                    //-------------------
                    g.Area    = new Rectangle(currentX, currentY, g.Width, g.Height);
                    currentX += g.Width;
                }
            }
            break;
            }

            currentY += maxRowHeight;
            int imgH = currentY;
            // -------------------------------
            //compact image location
            // TODO: review performance here again***

            int totalImgWidth = totalMaxLim;

            if (SpaceCompactOption == CompactOption.BinPack) //again here?
            {
                totalImgWidth = 0;                           //reset
                                                             //use bin packer

                BinPacker binPacker = new BinPacker(totalMaxLim, currentY);
                for (int i = itemList.Count - 1; i >= 0; --i)
                {
                    BitmapAtlasItemSource g       = itemList[i];
                    BinPackRect           newRect = binPacker.Insert(g.Width, g.Height);
                    g.Area = new Rectangle(newRect.X, newRect.Y, g.Width, g.Height);


                    //recalculate proper max midth again, after arrange and compact space
                    if (newRect.Right > totalImgWidth)
                    {
                        totalImgWidth = newRect.Right;
                    }
                }
            }
            // -------------------------------
            //4. create a mergeBmpBuffer
            //please note that original glyph image is head-down (Y axis)
            //so we will flip-Y axis again in step 5.
            int[] mergeBmpBuffer = new int[totalImgWidth * imgH];
            if (SpaceCompactOption == CompactOption.BinPack) //again here?
            {
                for (int i = itemList.Count - 1; i >= 0; --i)
                {
                    BitmapAtlasItemSource g = itemList[i];
                    //copy glyph image buffer to specific area of final result buffer
                    CopyToDest(g.GetImageBuffer(), g.Width, g.Height, mergeBmpBuffer, g.Area.Left, g.Area.Top, totalImgWidth);
                }
            }
            else
            {
                int itemCount = itemList.Count;
                for (int i = 0; i < itemCount; ++i)
                {
                    BitmapAtlasItemSource g = itemList[i];
                    //copy glyph image buffer to specific area of final result buffer

                    CopyToDest(g.GetImageBuffer(), g.Width, g.Height, mergeBmpBuffer, g.Area.Left, g.Area.Top, totalImgWidth);
                }
            }

            //5. since the mergeBmpBuffer is head-down
            //we will flipY axis again to head-up, the head-up img is easy to read and debug

            if (flipY)
            {
                int[] totalBufferFlipY = new int[mergeBmpBuffer.Length];
                int   srcRowIndex      = imgH - 1;
                int   strideInBytes    = totalImgWidth * 4;//32 argb

                for (int i = 0; i < imgH; ++i)
                {
                    //copy each row from src to dst
                    System.Buffer.BlockCopy(mergeBmpBuffer, strideInBytes * srcRowIndex, totalBufferFlipY, strideInBytes * i, strideInBytes);
                    srcRowIndex--;
                }

                //flipY on atlas info too
                for (int i = 0; i < itemList.Count; ++i)
                {
                    BitmapAtlasItemSource g    = itemList[i];
                    Rectangle             rect = g.Area;
                    g.Area = new Rectangle(rect.X, imgH - (rect.Y + rect.Height), rect.Width, rect.Height);
                }


                //***
                //6. generate final output
                //TODO: rename GlyphImage to another name to distinquist
                //between small glyph and a large one
                return(_latestResultBmp = PixelFarm.CpuBlit.MemBitmap.CreateFromCopy(totalImgWidth, imgH, totalBufferFlipY));
            }
            else
            {
                return(_latestResultBmp = PixelFarm.CpuBlit.MemBitmap.CreateFromCopy(totalImgWidth, imgH, mergeBmpBuffer));
            }
        }
 /// <summary>
 /// add or replace
 /// </summary>
 /// <param name="itemIndex"></param>
 /// <param name="img"></param>
 public void AddItemSource(BitmapAtlasItemSource img)
 {
     _items[img.UniqueInt16Name] = img;
 }
Esempio n. 5
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);
                    }
                }
            }
        }
Esempio n. 6
0
        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);
            }
        }
Esempio n. 7
0
        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);
                    }
                }
            }
        }