Ejemplo n.º 1
0
 public Element(Bitmap sheet, Bitmap source, Rectangle bounds, Image.Transform appliedTransform)
 {
     Sheet            = sheet;
     Source           = source;
     Bounds           = bounds;
     AppliedTransform = appliedTransform;
 }
Ejemplo n.º 2
0
        public static BitmapSheet Create(IEnumerable <Bitmap> input, int maxWidth, int maxHeight, int clusterWidth, int clusterHeight, Options options)
        {
            if (input == null)
            {
                throw new ArgumentNullException("input");
            }
            if (maxWidth <= 0)
            {
                throw new ArgumentException("maxWidth");
            }
            if (maxHeight <= 0)
            {
                throw new ArgumentException("maxHeight");
            }

            #region maxWidth/maxHeight를 2의 멱수로 맞춥니다.
            if (options.HasFlag(Options.PowerOfTwoSize))
            {
                // maxWidth/maxHeight보다 작은 2의 멱수로 맞춥니다.
                int mw = MathExtension.GetNearestPowerOfTwo(maxWidth);
                int mh = MathExtension.GetNearestPowerOfTwo(maxHeight);

                if (maxWidth != mw)
                {
                    maxWidth = mw / 2;
                }
                if (maxHeight != mh)
                {
                    maxHeight = mh / 2;
                }
            }
            #endregion

            foreach (Bitmap item in input)
            {
                if (item.Width >= maxWidth || item.Height >= maxHeight)
                {
                    throw new ArgumentException("input");
                }
            }

            // Bitmap들을 크기가 큰 순으로 정렬합니다.
            var sortedInput = new List <Bitmap>(input);
            sortedInput.Sort((a, b) =>
            {
                // 가로와 세로를 각각 제곱을 하면서 한 방향이 지나치게 클 경우 큰 이미지로 인식하게 합니다.
                int aWeight = (a.Width * a.Width) + (a.Height * a.Height);
                int bWeight = (b.Width * b.Width) + (b.Height * b.Height);
                return(bWeight.CompareTo(aWeight));
            });

            var items          = new List <Tuple <int, Bitmap, Rectangle, Image.Transform> >();
            int margin         = 2;
            var textureAtlases = new List <RectangleStorage>();
            #region 각각의 Bitmap들을 어떤 큰 Bitmap에 배치할지 결정합니다. (선작업)
            foreach (Bitmap item in sortedInput)
            {
                // 실제로는 여백을 추가한 크기로 할당합니다.
                int aw = margin + item.Width + margin;
                int ah = margin + item.Height + margin;

                RectangleStorage atlas = null;

                int index = -1;
                if (options.HasFlag(Options.RotatableMerging))
                {
                    // 병합할 때 변환 가능 Option이 설정되어 있으면 가로 세로를 바꾼 영역도 검색해봅니다.
                    index = textureAtlases.FindIndex((ta) => ta.CanAllocate(aw, ah) || ta.CanAllocate(ah, aw));
                }
                else
                {
                    index = textureAtlases.FindIndex((ta) => ta.CanAllocate(aw, ah));
                }

                if (index != -1)
                {
                    atlas = textureAtlases[index];
                }
                else
                {
                    atlas = new RectangleStorage(maxWidth, maxHeight, clusterWidth, clusterHeight);
                    index = textureAtlases.Count;
                    textureAtlases.Add(atlas);
                }

                Rectangle       rectangle;
                Image.Transform appliedTransform;
                if (options.HasFlag(Options.RotatableMerging))
                {
                    if (aw * 4 < ah) // 세로 길이가 지나치게 길면 눕히고나서 붙여봅니다.
                    {
                        rectangle        = atlas.Allocate(ah, aw);
                        appliedTransform = Image.Transform.RotateCW90;
                        if (rectangle.IsEmpty)
                        {
                            rectangle        = atlas.Allocate(aw, ah);
                            appliedTransform = Image.Transform.Identity;
                        }
                    }
                    else
                    {
                        rectangle        = atlas.Allocate(aw, ah);
                        appliedTransform = Image.Transform.Identity;
                        if (rectangle.IsEmpty)
                        {
                            rectangle        = atlas.Allocate(ah, aw);
                            appliedTransform = Image.Transform.RotateCW90;
                        }
                    }
                }
                else
                {
                    rectangle        = atlas.Allocate(aw, ah);
                    appliedTransform = Image.Transform.Identity;
                    Trace.Assert(!rectangle.IsEmpty);
                }


                Rectangle bitmapRectangle = Rectangle.FromLTRB(rectangle.Left + margin,
                                                               rectangle.Top + margin,
                                                               rectangle.Right - margin,
                                                               rectangle.Bottom - margin);
                items.Add(Tuple.Create(index, item, bitmapRectangle, appliedTransform));
            }
            #endregion

            var graphics     = new GDIGraphics[textureAtlases.Count];
            var sheetBitmaps = new Bitmap[textureAtlases.Count];
            var elements     = new List <Element>();
            #region 입력된 작은 Bitmap들을 큰 SheetBitmap에 복사합니다.
            foreach (var item in items)
            {
                int             sheetIndex       = item.Item1;
                Bitmap          bitmap           = item.Item2;
                Rectangle       rectangle        = item.Item3;
                Image.Transform appliedTransform = item.Item4;

                if (sheetBitmaps[sheetIndex] == null)
                {
                    Rectangle bounds = textureAtlases[item.Item1].GetBounds();
                    Debug.Assert(bounds.X == 0 && bounds.Y == 0);

                    int w = bounds.Width;
                    int h = bounds.Height;
                    if (options.HasFlag(Options.PowerOfTwoSize))
                    {
                        w = MathExtension.GetNearestPowerOfTwo(w);
                        h = MathExtension.GetNearestPowerOfTwo(h);
                    }

                    sheetBitmaps[sheetIndex] = new Bitmap(w, h);
                    graphics[sheetIndex]     = GDIGraphics.FromImage(sheetBitmaps[sheetIndex]);
                    graphics[sheetIndex].Clear(Color.Transparent);
                }

                switch (appliedTransform)
                {
                case Image.Transform.Identity:
                {
                    graphics[sheetIndex].DrawImageUnscaled(bitmap, rectangle.X, rectangle.Y);

                    graphics[sheetIndex].DrawImage(bitmap, rectangle.Left - 1, rectangle.Top, new Rectangle(0, 0, 1, rectangle.Height), GraphicsUnit.Pixel);
                    graphics[sheetIndex].DrawImage(bitmap, rectangle.Right, rectangle.Top, new Rectangle(bitmap.Width - 1, 0, 1, rectangle.Height), GraphicsUnit.Pixel);
                    graphics[sheetIndex].DrawImage(bitmap, rectangle.Left, rectangle.Top - 1, new Rectangle(0, 0, rectangle.Width, 1), GraphicsUnit.Pixel);
                    graphics[sheetIndex].DrawImage(bitmap, rectangle.Left, rectangle.Bottom, new Rectangle(0, bitmap.Height - 1, rectangle.Width, 1), GraphicsUnit.Pixel);

                    var sheetBitmap = sheetBitmaps[sheetIndex];
                    sheetBitmap.SetPixel(rectangle.Left - 1, rectangle.Top - 1, sheetBitmap.GetPixel(rectangle.Left, rectangle.Top));
                    sheetBitmap.SetPixel(rectangle.Right + 0, rectangle.Top - 1, sheetBitmap.GetPixel(rectangle.Right - 1, rectangle.Top));
                    sheetBitmap.SetPixel(rectangle.Left - 1, rectangle.Bottom + 0, sheetBitmap.GetPixel(rectangle.Left, rectangle.Bottom - 1));
                    sheetBitmap.SetPixel(rectangle.Right + 0, rectangle.Bottom + 0, sheetBitmap.GetPixel(rectangle.Right - 1, rectangle.Bottom - 1));
                }
                break;

                case Image.Transform.RotateCW90:
                {
                    graphics[sheetIndex].TranslateTransform(+rectangle.X, +rectangle.Y);
                    graphics[sheetIndex].RotateTransform(90.0f);
                    graphics[sheetIndex].TranslateTransform(-rectangle.X, -rectangle.Y - rectangle.Width + 1);

                    // 임시
                    //graphics[sheetIndex].DrawImageUnscaled(bitmap, rectangle.X + 0, rectangle.Y - 2);
                    //graphics[sheetIndex].DrawImageUnscaled(bitmap, rectangle.X - 2, rectangle.Y + 0);
                    //graphics[sheetIndex].DrawImageUnscaled(bitmap, rectangle.X + 2, rectangle.Y + 0);
                    //graphics[sheetIndex].DrawImageUnscaled(bitmap, rectangle.X + 0, rectangle.Y + 2);

                    //graphics[sheetIndex].FillRectangle(Brushes.White, new Rectangle(rectangle.X, rectangle.Y, bitmap.Width, bitmap.Height));
                    graphics[sheetIndex].DrawImageUnscaled(bitmap, rectangle.X, rectangle.Y);

                    graphics[sheetIndex].DrawImage(bitmap, rectangle.Left - 1, rectangle.Top, new Rectangle(0, 0, 1, rectangle.Height), GraphicsUnit.Pixel);
                    graphics[sheetIndex].DrawImage(bitmap, rectangle.Right, rectangle.Top, new Rectangle(bitmap.Width - 1, 0, 1, rectangle.Height), GraphicsUnit.Pixel);
                    graphics[sheetIndex].DrawImage(bitmap, rectangle.Left, rectangle.Top - 1, new Rectangle(0, 0, rectangle.Width, 1), GraphicsUnit.Pixel);
                    graphics[sheetIndex].DrawImage(bitmap, rectangle.Left, rectangle.Bottom, new Rectangle(0, bitmap.Height - 1, rectangle.Width, 1), GraphicsUnit.Pixel);

                    var sheetBitmap = sheetBitmaps[sheetIndex];
                    sheetBitmap.SetPixel(rectangle.Left - 1, rectangle.Top - 1, sheetBitmap.GetPixel(rectangle.Left, rectangle.Top));
                    sheetBitmap.SetPixel(rectangle.Right + 0, rectangle.Top - 1, sheetBitmap.GetPixel(rectangle.Right - 1, rectangle.Top));
                    sheetBitmap.SetPixel(rectangle.Left - 1, rectangle.Bottom + 0, sheetBitmap.GetPixel(rectangle.Left, rectangle.Bottom - 1));
                    sheetBitmap.SetPixel(rectangle.Right + 0, rectangle.Bottom + 0, sheetBitmap.GetPixel(rectangle.Right - 1, rectangle.Bottom - 1));

                    graphics[sheetIndex].ResetTransform();
                }
                break;

                default:
                    throw new NotSupportedException();
                }

                elements.Add(new Element(sheetBitmaps[sheetIndex], bitmap, rectangle, appliedTransform));
            }
            #endregion

            #region 생성한 Graphics 객체를 Dispose합니다.
            foreach (var item in graphics)
            {
                if (item != null)
                {
                    item.Dispose();
                }
            }
            #endregion

            return(new BitmapSheet(sheetBitmaps, elements));
        }