public bool AttemptResize(int width, int height, Textures.Group group, int extraBorder, bool pregms2_2_2) { Resize(width, height); foreach (var item in Items) { if (!Place(group, item.TextureItem, extraBorder, pregms2_2_2, out _, out _)) { return(false); } } return(true); }
// Assumes all texture items on this group are already set up as they're desired to be // (i.e., everything is already cropped and de-duplicated, which is out of the scope of this function) public static List <Page> Pack(Textures.Group group, GMData data, int maxTextureWidth = 2048, int maxTextureHeight = 2048) { List <Page> result = new List <Page>(); bool pregms2_2_2 = !data.VersionInfo.IsNumberAtLeast(2, 2, 2); int extraBorder = data.VersionInfo.IsNumberAtLeast(2) ? 1 : 2; // Sort entries by area, largest to smallest List <GMTextureItem> entries = group.Items.OrderByDescending((entry) => entry.SourceWidth * entry.SourceHeight).ToList(); // Place every texture item on pages in that order List <GMTextureItem> duplicates = new List <GMTextureItem>(64); foreach (var entry in entries) { if (entry._DuplicateOf != null) { duplicates.Add(entry); continue; // this is a duplicate of another entry; this will be figured out later } int x = -1, y = -1; Page targetPage = null; // Try placing on each page in order until we find one that the entry fits on foreach (Page page in result) { if (page.Place(group, entry, extraBorder, pregms2_2_2, out x, out y)) { targetPage = page; break; } } if (targetPage == null) { // Make a new page and place this entry on that one targetPage = new Page(maxTextureWidth, maxTextureHeight); result.Add(targetPage); targetPage.Place(group, entry, extraBorder, pregms2_2_2, out x, out y); } if (x == -1) { // Failed to pack! return(null); } entry._PackItem = new Page.Item(x, y, targetPage, entry); } // Now try to shrink each page foreach (var page in result) { int w = page.Width, h = page.Height; int originalWidth = w, originalHeight = h; while (w != 1 || h != 1) { if (page.AttemptResize(w / 2, h / 2, group, extraBorder, pregms2_2_2)) { // Successful, so halve the size w /= 2; h /= 2; } else { // Unsuccessful, try each direction, then stop if (page.AttemptResize(w / 2, h, group, extraBorder, pregms2_2_2)) { w /= 2; } else if (page.AttemptResize(w, h / 2, group, extraBorder, pregms2_2_2)) { h /= 2; } break; } } // We now have the final width/height page.Resize(w, h); if (w != originalWidth || h != originalHeight) { // ...and it changed, so now we have to pack again for real foreach (var item in page.Items) { page.Place(group, item.TextureItem, extraBorder, pregms2_2_2, out item.X, out item.Y); } } } // Now process the duplicates foreach (var entry in duplicates) { var target = entry._DuplicateOf; while (target._DuplicateOf != null) { target = target._DuplicateOf; } entry._PackItem = new Page.Item(target._PackItem.X, target._PackItem.Y, target._PackItem.TargetPage, entry); } return(result); }
public bool Place(Textures.Group group, GMTextureItem entry, int extraBorder, bool pregms2_2_2, out int x, out int y) { int gap = group.Border; if (entry._HasExtraBorder) { gap += extraBorder; } int width = entry.SourceWidth, height = entry.SourceHeight; bool addX = false, addY = false; if (pregms2_2_2) { // Before 2.2.2 if (width + (gap * 2) < Width) { width = (width + (gap * 2) + 3) & -4; addX = true; } if (height + (gap * 2) < Height) { height = (height + (gap * 2) + 3) & -4; addY = true; } } else { // After 2.2.2 if (width != Width) { width += gap * 2; addX = true; } if (height != Height) { height += gap * 2; addY = true; } } Rect rect = MainRect.FindSpace(width, height); if (rect != null) { MainRect.Place(rect, width, height, out x, out y); if (addX) { x += gap; } if (addY) { y += gap; } return(true); } x = -1; y = -1; return(false); }