Skip to content

Instantly share code, notes, and snippets.

@tterrag1098
Last active February 1, 2016 05:59
Show Gist options
  • Save tterrag1098/212afae4c9f12541b496 to your computer and use it in GitHub Desktop.
Save tterrag1098/212afae4c9f12541b496 to your computer and use it in GitHub Desktop.
package com.creatubbles.ctbmod.client.gui.upload;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import lombok.Getter;
import lombok.Value;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.MathHelper;
import org.apache.commons.io.comparator.LastModifiedFileComparator;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.lwjgl.util.Dimension;
import com.creatubbles.ctbmod.client.gui.creator.OverlayBase;
import com.creatubbles.repack.endercore.api.client.gui.IGuiScreen;
import com.creatubbles.repack.endercore.client.gui.widget.GuiToolTip;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import static com.creatubbles.ctbmod.client.gui.upload.ThumbnailStitcher.*;
public class OverlayScreenshotThumbs extends OverlayBase<GuiScreenshotList> {
private static final ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
private static ThumbnailStitcher prevStitcher = null;
private static File[] prevFiles = null;
private ThumbnailStitcher stitcher;
private Future<?> stitchTask;
private List<File> screenshots;
private List<ThumbnailAndLocation> thumbnails = Lists.newArrayList();
private ListenableFuture<?> listTask;
private int thumbSize = 64;
private int padding = 16;
@Getter
private int pages;
@Getter
private int page;
public OverlayScreenshotThumbs(int x, int y, Dimension dimension) {
super(x, y, dimension);
final File[] files = new File(Minecraft.getMinecraft().mcDataDir, "screenshots").listFiles((FilenameFilter) FileFilterUtils.suffixFileFilter(".png"));
if (Arrays.equals(files, prevFiles)) {
stitcher = prevStitcher;
} else {
prevFiles = files;
if (prevStitcher != null) {
prevStitcher.dispose();
}
stitcher = prevStitcher = new ThumbnailStitcher();
stitchTask = executor.submit(new Runnable() {
@Override
public void run() {
stitcher.loadFiles(files);
}
});
}
screenshots = Lists.newArrayList(files);
Collections.sort(screenshots, LastModifiedFileComparator.LASTMODIFIED_REVERSE);
}
@Value
private class ThumbnailAndLocation {
private Rectangle slot;
private Point location;
private Rectangle bounds;
private GuiToolTip tooltip;
private int page;
private ThumbnailAndLocation(File f, Point p, int page) {
this.slot = new Rectangle(stitcher.getRect(f));
this.location = p;
this.page = page;
int widthOff = ((WIDTH - slot.width) / 2);
int heightOff = ((HEIGHT - slot.height) / 2);
slot.x += widthOff;
slot.y += heightOff;
bounds = new Rectangle(p.x + (widthOff / 2), p.y + (heightOff / 2), 64 - widthOff, 64 - heightOff);
List<String> tt = Lists.newArrayList();
tt.add(f.getName());
tooltip = new GuiToolTip(bounds, tt);
}
}
@Override
public void init(IGuiScreen screen) {
super.init(screen);
thumbnails.clear();
Runnable listBuilder = new Runnable() {
public void run() {
int perRow = (getWidth() - padding) / (padding + thumbSize);
int perCol = (getHeight() - padding) / (padding + thumbSize);
System.out.println(screenshots.size());
pages = (int) Math.ceil(((double) screenshots.size() / (perRow * perCol)));
page = MathHelper.clamp_int(page, 0, pages - 1);
int xOff = (getWidth() - (padding + ((thumbSize + padding) * perRow))) / 2;
int yOff = (getHeight() - (padding + ((thumbSize + padding) * perCol))) / 2;
int row = 0, col = 0, page = 0;
for (File f : screenshots) {
int x = getX() + xOff + padding + ((thumbSize + padding) * col);
int y = getY() + yOff + padding + ((thumbSize + padding) * row);
thumbnails.add(new ThumbnailAndLocation(f, new Point(x, y), page));
if (++col >= perRow) {
col = 0;
row++;
}
if (row >= perCol) {
row = col = 0;
page++;
}
}
}
};
if (stitchTask == null || stitchTask.isDone()) {
listBuilder.run();
} else {
listTask = executor.submit(listBuilder);
}
}
public void onListBuilt(Runnable toRun) {
if (listTask == null || listTask.isDone()) {
toRun.run();
} else {
listTask.addListener(toRun, MoreExecutors.sameThreadExecutor());
}
}
@Override
protected void doDraw(int mouseX, int mouseY, float partialTick) {
super.doDraw(mouseX, mouseY, partialTick);
WorldRenderer worldrenderer = Tessellator.getInstance().getWorldRenderer();
Minecraft.getMinecraft().getTextureManager().bindTexture(Gui.optionsBackground);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
float f = 32.0F;
worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX_COLOR);
int left = getX(), top = getY();
int right = left + getWidth(), bottom = top + getHeight();
worldrenderer.pos(left, bottom, 0.0D).tex(left / f, bottom / f).color(32, 32, 32, 255).endVertex();
worldrenderer.pos(right, bottom, 0.0D).tex(right / f, bottom / f).color(32, 32, 32, 255).endVertex();
worldrenderer.pos(right, top, 0.0D).tex(right / f, top/ f).color(32, 32, 32, 255).endVertex();
worldrenderer.pos(left, top, 0.0D).tex(left / f, top / f).color(32, 32, 32, 255).endVertex();
Tessellator.getInstance().draw();
GlStateManager.enableTexture2D();
GlStateManager.disableLighting();
if (listTask == null || listTask.isDone()) {
Minecraft.getMinecraft().getTextureManager().bindTexture(stitcher.getRes());
for (ThumbnailAndLocation thumb : thumbnails) {
if (thumb.getPage() == page) {
Rectangle bounds = thumb.getBounds();
Rectangle slot = thumb.getSlot();
if (bounds.contains(mouseX, mouseY)) {
drawRect(bounds.x - 1, bounds.y - 1, bounds.x + bounds.width + 1, bounds.y + bounds.height + 1, 0xFFFFFFFF);
}
drawScaledCustomSizeModalRect(bounds.x, bounds.y, slot.x, slot.y, slot.width, slot.height, bounds.width, bounds.height, stitcher.getWidth(), stitcher.getHeight());
}
}
} else {
drawCenteredString(Minecraft.getMinecraft().fontRendererObj, "Loading...", left + getWidth() / 2, top + getHeight() / 2, 0xFFFFFF);
}
}
public void page(int delta) {
this.page = MathHelper.clamp_int(page + delta, 0, pages - 1);
}
}
package com.creatubbles.ctbmod.client.gui.upload;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Map;
import java.util.Map.Entry;
import javax.imageio.ImageIO;
import lombok.Getter;
import lombok.SneakyThrows;
import net.minecraft.client.Minecraft;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
import com.creatubbles.ctbmod.client.gui.LazyLoadedTexture;
import com.creatubbles.ctbmod.client.gui.creator.OverlayCreationList;
import com.google.common.collect.Maps;
public class ThumbnailStitcher {
public static final int WIDTH = 128;
public static final int HEIGHT = WIDTH;
private static final int MAX_SIZE = Minecraft.getGLMaximumTextureSize();
private BufferedImage map;
@Getter
private ResourceLocation res = OverlayCreationList.LOADING_TEX;
private Map<File, Rectangle> rects = Maps.newHashMap();
@SneakyThrows
public void loadFiles(final File... files) {
Map<File, Image> images = Maps.newHashMap();
int type = -1;
for (File f : files) {
BufferedImage img = ImageIO.read(f);
if (type == -1) {
type = img.getType();
}
int w = img.getWidth(), h = img.getHeight();
int scaledW, scaledH;
if (w > h) {
scaledW = WIDTH;
scaledH = (int) (((double) h / w) * HEIGHT);
} else {
scaledH = HEIGHT;
scaledW = (int) (((double) w / h) * WIDTH);
}
images.put(f, img.getScaledInstance(scaledW, scaledH, BufferedImage.SCALE_SMOOTH));
}
double sqrt = Math.sqrt(images.size());
int width = (int) sqrt;
if (sqrt != width) {
width++;
}
width *= WIDTH;
int scaled = 2 << (32 - Integer.numberOfLeadingZeros(width - 1)) - 1;
if (scaled > MAX_SIZE) {
throw new IllegalStateException("Cannot load thumbnail sheet, not enough size! Max: " + MAX_SIZE + " Needed: " + scaled);
}
map = new BufferedImage(scaled, scaled, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D out = map.createGraphics();
try {
int x, y;
x = y = 0;
for (Entry<File, Image> e : images.entrySet()) {
BufferedImage full = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_4BYTE_ABGR);
Image thumb = e.getValue();
int w = thumb.getWidth(null), h = thumb.getHeight(null);
Graphics2D transcribe = full.createGraphics();
try {
transcribe.setColor(new Color(0, 0, 0, 0));
transcribe.fillRect(0, 0, WIDTH, HEIGHT);
transcribe.drawImage(thumb, Math.max(0, h - w) / 2, Math.max(0, w - h) / 2, null);
} finally {
transcribe.dispose();
}
out.drawImage(full, x, y, null);
rects.put(e.getKey(), new Rectangle(x, y, w, h));
x += WIDTH;
if (x >= width) {
x = 0;
y += HEIGHT;
}
}
} finally {
out.dispose();
}
final LazyLoadedTexture tex = new LazyLoadedTexture(map);
Minecraft.getMinecraft().addScheduledTask(new Runnable() {
@Override
public void run() {
tex.uploadTexture();
ResourceLocation loc = new ResourceLocation("ctbmod", files[0].getParentFile().getName());
Minecraft.getMinecraft().getTextureManager().loadTexture(loc, tex);
res = loc;
}
});
}
public Rectangle getRect(File f) {
return rects.get(f);
}
public int getWidth() {
return map.getWidth();
}
public int getHeight() {
return map.getHeight();
}
public void dispose() {
GL11.glDeleteTextures(Minecraft.getMinecraft().getTextureManager().getTexture(res).getGlTextureId());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment