Пример #1
0
        bool SplitFreeNode(BinPackRect freeNode, BinPackRect usedNode)
        {
            // test if the rects even intersect
            bool insideX = usedNode.X <freeNode.Right && usedNode.Right> freeNode.X;
            bool insideY = usedNode.Y <freeNode.Bottom && usedNode.Bottom> freeNode.Y;

            if (!insideX || !insideY)
            {
                return(false);
            }

            if (insideX)
            {
                // new node at the top side of the used node
                if (usedNode.Y > freeNode.Y && usedNode.Y < freeNode.Bottom)
                {
                    BinPackRect newNode = freeNode;
                    newNode.Height = usedNode.Y - newNode.Y;
                    freeList.Add(newNode);
                }

                // new node at the bottom side of the used node
                if (usedNode.Bottom < freeNode.Bottom)
                {
                    BinPackRect newNode = freeNode;
                    newNode.Y      = usedNode.Bottom;
                    newNode.Height = freeNode.Bottom - usedNode.Bottom;
                    freeList.Add(newNode);
                }
            }

            if (insideY)
            {
                // new node at the left side of the used node
                if (usedNode.X > freeNode.X && usedNode.X < freeNode.Right)
                {
                    BinPackRect newNode = freeNode;
                    newNode.Width = usedNode.X - newNode.X;
                    freeList.Add(newNode);
                }

                // new node at the right side of the used node
                if (usedNode.Right < freeNode.Right)
                {
                    BinPackRect newNode = freeNode;
                    newNode.X     = usedNode.Right;
                    newNode.Width = freeNode.Right - usedNode.Right;
                    freeList.Add(newNode);
                }
            }

            return(true);
        }
Пример #2
0
        public BinPackRect Insert(int width, int height)
        {
            var bestNode     = new BinPackRect();
            int bestShortFit = int.MaxValue;
            int bestLongFit  = int.MaxValue;

            int count = freeList.Count;

            for (int i = 0; i < count; i++)
            {
                // try to place the rect
                BinPackRect rect = freeList[i];
                if (rect.Width < width || rect.Height < height)
                {
                    continue;
                }

                int leftoverX = Math.Abs(rect.Width - width);
                int leftoverY = Math.Abs(rect.Height - height);
                int shortFit  = Math.Min(leftoverX, leftoverY);
                int longFit   = Math.Max(leftoverX, leftoverY);

                if (shortFit < bestShortFit || (shortFit == bestShortFit && longFit < bestLongFit))
                {
                    bestNode     = new BinPackRect(rect.X, rect.Y, width, height);
                    bestShortFit = shortFit;
                    bestLongFit  = longFit;
                }
            }

            if (bestNode.Height == 0)
            {
                return(bestNode);
            }

            // split out free areas into smaller ones
            for (int i = 0; i < count; i++)
            {
                if (SplitFreeNode(freeList[i], bestNode))
                {
                    freeList.RemoveAt(i);
                    i--;
                    count--;
                }
            }

            // prune the freelist
            for (int i = 0; i < freeList.Count; i++)
            {
                for (int j = i + 1; j < freeList.Count; j++)
                {
                    BinPackRect idata = freeList[i];
                    BinPackRect jdata = freeList[j];
                    if (jdata.Contains(idata))
                    {
                        freeList.RemoveAt(i);
                        i--;
                        break;
                    }

                    if (idata.Contains(jdata))
                    {
                        freeList.RemoveAt(j);
                        j--;
                    }
                }
            }

            return(bestNode);
        }
Пример #3
0
 public bool Contains(BinPackRect rect)
 {
     return(rect.X >= X && rect.Y >= Y &&
            rect.Right <= Right && rect.Bottom <= Bottom);
 }
        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));
            }
        }