diff --git a/src/main/java/net/hoosiertransfer/Argon/stitcher/HolderSlot.java b/src/main/java/net/hoosiertransfer/Argon/stitcher/HolderSlot.java new file mode 100644 index 0000000..3767e3b --- /dev/null +++ b/src/main/java/net/hoosiertransfer/Argon/stitcher/HolderSlot.java @@ -0,0 +1,27 @@ +package net.hoosiertransfer.Argon.stitcher; + +import java.util.Collections; +import java.util.List; + +import net.minecraft.client.renderer.texture.Stitcher; + +public class HolderSlot extends SpriteSlot { + private final Stitcher.Holder holder; + + public HolderSlot(Stitcher.Holder holder) { + this.holder = holder; + width = holder.getWidth(); + height = holder.getHeight(); + } + + public Stitcher.Holder getHolder() { + return holder; + } + + @Override + public List getSlots(Rect2D parent) { + Stitcher.Slot slot = new Stitcher.Slot(x + parent.x, y + parent.y, width, height); + slot.insertHolder(holder); + return Collections.singletonList(slot); + } +} diff --git a/src/main/java/net/hoosiertransfer/Argon/stitcher/Rect2D.java b/src/main/java/net/hoosiertransfer/Argon/stitcher/Rect2D.java new file mode 100644 index 0000000..c8489e0 --- /dev/null +++ b/src/main/java/net/hoosiertransfer/Argon/stitcher/Rect2D.java @@ -0,0 +1,26 @@ +package net.hoosiertransfer.Argon.stitcher; + +public class Rect2D implements Comparable { + public int x; + public int y; + public int width; + public int height; + + public Rect2D() { + } + + public Rect2D(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + @Override + public int compareTo(Rect2D o) { + int ourArea = width * height; + int theirArea = o.width * o.height; + // will make sure that larger areas go first + return theirArea - ourArea; + } +} diff --git a/src/main/java/net/hoosiertransfer/Argon/stitcher/SpriteSlot.java b/src/main/java/net/hoosiertransfer/Argon/stitcher/SpriteSlot.java new file mode 100644 index 0000000..925d786 --- /dev/null +++ b/src/main/java/net/hoosiertransfer/Argon/stitcher/SpriteSlot.java @@ -0,0 +1,9 @@ +package net.hoosiertransfer.Argon.stitcher; + +import net.minecraft.client.renderer.texture.Stitcher; + +import java.util.List; + +public abstract class SpriteSlot extends Rect2D { + public abstract List getSlots(Rect2D parent); +} diff --git a/src/main/java/net/hoosiertransfer/Argon/stitcher/StitcherState.java b/src/main/java/net/hoosiertransfer/Argon/stitcher/StitcherState.java new file mode 100644 index 0000000..4c00e3a --- /dev/null +++ b/src/main/java/net/hoosiertransfer/Argon/stitcher/StitcherState.java @@ -0,0 +1,6 @@ +package net.hoosiertransfer.Argon.stitcher; + +public enum StitcherState { + SETUP, + STITCHED, +} diff --git a/src/main/java/net/hoosiertransfer/Argon/stitcher/TooBigException.java b/src/main/java/net/hoosiertransfer/Argon/stitcher/TooBigException.java new file mode 100644 index 0000000..e65f7c3 --- /dev/null +++ b/src/main/java/net/hoosiertransfer/Argon/stitcher/TooBigException.java @@ -0,0 +1,4 @@ +package net.hoosiertransfer.Argon.stitcher; + +public class TooBigException extends Exception { +} diff --git a/src/main/java/net/hoosiertransfer/Argon/stitcher/TurboStitcher.java b/src/main/java/net/hoosiertransfer/Argon/stitcher/TurboStitcher.java new file mode 100644 index 0000000..d6a329d --- /dev/null +++ b/src/main/java/net/hoosiertransfer/Argon/stitcher/TurboStitcher.java @@ -0,0 +1,177 @@ +package net.hoosiertransfer.Argon.stitcher; + +import java.util.*; + +import com.google.common.collect.ImmutableSet; + +import net.hoosiertransfer.Argon.stitcher.packing2d.Algorithm; +import net.hoosiertransfer.Argon.stitcher.packing2d.Packer; +import net.minecraft.client.renderer.texture.Stitcher; +import net.minecraft.util.ResourceLocation; + +public class TurboStitcher extends SpriteSlot { + private final int maxWidth; + private final int maxHeight; + private final boolean forcePowerOf2; + private List slots = new LinkedList<>(); + private List finalizedSlots = null; + private boolean needsSorting = false; + private int trackedArea = 0; + private StitcherState state = StitcherState.SETUP; + private static final boolean OPTIMAL_PACKING = true; + + public TurboStitcher(int maxWidth, int maxHeight, boolean forcePowerOf2) { + this.maxHeight = maxHeight; + this.maxWidth = maxWidth; + this.forcePowerOf2 = forcePowerOf2; + } + + private static int nextPowerOfTwo(int number) { + number--; + number |= number >>> 1; + number |= number >>> 2; + number |= number >>> 4; + number |= number >>> 8; + number |= number >>> 16; + number++; + return number; + } + + public void addSprite(Stitcher.Holder holder) { + addSprite(new HolderSlot(holder)); + } + + public void addSprite(SpriteSlot rect) { + verifyState(StitcherState.SETUP); + slots.add(rect); + trackedArea += rect.width * rect.height; + needsSorting = true; + } + + public void reset() { + state = StitcherState.SETUP; + } + + public void dropFirst() { + verifyState(StitcherState.SETUP); + if (slots.size() > 0) { + SpriteSlot slot = slots.remove(0); + String name; + if (slot instanceof HolderSlot) { + name = ((HolderSlot) slot).getHolder().getAtlasSprite().getIconName(); + } else { + name = "unknown"; + } + trackedArea -= slot.width * slot.height; + } else + throw new IllegalStateException(); + } + + public void retainAllSprites(Set theLocations) { + verifyState(StitcherState.SETUP); + Set locationStrings = new HashSet<>(); + for (ResourceLocation rl : theLocations) + locationStrings.add(rl.toString()); + slots.removeIf(slot -> { + if (slot instanceof HolderSlot) { + String spriteName = ((HolderSlot) slot).getHolder().getAtlasSprite().getIconName(); + // drop textures that are: + // - not in the desired list of locations + if (!locationStrings.contains(spriteName)) { + trackedArea -= slot.width * slot.height; + return true; + } + } + return false; + }); + } + + public void stitch() throws TooBigException { + verifyState(StitcherState.SETUP); + width = 0; + height = 0; + if (slots.size() == 0) { + state = StitcherState.STITCHED; + return; + } + // ensure we have largest sprites first + if (needsSorting) { + Collections.sort(slots); + needsSorting = false; + } + if (trackedArea > (maxWidth * maxHeight)) { + throw new TooBigException(); + } + // start with a really simple check, if the total area is larger than we could + // handle, we know this will fail + for (SpriteSlot slot : slots) { + width = Math.max(width, slot.width); + } + if (forcePowerOf2 || !OPTIMAL_PACKING) { + width = nextPowerOfTwo(width); + } + if (width > maxWidth) { + throw new TooBigException(); + } + width = Math.max(width >>> 1, 1); + List packedSlots; + List toPack = new ArrayList<>(slots); + do { + if (width == maxWidth) { + throw new TooBigException(); + } + if (forcePowerOf2 || !OPTIMAL_PACKING) { + width *= 2; + } else { + width += Math.min(width, 16); + } + if (width > maxWidth) { + width = maxWidth; + } + packedSlots = Packer.pack(toPack, Algorithm.FIRST_FIT_DECREASING_HEIGHT, width); + height = 0; + for (SpriteSlot sprite : packedSlots) { + height = Math.max(height, sprite.y + sprite.height); + } + if (forcePowerOf2) { + height = nextPowerOfTwo(height); + } + } while (height > maxHeight || height > width); + finalizedSlots = packedSlots; + state = StitcherState.STITCHED; + // TextureCollector.weaklyCollectedTextures = ImmutableSet.of(); + } + + public List getSlots() { + return getSlots(new Rect2D()); + } + + public List getSlots(Rect2D parent) { + verifyState(StitcherState.STITCHED); + ArrayList mineSlots = new ArrayList(); + Rect2D offset = new Rect2D(x + parent.x, y + parent.y, width, height); + for (SpriteSlot slot : finalizedSlots) { + mineSlots.addAll(slot.getSlots(offset)); + } + /* + * for(Stitcher.Slot slot : mineSlots) { + * System.out.println(slot.getStitchHolder().getAtlasSprite().getIconName()); + * } + * + */ + return mineSlots; + } + + private void verifyState(StitcherState... allowedStates) { + boolean ok = false; + for (StitcherState state : allowedStates) { + if (state == this.state) { + ok = true; + break; + } + } + if (!ok) { + throw new IllegalStateException("Cold not execute operation: invalid state"); + } + } +} diff --git a/src/main/java/net/hoosiertransfer/Argon/stitcher/WrongDimensionException.java b/src/main/java/net/hoosiertransfer/Argon/stitcher/WrongDimensionException.java new file mode 100644 index 0000000..7ea9b00 --- /dev/null +++ b/src/main/java/net/hoosiertransfer/Argon/stitcher/WrongDimensionException.java @@ -0,0 +1,5 @@ +package net.hoosiertransfer.Argon.stitcher; + +public class WrongDimensionException extends RuntimeException { + public static WrongDimensionException INSTANCE = new WrongDimensionException(); +} diff --git a/src/main/java/net/hoosiertransfer/Argon/stitcher/packing2d/Algorithm.java b/src/main/java/net/hoosiertransfer/Argon/stitcher/packing2d/Algorithm.java new file mode 100644 index 0000000..2b705f4 --- /dev/null +++ b/src/main/java/net/hoosiertransfer/Argon/stitcher/packing2d/Algorithm.java @@ -0,0 +1,6 @@ +package net.hoosiertransfer.Argon.stitcher.packing2d; + +public enum Algorithm { + FIRST_FIT_DECREASING_HEIGHT, + BEST_FIT_DECREASING_HEIGHT +} \ No newline at end of file diff --git a/src/main/java/net/hoosiertransfer/Argon/stitcher/packing2d/Packer.java b/src/main/java/net/hoosiertransfer/Argon/stitcher/packing2d/Packer.java new file mode 100644 index 0000000..d2e786a --- /dev/null +++ b/src/main/java/net/hoosiertransfer/Argon/stitcher/packing2d/Packer.java @@ -0,0 +1,38 @@ +package net.hoosiertransfer.Argon.stitcher.packing2d; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import net.hoosiertransfer.Argon.stitcher.Rect2D; + +public abstract class Packer { + final int stripWidth; + final List rectangles; + + Packer(int stripWidth, List rectangles) { + this.stripWidth = stripWidth; + this.rectangles = rectangles; + } + + public static List pack(List rectangles, Algorithm algorithm, int stripWidth) { + Packer packer; + switch (algorithm) { + case FIRST_FIT_DECREASING_HEIGHT: + packer = new PackerFFDH<>(stripWidth, rectangles); + return packer.pack(); + case BEST_FIT_DECREASING_HEIGHT: + packer = new PackerBFDH<>(stripWidth, rectangles); + break; + default: + return new ArrayList<>(); + } + return packer.pack(); + } + + public abstract List pack(); + + void sortByNonIncreasingHeight(List rectangles) { + rectangles.sort(Comparator.comparingInt((rect) -> rect.height).reversed()); + } +} diff --git a/src/main/java/net/hoosiertransfer/Argon/stitcher/packing2d/PackerBFDH.java b/src/main/java/net/hoosiertransfer/Argon/stitcher/packing2d/PackerBFDH.java new file mode 100644 index 0000000..aceb654 --- /dev/null +++ b/src/main/java/net/hoosiertransfer/Argon/stitcher/packing2d/PackerBFDH.java @@ -0,0 +1,48 @@ +package net.hoosiertransfer.Argon.stitcher.packing2d; + +import java.util.ArrayList; +import java.util.List; + +import net.hoosiertransfer.Argon.stitcher.Rect2D; + +public class PackerBFDH extends Packer { + private final List levels; + + public PackerBFDH(int stripWidth, List rectangles) { + super(stripWidth, rectangles); + levels = new ArrayList<>(); + } + + @Override + public List pack() { + int top = 0; + sortByNonIncreasingHeight(rectangles); + for (T r : rectangles) { + StripLevel levelWithSmallestResidual = null; + for (StripLevel level : levels) { + if (!level.canFit(r)) { + continue; + } + if (levelWithSmallestResidual != null && + levelWithSmallestResidual.availableWidth() > level.availableWidth()) { + levelWithSmallestResidual = level; + } else if (levelWithSmallestResidual == null) { + levelWithSmallestResidual = level; + } + } + if (levelWithSmallestResidual == null) { + StripLevel level = new StripLevel(stripWidth, top); + level.fitRectangle(r); + levels.add(level); + top += r.height; + } else { + StripLevel newLevel = levelWithSmallestResidual.fitRectangle(r); + if (newLevel != null) { + levels.add(levels.indexOf(levelWithSmallestResidual) + 1, newLevel); + } + } + + } + return rectangles; + } +} diff --git a/src/main/java/net/hoosiertransfer/Argon/stitcher/packing2d/PackerFFDH.java b/src/main/java/net/hoosiertransfer/Argon/stitcher/packing2d/PackerFFDH.java new file mode 100644 index 0000000..57aee34 --- /dev/null +++ b/src/main/java/net/hoosiertransfer/Argon/stitcher/packing2d/PackerFFDH.java @@ -0,0 +1,43 @@ +package net.hoosiertransfer.Argon.stitcher.packing2d; + +import java.util.ArrayList; +import java.util.List; + +import net.hoosiertransfer.Argon.stitcher.Rect2D; + +class PackerFFDH extends Packer { + private final List levels = new ArrayList<>(1); + private int top = 0; + + public PackerFFDH(int stripWidth, List rectangles) { + super(stripWidth, rectangles); + } + + @Override + public List pack() { + sortByNonIncreasingHeight(rectangles); + for (T r : rectangles) { + boolean fitsOnALevel = false; + for (int i = 0; i < levels.size(); i++) { + StripLevel level = levels.get(i); + fitsOnALevel = level.checkFitRectangle(r); + if (!fitsOnALevel) { + continue; + } + StripLevel newStrip = level.fitRectangle(r); + if (newStrip != null) { + levels.add(0, newStrip); + } + break; + } + if (fitsOnALevel) { + continue; + } + StripLevel level = new StripLevel(stripWidth, top); + level.fitRectangle(r); + levels.add(level); + top += r.height; + } + return rectangles; + } +} \ No newline at end of file diff --git a/src/main/java/net/hoosiertransfer/Argon/stitcher/packing2d/StripLevel.java b/src/main/java/net/hoosiertransfer/Argon/stitcher/packing2d/StripLevel.java new file mode 100644 index 0000000..ec4f971 --- /dev/null +++ b/src/main/java/net/hoosiertransfer/Argon/stitcher/packing2d/StripLevel.java @@ -0,0 +1,51 @@ +package net.hoosiertransfer.Argon.stitcher.packing2d; + +import net.hoosiertransfer.Argon.stitcher.Rect2D; + +class StripLevel { + private final int width; + private final int top; + private int availableWidth; + private int tallest = -1; + + StripLevel(int width, int top) { + this.width = width; + this.availableWidth = width; + this.top = top; + } + + boolean checkFitRectangle(Rect2D r) { + return (tallest < 0 || r.height <= tallest) && r.width <= availableWidth; + } + + StripLevel fitRectangle(Rect2D r) { + if (tallest >= 0 && r.height > tallest) { + return null; + } + StripLevel newStrip = null; + int leftOver = availableWidth - r.width; + if (leftOver >= 0) { + r.x = width - availableWidth; + r.y = top; + if (tallest == -1) { + tallest = r.height; + } + if (r.height < tallest) { + newStrip = new StripLevel(width, top + r.height); + newStrip.availableWidth = availableWidth; + newStrip.tallest = tallest - r.height; + tallest = r.height; + } + availableWidth = leftOver; + } + return newStrip; + } + + int availableWidth() { + return availableWidth; + } + + boolean canFit(Rect2D r) { + return availableWidth - r.width >= 0 && (tallest < 0 || r.height <= tallest); + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java index ef9f92a..e6d2bf5 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java @@ -26,14 +26,21 @@ import net.minecraft.util.ResourceLocation; /** * Copyright (c) 2022 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -151,7 +158,7 @@ public class EaglerTextureAtlasSprite { } public void updateAnimation(IFramebufferGL[] copyColorFramebuffer) { - if(animationCache == null) { + if (animationCache == null) { throw new IllegalStateException("Animation cache for '" + this.iconName + "' was never baked!"); } ++this.tickCounter; @@ -163,16 +170,19 @@ public class EaglerTextureAtlasSprite { this.tickCounter = 0; int k = this.animationMetadata.getFrameIndex(this.frameCounter); if (i != k && k >= 0 && k < this.framesTextureData.size()) { - animationCache.copyFrameLevelsToTex2D(k, this.originX, this.originY, this.width, this.height, copyColorFramebuffer); + animationCache.copyFrameLevelsToTex2D(k, this.originX, this.originY, this.width, this.height, + copyColorFramebuffer); } } else if (this.animationMetadata.isInterpolate()) { - float f = 1.0f - (float) this.tickCounter / (float) this.animationMetadata.getFrameTimeSingle(this.frameCounter); + float f = 1.0f + - (float) this.tickCounter / (float) this.animationMetadata.getFrameTimeSingle(this.frameCounter); int i = this.animationMetadata.getFrameIndex(this.frameCounter); int j = this.animationMetadata.getFrameCount() == 0 ? this.framesTextureData.size() : this.animationMetadata.getFrameCount(); int k = this.animationMetadata.getFrameIndex((this.frameCounter + 1) % j); if (i != k && k >= 0 && k < this.framesTextureData.size()) { - animationCache.copyInterpolatedFrameLevelsToTex2D(i, k, f, this.originX, this.originY, this.width, this.height, copyColorFramebuffer); + animationCache.copyInterpolatedFrameLevelsToTex2D(i, k, f, this.originX, this.originY, this.width, + this.height, copyColorFramebuffer); } } } @@ -295,9 +305,9 @@ public class EaglerTextureAtlasSprite { } public void bakeAnimationCache() { - if(animationMetadata != null) { + if (animationMetadata != null) { int mipLevels = framesTextureData.get(0).length; - if(animationCache == null) { + if (animationCache == null) { animationCache = new TextureAnimationCache(width, height, mipLevels); } animationCache.initialize(framesTextureData); @@ -328,7 +338,7 @@ public class EaglerTextureAtlasSprite { public void clearFramesTextureData() { this.framesTextureData.clear(); - if(this.animationCache != null) { + if (this.animationCache != null) { this.animationCache.free(); this.animationCache = null; } @@ -347,7 +357,7 @@ public class EaglerTextureAtlasSprite { this.setFramesTextureData(Lists.newArrayList()); this.frameCounter = 0; this.tickCounter = 0; - if(this.animationCache != null) { + if (this.animationCache != null) { this.animationCache.free(); this.animationCache = null; } @@ -365,16 +375,17 @@ public class EaglerTextureAtlasSprite { Throwable t = new UnsupportedOperationException("PBR is not enabled"); try { throw t; - }catch(Throwable tt) { + } catch (Throwable tt) { logger.error(t); } } - public void updateAnimationPBR(IFramebufferGL[] copyColorFramebuffer, IFramebufferGL[] copyMaterialFramebuffer, int materialTexOffset) { + public void updateAnimationPBR(IFramebufferGL[] copyColorFramebuffer, IFramebufferGL[] copyMaterialFramebuffer, + int materialTexOffset) { Throwable t = new UnsupportedOperationException("PBR is not enabled"); try { throw t; - }catch(Throwable tt) { + } catch (Throwable tt) { logger.error(t); } } diff --git a/src/main/java/net/minecraft/client/renderer/texture/Stitcher.java b/src/main/java/net/minecraft/client/renderer/texture/Stitcher.java index 555733f..e8f0633 100644 --- a/src/main/java/net/minecraft/client/renderer/texture/Stitcher.java +++ b/src/main/java/net/minecraft/client/renderer/texture/Stitcher.java @@ -1,11 +1,11 @@ package net.minecraft.client.renderer.texture; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Set; -import net.lax1dude.eaglercraft.v1_8.HString; +import net.hoosiertransfer.Argon.stitcher.TooBigException; +import net.hoosiertransfer.Argon.stitcher.TurboStitcher; import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; import com.google.common.collect.Lists; @@ -13,6 +13,7 @@ import com.google.common.collect.Sets; import net.minecraft.client.renderer.StitcherException; import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; /** * + @@ -54,12 +55,16 @@ public class Stitcher { private final boolean forcePowerOf2; private final int maxTileDimension; + private TurboStitcher masterStitcher; + private List holdersToReadd = new ArrayList<>(); + public Stitcher(int maxTextureWidth, int maxTextureHeight, boolean parFlag, int parInt1, int mipmapLevel) { this.mipmapLevelStitcher = mipmapLevel; this.maxWidth = maxTextureWidth; this.maxHeight = maxTextureHeight; this.forcePowerOf2 = parFlag; this.maxTileDimension = parInt1; + masterStitcher = new TurboStitcher(maxTextureWidth, maxTextureHeight, true); } public int getCurrentWidth() { @@ -75,31 +80,32 @@ public class Stitcher { if (this.maxTileDimension > 0) { stitcher$holder.setNewDimension(this.maxTileDimension); } - + masterStitcher.addSprite(stitcher$holder); + holdersToReadd.add(stitcher$holder); this.setStitchHolders.add(stitcher$holder); } public void doStitch() { - Stitcher.Holder[] astitcher$holder = (Stitcher.Holder[]) this.setStitchHolders - .toArray(new Stitcher.Holder[this.setStitchHolders.size()]); - Arrays.sort(astitcher$holder); - - for (int i = 0; i < astitcher$holder.length; ++i) { - Stitcher.Holder stitcher$holder = astitcher$holder[i]; - if (!this.allocateSlot(stitcher$holder)) { - String s = HString.format("Unable to fit: %s - size: %dx%d - Maybe try a lowerresolution resourcepack?", - new Object[] { stitcher$holder.getAtlasSprite().getIconName(), - Integer.valueOf(stitcher$holder.getAtlasSprite().getIconWidth()), - Integer.valueOf(stitcher$holder.getAtlasSprite().getIconHeight()) }); - throw new StitcherException(stitcher$holder, s); - } + try { + masterStitcher.stitch(); + currentWidth = masterStitcher.width; + currentHeight = masterStitcher.height; + stitchSlots.clear(); + stitchSlots.addAll(masterStitcher.getSlots()); + } catch (TooBigException ignored) { + throw new StitcherException(null, + "Unable to fit all textures into atlas. Maybe try a lower resolution resourcepack?"); + } finally { + masterStitcher.reset(); } + } - if (this.forcePowerOf2) { - this.currentWidth = MathHelper.roundUpToPowerOfTwo(this.currentWidth); - this.currentHeight = MathHelper.roundUpToPowerOfTwo(this.currentHeight); - } + public void dropLargestSprite() { + masterStitcher.dropFirst(); + } + public void retainAllSprites(Set spriteLocations) { + masterStitcher.retainAllSprites(spriteLocations); } public List getStichSlots() { @@ -306,6 +312,10 @@ public class Stitcher { return this.holder; } + public void insertHolder(Stitcher.Holder holder) { + this.holder = holder; + } + public int getOriginX() { return this.originX; }