private void ApplyEdgeProcessFast(TextureEdgeProcess edge, FastBitmap f, Bitmap s, Node n)
        {
            if (edge == TextureEdgeProcess.Expand1Px)
            {
                if (Padding < 2) // partL expand 1px to right, partR expand 1px to left, therefore 2px needed
                {
                    throw new ArgumentOutOfRangeException(nameof(edge), "Padding must > 2 when using Texture Edge Expand");
                }

                //source: pick a slice from part; dest: put slice to atlas
                //top
                f.CopyRegion(s, new Rectangle(0, 0, s.Width, 1),
                             new Rectangle(n.Bounds.X, n.Bounds.Y - 1, s.Width, 1));
                //down
                f.CopyRegion(s, new Rectangle(0, s.Height - 1, s.Width, 1),
                             new Rectangle(n.Bounds.X, n.Bounds.Y + n.Bounds.Height, s.Width, 1));
                //left
                f.CopyRegion(s, new Rectangle(0, 0, 1, s.Height),
                             new Rectangle(n.Bounds.X - 1, n.Bounds.Y, 1, s.Height));
                //right
                f.CopyRegion(s, new Rectangle(s.Width - 1, 0, 1, s.Height),
                             new Rectangle(n.Bounds.X + n.Bounds.Width, n.Bounds.Y, 1, s.Height));
            }
        }
        public Image ToImage(bool debugMode = false, TextureEdgeProcess edge = TextureEdgeProcess.None, Color?background = null)
        {
            var    bgColor = background ?? Color.FromArgb(0, Color.Black);
            Bitmap img     = new Bitmap(Width, Height, PixelFormat.Format32bppArgb);

            //avoid using Graphics
#if USE_FASTBITMAP
            if (!debugMode)
            {
                using (var f = img.FastLock())
                {
                    if (background != null)
                    {
                        f.Clear(bgColor);
                    }

                    foreach (Node n in Nodes)
                    {
                        if (n.Texture != null)
                        {
                            Image sourceImg = n.Texture.SourceImage ?? new Bitmap(n.Texture.Source);
                            if (!(sourceImg is Bitmap s))
                            {
                                s = new Bitmap(sourceImg);
                            }

                            f.CopyRegion(s, new Rectangle(0, 0, s.Width, s.Height), n.Bounds);

                            ApplyEdgeProcessFast(edge, f, s, n);
                        }
                    }
                }

                //img.Save("tex.png", ImageFormat.png);
                return(img);
            }
#endif

            Graphics g = Graphics.FromImage(img);
            g.Clear(bgColor);

            //g.PixelOffsetMode = PixelOffsetMode.Half;
            //g.InterpolationMode = InterpolationMode.Default;
            //g.SmoothingMode = SmoothingMode.None;

            //ImageAttributes attributes = new ImageAttributes();
            //attributes.SetWrapMode(WrapMode.TileFlipXY);

            if (debugMode)
            {
                g.FillRectangle(Brushes.Green, new Rectangle(0, 0, Width, Height));
            }

            foreach (Node n in Nodes)
            {
                if (n.Texture != null)
                {
                    Image sourceImg = n.Texture.SourceImage ?? new Bitmap(n.Texture.Source);
#if USE_FASTBITMAP
                    using (var f = img.FastLock())
                    {
                        if (!(sourceImg is Bitmap s))
                        {
                            s = new Bitmap(sourceImg);
                        }

                        f.CopyRegion(s, new Rectangle(0, 0, s.Width, s.Height), n.Bounds);

                        ApplyEdgeProcessFast(edge, f, s, n);
                    }
#else
                    g.DrawImage(sourceImg, n.Bounds);
                    //g.DrawImage(sourceImg, n.Bounds, 0,0, sourceImg.Width, sourceImg.Height, GraphicsUnit.Pixel, attributes);
#endif

                    if (debugMode)
                    {
                        string     label      = Path.GetFileNameWithoutExtension(n.Texture.Source);
                        SizeF      labelBox   = g.MeasureString(label, SystemFonts.MenuFont, new SizeF(n.Bounds.Size));
                        RectangleF rectBounds = new Rectangle(n.Bounds.Location,
                                                              new Size((int)labelBox.Width, (int)labelBox.Height));
                        g.FillRectangle(Brushes.Black, rectBounds);
                        g.DrawString(label, SystemFonts.MenuFont, Brushes.White, rectBounds);
                    }
                }
                else
                {
                    g.FillRectangle(Brushes.DarkMagenta, n.Bounds);

                    if (debugMode)
                    {
                        string     label      = n.Bounds.Width + "x" + n.Bounds.Height;
                        SizeF      labelBox   = g.MeasureString(label, SystemFonts.MenuFont, new SizeF(n.Bounds.Size));
                        RectangleF rectBounds = new Rectangle(n.Bounds.Location,
                                                              new Size((int)labelBox.Width, (int)labelBox.Height));
                        g.FillRectangle(Brushes.Black, rectBounds);
                        g.DrawString(label, SystemFonts.MenuFont, Brushes.White, rectBounds);
                    }
                }
            }

            return(img);
        }