2025-11-24 22:52:51 +03:00

209 lines
8.0 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.google.common.collect.ImmutableList
* org.jspecify.annotations.Nullable
*/
package net.minecraft.client.renderer.texture;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import net.minecraft.client.renderer.texture.StitcherException;
import net.minecraft.resources.Identifier;
import net.minecraft.util.Mth;
import org.jspecify.annotations.Nullable;
public class Stitcher<T extends Entry> {
private static final Comparator<Holder<?>> HOLDER_COMPARATOR = Comparator.comparing(h -> -h.height).thenComparing(h -> -h.width).thenComparing(h -> h.entry.name());
private final int mipLevel;
private final List<Holder<T>> texturesToBeStitched = new ArrayList<Holder<T>>();
private final List<Region<T>> storage = new ArrayList<Region<T>>();
private int storageX;
private int storageY;
private final int maxWidth;
private final int maxHeight;
private final int padding;
public Stitcher(int maxWidth, int maxHeight, int mipLevel, int anisotropyBit) {
this.mipLevel = mipLevel;
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
this.padding = 1 << mipLevel << Mth.clamp(anisotropyBit - 1, 0, 4);
}
public int getWidth() {
return this.storageX;
}
public int getHeight() {
return this.storageY;
}
public void registerSprite(T entry) {
Holder<T> holder = new Holder<T>(entry, Stitcher.smallestFittingMinTexel(entry.width() + this.padding * 2, this.mipLevel), Stitcher.smallestFittingMinTexel(entry.height() + this.padding * 2, this.mipLevel));
this.texturesToBeStitched.add(holder);
}
public void stitch() {
ArrayList<Holder<T>> holders = new ArrayList<Holder<T>>(this.texturesToBeStitched);
holders.sort(HOLDER_COMPARATOR);
for (Holder holder : holders) {
if (this.addToStorage(holder)) continue;
throw new StitcherException((Entry)holder.entry, (Collection)holders.stream().map(h -> h.entry).collect(ImmutableList.toImmutableList()));
}
}
public void gatherSprites(SpriteLoader<T> loader) {
for (Region<T> topRegion : this.storage) {
topRegion.walk(loader, this.padding);
}
}
private static int smallestFittingMinTexel(int input, int maxMipLevel) {
return (input >> maxMipLevel) + ((input & (1 << maxMipLevel) - 1) == 0 ? 0 : 1) << maxMipLevel;
}
private boolean addToStorage(Holder<T> holder) {
for (Region<T> region : this.storage) {
if (!region.add(holder)) continue;
return true;
}
return this.expand(holder);
}
private boolean expand(Holder<T> holder) {
Region<T> slot;
boolean growOnX;
boolean yWillGrow;
boolean yCanGrow;
int xCurrentSize = Mth.smallestEncompassingPowerOfTwo(this.storageX);
int yCurrentSize = Mth.smallestEncompassingPowerOfTwo(this.storageY);
int xNewSize = Mth.smallestEncompassingPowerOfTwo(this.storageX + holder.width);
int yNewSize = Mth.smallestEncompassingPowerOfTwo(this.storageY + holder.height);
boolean xCanGrow = xNewSize <= this.maxWidth;
boolean bl = yCanGrow = yNewSize <= this.maxHeight;
if (!xCanGrow && !yCanGrow) {
return false;
}
boolean xWillGrow = xCanGrow && xCurrentSize != xNewSize;
boolean bl2 = yWillGrow = yCanGrow && yCurrentSize != yNewSize;
if (xWillGrow ^ yWillGrow) {
growOnX = xWillGrow;
} else {
boolean bl3 = growOnX = xCanGrow && xCurrentSize <= yCurrentSize;
}
if (growOnX) {
if (this.storageY == 0) {
this.storageY = yNewSize;
}
slot = new Region(this.storageX, 0, xNewSize - this.storageX, this.storageY);
this.storageX = xNewSize;
} else {
slot = new Region<T>(0, this.storageY, this.storageX, yNewSize - this.storageY);
this.storageY = yNewSize;
}
slot.add(holder);
this.storage.add(slot);
return true;
}
private record Holder<T extends Entry>(T entry, int width, int height) {
}
public static interface Entry {
public int width();
public int height();
public Identifier name();
}
public static class Region<T extends Entry> {
private final int originX;
private final int originY;
private final int width;
private final int height;
private @Nullable List<Region<T>> subSlots;
private @Nullable Holder<T> holder;
public Region(int originX, int originY, int width, int height) {
this.originX = originX;
this.originY = originY;
this.width = width;
this.height = height;
}
public int getX() {
return this.originX;
}
public int getY() {
return this.originY;
}
public boolean add(Holder<T> holder) {
if (this.holder != null) {
return false;
}
int textureWidth = holder.width;
int textureHeight = holder.height;
if (textureWidth > this.width || textureHeight > this.height) {
return false;
}
if (textureWidth == this.width && textureHeight == this.height) {
this.holder = holder;
return true;
}
if (this.subSlots == null) {
this.subSlots = new ArrayList<Region<T>>(1);
this.subSlots.add(new Region<T>(this.originX, this.originY, textureWidth, textureHeight));
int spareWidth = this.width - textureWidth;
int spareHeight = this.height - textureHeight;
if (spareHeight > 0 && spareWidth > 0) {
int bottom;
int right = Math.max(this.height, spareWidth);
if (right >= (bottom = Math.max(this.width, spareHeight))) {
this.subSlots.add(new Region<T>(this.originX, this.originY + textureHeight, textureWidth, spareHeight));
this.subSlots.add(new Region<T>(this.originX + textureWidth, this.originY, spareWidth, this.height));
} else {
this.subSlots.add(new Region<T>(this.originX + textureWidth, this.originY, spareWidth, textureHeight));
this.subSlots.add(new Region<T>(this.originX, this.originY + textureHeight, this.width, spareHeight));
}
} else if (spareWidth == 0) {
this.subSlots.add(new Region<T>(this.originX, this.originY + textureHeight, textureWidth, spareHeight));
} else if (spareHeight == 0) {
this.subSlots.add(new Region<T>(this.originX + textureWidth, this.originY, spareWidth, textureHeight));
}
}
for (Region<T> subSlot : this.subSlots) {
if (!subSlot.add(holder)) continue;
return true;
}
return false;
}
public void walk(SpriteLoader<T> output, int padding) {
if (this.holder != null) {
output.load(this.holder.entry, this.getX(), this.getY(), padding);
} else if (this.subSlots != null) {
for (Region subSlot : this.subSlots) {
subSlot.walk(output, padding);
}
}
}
public String toString() {
return "Slot{originX=" + this.originX + ", originY=" + this.originY + ", width=" + this.width + ", height=" + this.height + ", texture=" + String.valueOf(this.holder) + ", subSlots=" + String.valueOf(this.subSlots) + "}";
}
}
public static interface SpriteLoader<T extends Entry> {
public void load(T var1, int var2, int var3, int var4);
}
}