AWT is forbidden on 1.13+ (this includes BufferedImage)
Instead use NativeImage on 1.13+ via newly introduced Image class. See Image docs for details. Also fixes an issue with thumbnail taking on 1.13+: The original ScreenshotHelper method is now async, luckily there's a sync variant and as a bonus it directly returns a NativeImage so we can skip having to read the screenshot from disk.
This commit is contained in:
@@ -21,6 +21,7 @@ import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
|
||||
import de.johni0702.minecraft.gui.utils.Colors;
|
||||
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
|
||||
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
|
||||
import de.johni0702.minecraft.gui.versions.Image;
|
||||
import de.johni0702.minecraft.gui.versions.MCVer;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.util.crash.CrashReport;
|
||||
@@ -48,11 +49,9 @@ import net.minecraft.client.util.DefaultSkinHelper;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -89,14 +88,14 @@ public class Utils {
|
||||
//#endif
|
||||
}
|
||||
|
||||
public static final BufferedImage DEFAULT_THUMBNAIL;
|
||||
public static final Image DEFAULT_THUMBNAIL;
|
||||
|
||||
static {
|
||||
BufferedImage thumbnail;
|
||||
Image thumbnail;
|
||||
try {
|
||||
thumbnail = ImageIO.read(getResourceAsStream("/default_thumb.jpg"));
|
||||
thumbnail = Image.read(getResourceAsStream("/default_thumb.jpg"));
|
||||
} catch (Exception e) {
|
||||
thumbnail = new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR);
|
||||
thumbnail = new Image(1, 1);
|
||||
e.printStackTrace();
|
||||
}
|
||||
DEFAULT_THUMBNAIL = thumbnail;
|
||||
|
||||
@@ -19,6 +19,8 @@ import net.minecraft.util.SystemUtil;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.WorldChunk;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
//#if MC>=11400
|
||||
import com.replaymod.core.mixin.AbstractButtonWidgetAccessor;
|
||||
@@ -90,6 +92,7 @@ import net.minecraft.SharedConstants;
|
||||
//#endif
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
@@ -99,6 +102,8 @@ import java.util.function.Consumer;
|
||||
* Abstraction over things that have changed between different MC versions.
|
||||
*/
|
||||
public class MCVer {
|
||||
private static Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
//#if MC<11400
|
||||
//$$ public static IEventBus FORGE_BUS = MinecraftForge.EVENT_BUS;
|
||||
//#if MC>=10809
|
||||
@@ -578,6 +583,22 @@ public class MCVer {
|
||||
//#endif
|
||||
}
|
||||
|
||||
public static void openURL(URI url) {
|
||||
//#if MC>=11300
|
||||
//#if MC>=11400
|
||||
SystemUtil.getOperatingSystem().open(url);
|
||||
//#else
|
||||
//$$ Util.getOSType().openURI(url);
|
||||
//#endif
|
||||
//#else
|
||||
//$$ try {
|
||||
//$$ Desktop.getDesktop().browse(url);
|
||||
//$$ } catch (Throwable e) {
|
||||
//$$ LOGGER.error("Failed to open URL: ", e);
|
||||
//$$ }
|
||||
//#endif
|
||||
}
|
||||
|
||||
//#if MC>=11300
|
||||
private static Boolean hasOptifine;
|
||||
public static boolean hasOptifine() {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.replaymod.extras.advancedscreenshots;
|
||||
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import com.replaymod.core.SettingsRegistry;
|
||||
import com.replaymod.core.versions.MCVer;
|
||||
import com.replaymod.extras.Setting;
|
||||
import com.replaymod.render.RenderSettings;
|
||||
import de.johni0702.minecraft.gui.container.GuiContainer;
|
||||
@@ -14,8 +15,6 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
||||
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableColor;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
public class GuiUploadScreenshot extends AbstractGuiPopup<GuiUploadScreenshot> {
|
||||
@@ -68,22 +67,10 @@ public class GuiUploadScreenshot extends AbstractGuiPopup<GuiUploadScreenshot> {
|
||||
}
|
||||
|
||||
if (veer) {
|
||||
veerUploadButton.onClick(() -> {
|
||||
try {
|
||||
Desktop.getDesktop().browse(URI.create("https://veer.tv/upload"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
veerUploadButton.onClick(() -> MCVer.openURL(URI.create("https://veer.tv/upload")));
|
||||
}
|
||||
|
||||
showOnDiskButton.onClick(() -> {
|
||||
try {
|
||||
Desktop.getDesktop().browse(renderSettings.getOutputFile().toURI());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
showOnDiskButton.onClick(() -> MCVer.openFile(renderSettings.getOutputFile().getParentFile()));
|
||||
|
||||
closeButton.onClick(() -> {
|
||||
if (neverOpenCheckbox.isChecked()) {
|
||||
|
||||
@@ -8,10 +8,9 @@ import com.replaymod.render.frame.RGBFrame;
|
||||
import com.replaymod.render.rendering.FrameConsumer;
|
||||
import com.replaymod.replay.ReplayModReplay;
|
||||
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
|
||||
import de.johni0702.minecraft.gui.versions.Image;
|
||||
import net.minecraft.util.crash.CrashReport;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -28,23 +27,20 @@ public class ScreenshotWriter implements FrameConsumer<RGBFrame> {
|
||||
// skip the first frame, in which not all chunks are properly loaded
|
||||
if (frame.getFrameId() == 0) return;
|
||||
|
||||
try {
|
||||
final ReadableDimension frameSize = frame.getSize();
|
||||
|
||||
BufferedImage img = new BufferedImage(frameSize.getWidth(), frameSize.getHeight(), BufferedImage.TYPE_INT_RGB);
|
||||
final ReadableDimension frameSize = frame.getSize();
|
||||
try (Image img = new Image(frameSize.getWidth(), frameSize.getHeight())) {
|
||||
for (int y = 0; y < frameSize.getHeight(); y++) {
|
||||
for (int x = 0; x < frameSize.getWidth(); x++) {
|
||||
byte r = frame.getByteBuffer().get();
|
||||
byte g = frame.getByteBuffer().get();
|
||||
byte b = frame.getByteBuffer().get();
|
||||
|
||||
int color = ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff);
|
||||
img.setRGB(x, y, color);
|
||||
img.setRGBA(x, y, r, g, b, 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
outputFile.getParentFile().mkdirs();
|
||||
ImageIO.write(img, "PNG", outputFile);
|
||||
img.writePNG(outputFile);
|
||||
} catch (OutOfMemoryError e) {
|
||||
e.printStackTrace();
|
||||
CrashReport report = CrashReport.create(e, "Exporting frame");
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.replaymod.core.utils.Utils;
|
||||
import com.replaymod.core.versions.MCVer;
|
||||
import com.replaymod.render.RenderSettings;
|
||||
import de.johni0702.minecraft.gui.GuiRenderer;
|
||||
import de.johni0702.minecraft.gui.RenderInfo;
|
||||
@@ -19,6 +20,7 @@ import de.johni0702.minecraft.gui.layout.CustomLayout;
|
||||
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||
import de.johni0702.minecraft.gui.popup.GuiFileChooserPopup;
|
||||
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
|
||||
import de.johni0702.minecraft.gui.versions.Image;
|
||||
import joptsimple.internal.Strings;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
@@ -34,8 +36,6 @@ import javax.annotation.Nullable;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -43,6 +43,7 @@ import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import static com.replaymod.extras.ReplayModExtras.LOGGER;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@@ -125,13 +126,13 @@ public class GuiYoutubeUpload extends GuiScreen {
|
||||
public void onSuccess(@Nullable File result) {
|
||||
if (result != null) {
|
||||
thumbnailButton.setLabel(result.getName());
|
||||
BufferedImage image;
|
||||
Image image;
|
||||
try {
|
||||
thumbnailImage = IOUtils.toByteArray(new FileInputStream(result));
|
||||
ImageInputStream in = ImageIO.createImageInputStream(new ByteArrayInputStream(thumbnailImage));
|
||||
ImageReader reader = ImageIO.getImageReaders(in).next();
|
||||
thumbnailFormat = reader.getFormatName().toLowerCase();
|
||||
image = ImageIO.read(new ByteArrayInputStream(thumbnailImage));
|
||||
image = Image.read(new ByteArrayInputStream(thumbnailImage));
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
thumbnailImage = null;
|
||||
@@ -216,17 +217,9 @@ public class GuiYoutubeUpload extends GuiScreen {
|
||||
public void onSuccess(Video result) {
|
||||
String url = "https://youtu.be/" + result.getId();
|
||||
try {
|
||||
Desktop.getDesktop().browse(new URL(url).toURI());
|
||||
MCVer.openURL(new URL(url).toURI());
|
||||
} catch(Throwable throwable) {
|
||||
//#if MC>=11400
|
||||
SystemUtil.getOperatingSystem().open(url);
|
||||
//#else
|
||||
//#if MC>=11300
|
||||
//$$ Util.getOSType().openURI(url);
|
||||
//#else
|
||||
//$$ Sys.openURL(url);
|
||||
//#endif
|
||||
//#endif
|
||||
LOGGER.error("Failed to open video URL \"{}\":", url, throwable);
|
||||
}
|
||||
upload = null;
|
||||
progressBar.setLabel(I18n.translate("replaymod.gui.ytuploadprogress.done", url));
|
||||
|
||||
@@ -11,13 +11,12 @@ import com.replaymod.online.AuthenticationHash;
|
||||
import com.replaymod.online.api.replay.ReplayModApiMethods;
|
||||
import com.replaymod.online.api.replay.SearchQuery;
|
||||
import com.replaymod.online.api.replay.holders.*;
|
||||
import de.johni0702.minecraft.gui.versions.Image;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -133,14 +132,14 @@ public class ApiClient {
|
||||
return SimpleApiClient.invokeUrl(builder.toString());
|
||||
}
|
||||
|
||||
public BufferedImage downloadThumbnail(int file) throws IOException {
|
||||
public Image downloadThumbnail(int file) throws IOException {
|
||||
QueryBuilder builder = new QueryBuilder(ReplayModApiMethods.get_thumbnail);
|
||||
builder.put("id", file);
|
||||
URL url = new URL(builder.toString());
|
||||
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
|
||||
connection.setSSLSocketFactory(SSL_SOCKET_FACTORY);
|
||||
try (InputStream in = connection.getInputStream()) {
|
||||
return ImageIO.read(in);
|
||||
return Image.read(in);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,10 +39,10 @@ import de.johni0702.minecraft.gui.utils.Colors;
|
||||
import de.johni0702.minecraft.gui.utils.Consumer;
|
||||
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
|
||||
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
|
||||
import de.johni0702.minecraft.gui.versions.Image;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minecraft.util.Formatting;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
@@ -311,19 +311,9 @@ public class GuiReplayCenter extends GuiScreen {
|
||||
if (Thread.interrupted()) return;
|
||||
try {
|
||||
// Make sure that to int[] conversion doesn't have to occur in main thread
|
||||
final BufferedImage theThumb;
|
||||
final Image theThumb;
|
||||
if (fileInfo.hasThumbnail()) {
|
||||
BufferedImage buf = apiClient.downloadThumbnail(fileInfo.getId());
|
||||
// This is the same way minecraft calls this method, we cache the result and hand
|
||||
// minecraft a BufferedImage with way simpler logic using the precomputed values
|
||||
final int[] theIntArray = buf.getRGB(0, 0, buf.getWidth(), buf.getHeight(), null, 0, buf.getWidth());
|
||||
theThumb = new BufferedImage(buf.getWidth(), buf.getHeight(), BufferedImage.TYPE_INT_ARGB) {
|
||||
@Override
|
||||
public int[] getRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize) {
|
||||
System.arraycopy(theIntArray, 0, rgbArray, 0, theIntArray.length);
|
||||
return null; // Minecraft doesn't use the return value
|
||||
}
|
||||
};
|
||||
theThumb = apiClient.downloadThumbnail(fileInfo.getId());
|
||||
} else {
|
||||
theThumb = null;
|
||||
}
|
||||
@@ -412,7 +402,7 @@ public class GuiReplayCenter extends GuiScreen {
|
||||
private final boolean downloaded;
|
||||
private final boolean incompatible;
|
||||
|
||||
public GuiReplayEntry(FileInfo fileInfo, BufferedImage thumbImage, int sortId, boolean downloaded) {
|
||||
public GuiReplayEntry(FileInfo fileInfo, Image thumbImage, int sortId, boolean downloaded) {
|
||||
this.fileInfo = fileInfo;
|
||||
this.sortId = sortId;
|
||||
this.downloaded = downloaded;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.replaymod.online.gui;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.replaymod.core.KeyBindingRegistry;
|
||||
@@ -32,6 +31,7 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
||||
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
|
||||
import de.johni0702.minecraft.gui.utils.Colors;
|
||||
import de.johni0702.minecraft.gui.versions.Image;
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import net.minecraft.client.options.KeyBinding;
|
||||
import net.minecraft.util.crash.CrashReport;
|
||||
@@ -45,9 +45,10 @@ import org.apache.commons.lang3.StringUtils;
|
||||
//#endif
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -127,12 +128,24 @@ public class GuiUploadReplay extends GuiScreen {
|
||||
this.uploader = new FileUploader(mod.getApiClient());
|
||||
|
||||
ReplayMetaData metaData;
|
||||
Optional<BufferedImage> optThumbnail;
|
||||
Optional<Image> optThumbnail;
|
||||
|
||||
// Read from replay file
|
||||
try (ReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), file)) {
|
||||
metaData = replayFile.getMetaData();
|
||||
optThumbnail = replayFile.getThumb();
|
||||
// TODO add a getThumbBytes method to ReplayStudio
|
||||
optThumbnail = Optional.ofNullable(replayFile.get("thumb").orNull()).flatMap(stream -> {
|
||||
try (InputStream in = stream) {
|
||||
int i = 7;
|
||||
while (i > 0) {
|
||||
i -= in.skip(i);
|
||||
}
|
||||
return Optional.of(Image.read(in));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return Optional.empty();
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new CrashException(CrashReport.create(e, "Read replay file " + file.getName()));
|
||||
}
|
||||
|
||||
@@ -4,13 +4,11 @@ import com.replaymod.render.blend.data.DImage;
|
||||
import com.replaymod.render.blend.data.DMaterial;
|
||||
import com.replaymod.render.blend.data.DPackedFile;
|
||||
import com.replaymod.render.blend.data.DTexture;
|
||||
import de.johni0702.minecraft.gui.versions.Image;
|
||||
import net.minecraft.client.util.GlAllocationUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBufferByte;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -30,20 +28,22 @@ public class BlendMaterials {
|
||||
ByteBuffer buffer = GlAllocationUtils.allocateByteBuffer(width * height * 4);
|
||||
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
// Convert to BufferedImage
|
||||
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
|
||||
byte[] data = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()).getData();
|
||||
for (int i = 0; i < data.length; i += 4) {
|
||||
data[i + 3] = buffer.get();
|
||||
data[i + 2] = buffer.get();
|
||||
data[i + 1] = buffer.get();
|
||||
data[i ] = buffer.get();
|
||||
// Convert to Image
|
||||
Image bufImage = new Image(width, height);
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int a = buffer.get();
|
||||
int b = buffer.get();
|
||||
int g = buffer.get();
|
||||
int r = buffer.get();
|
||||
bufImage.setRGBA(x, y, r, b, g, a);
|
||||
}
|
||||
}
|
||||
|
||||
// Encode as png image
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
try {
|
||||
ImageIO.write(bufferedImage, "png", stream);
|
||||
bufImage.writePNG(stream);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e); // never happens unless ImageIO is bugged
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
package com.replaymod.replay;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import com.replaymod.core.versions.MCVer;
|
||||
import de.johni0702.minecraft.gui.versions.Image;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import net.minecraft.client.util.ScreenshotUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
//#if MC<11300
|
||||
//$$ import com.google.common.io.Files;
|
||||
//$$ import org.apache.commons.io.FileUtils;
|
||||
//$$ import java.io.File;
|
||||
//#endif
|
||||
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@Getter
|
||||
public class NoGuiScreenshot {
|
||||
private final BufferedImage image;
|
||||
private final Image image;
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
@@ -80,42 +80,37 @@ public class NoGuiScreenshot {
|
||||
|
||||
// The frame without GUI has been rendered
|
||||
// Read it, create the screenshot and finish the future
|
||||
// We're using Minecraft's ScreenShotHelper even though it writes the screenshot to
|
||||
// disk for better maintainability
|
||||
File tmpFolder = Files.createTempDir();
|
||||
try {
|
||||
//#if MC>=11300
|
||||
ScreenshotUtils.method_1662(tmpFolder, "tmp", frameWidth, frameHeight, mc.getFramebuffer(), (msg) ->
|
||||
ReplayMod.instance.runLater(() -> mc.inGameHud.getChatHud().addMessage(msg)));
|
||||
Image image = new Image(ScreenshotUtils.method_1663(frameWidth, frameHeight, mc.getFramebuffer()));
|
||||
//#else
|
||||
//$$ ScreenShotHelper.saveScreenshot(tmpFolder, "tmp", frameWidth, frameHeight, mc.getFramebuffer());
|
||||
// We're using Minecraft's ScreenShotHelper even though it writes the screenshot to
|
||||
// disk for better maintainability
|
||||
//$$ File tmpFolder = Files.createTempDir();
|
||||
//$$ Image image;
|
||||
//$$ try {
|
||||
//$$ ScreenShotHelper.saveScreenshot(tmpFolder, "tmp", frameWidth, frameHeight, mc.getFramebuffer());
|
||||
//$$ File screenshotFile = new File(tmpFolder, "screenshots/tmp");
|
||||
//$$ image = Image.read(screenshotFile.toPath());
|
||||
//$$ } finally {
|
||||
//$$ FileUtils.deleteQuietly(tmpFolder);
|
||||
//$$ }
|
||||
//#endif
|
||||
File screenshotFile = new File(tmpFolder, "screenshots/tmp");
|
||||
BufferedImage image = ImageIO.read(screenshotFile);
|
||||
int imageWidth = image.getWidth();
|
||||
int imageHeight = image.getHeight();
|
||||
|
||||
// First scale
|
||||
// Scale & crop
|
||||
float scaleFactor = Math.max((float) width / imageWidth, (float) height / imageHeight);
|
||||
int scaledWidth = (int) (imageWidth * scaleFactor);
|
||||
int scaledHeight = (int) (imageHeight * scaleFactor);
|
||||
Image scaledImage = image.getScaledInstance(scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
|
||||
|
||||
// Then crop
|
||||
int resultX = (scaledWidth - width) / 2;
|
||||
int resultY = (scaledHeight - height) / 2;
|
||||
BufferedImage resultImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
|
||||
Graphics2D graphics = resultImage.createGraphics();
|
||||
graphics.drawImage(scaledImage, 0, 0, width, height,
|
||||
resultX, resultY, resultX + width, resultY + height, null);
|
||||
graphics.dispose();
|
||||
int croppedWidth = Math.min(Math.max(0, (int) (width / scaleFactor)), imageWidth);
|
||||
int croppedHeight = Math.min(Math.max(0, (int) (height / scaleFactor)), imageHeight);
|
||||
int offsetX = (imageWidth - croppedWidth) / 2;
|
||||
int offsetY = (imageHeight - croppedHeight) / 2;
|
||||
image = image.scaledSubRect(offsetX, offsetY, croppedWidth, croppedHeight, width, height);
|
||||
|
||||
// Finish
|
||||
future.set(new NoGuiScreenshot(resultImage, width, height));
|
||||
future.set(new NoGuiScreenshot(image, width, height));
|
||||
} catch (Throwable t) {
|
||||
future.setException(t);
|
||||
} finally {
|
||||
FileUtils.deleteQuietly(tmpFolder);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -27,6 +27,8 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
@@ -87,7 +89,14 @@ public class ReplayModReplay implements Module {
|
||||
public void onSuccess(NoGuiScreenshot result) {
|
||||
try {
|
||||
core.printInfoToChat("replaymod.chat.savingthumb");
|
||||
replayHandler.getReplayFile().writeThumb(result.getImage());
|
||||
@SuppressWarnings("deprecation") // there's no easy way to produce jpg images from NativeImage
|
||||
BufferedImage image = result.getImage().toBufferedImage();
|
||||
// Encoding with alpha fails on OpenJDK and produces broken image on Sun JDK.
|
||||
BufferedImage bgrImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
|
||||
Graphics graphics = bgrImage.getGraphics();
|
||||
graphics.drawImage(image, 0, 0, null);
|
||||
graphics.dispose();
|
||||
replayHandler.getReplayFile().writeThumb(bgrImage);
|
||||
core.printInfoToChat("replaymod.chat.savedthumb");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.replaymod.replay.gui.screen;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import de.johni0702.minecraft.gui.versions.Image;
|
||||
import net.minecraft.util.Formatting;
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import com.replaymod.core.SettingsRegistry;
|
||||
@@ -44,15 +44,16 @@ import org.apache.commons.io.IOCase;
|
||||
import org.apache.commons.io.filefilter.SuffixFileFilter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static com.replaymod.replay.ReplayModReplay.LOGGER;
|
||||
|
||||
@@ -324,28 +325,23 @@ public class GuiReplayViewer extends GuiScreen {
|
||||
for (final File file : files) {
|
||||
if (Thread.interrupted()) break;
|
||||
try (ReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), file)) {
|
||||
Optional<BufferedImage> thumb = replayFile.getThumb();
|
||||
// Make sure that to int[] conversion doesn't have to occur in main thread
|
||||
final BufferedImage theThumb;
|
||||
if (thumb.isPresent()) {
|
||||
BufferedImage buf = thumb.get();
|
||||
// This is the same way minecraft calls this method, we cache the result and hand
|
||||
// minecraft a BufferedImage with way simpler logic using the precomputed values
|
||||
final int[] theIntArray = buf.getRGB(0, 0, buf.getWidth(), buf.getHeight(), null, 0, buf.getWidth());
|
||||
theThumb = new BufferedImage(buf.getWidth(), buf.getHeight(), BufferedImage.TYPE_INT_ARGB) {
|
||||
@Override
|
||||
public int[] getRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize) {
|
||||
System.arraycopy(theIntArray, 0, rgbArray, 0, theIntArray.length);
|
||||
return null; // Minecraft doesn't use the return value
|
||||
// TODO add a getThumbBytes method to ReplayStudio
|
||||
final Image thumb = Optional.ofNullable(replayFile.get("thumb").orNull()).flatMap(stream -> {
|
||||
try (InputStream in = stream) {
|
||||
int i = 7;
|
||||
while (i > 0) {
|
||||
i -= in.skip(i);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
theThumb = null;
|
||||
}
|
||||
return Optional.of(Image.read(in));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return Optional.empty();
|
||||
}
|
||||
}).orElse(null);
|
||||
final ReplayMetaData metaData = replayFile.getMetaData();
|
||||
|
||||
if (metaData != null) {
|
||||
results.consume(() -> new GuiReplayEntry(file, metaData, theThumb));
|
||||
results.consume(() -> new GuiReplayEntry(file, metaData, thumb));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Could not load Replay File {}", file.getName(), e);
|
||||
@@ -402,7 +398,7 @@ public class GuiReplayViewer extends GuiScreen {
|
||||
private final long dateMillis;
|
||||
private final boolean incompatible;
|
||||
|
||||
public GuiReplayEntry(File file, ReplayMetaData metaData, BufferedImage thumbImage) {
|
||||
public GuiReplayEntry(File file, ReplayMetaData metaData, Image thumbImage) {
|
||||
this.file = file;
|
||||
|
||||
name.setText(Formatting.UNDERLINE + Utils.fileNameToReplayName(file.getName()));
|
||||
|
||||
Reference in New Issue
Block a user