133 lines
4.5 KiB
Java
133 lines
4.5 KiB
Java
package net.minecraft.client;
|
|
|
|
import com.mojang.blaze3d.buffers.BufferType;
|
|
import com.mojang.blaze3d.buffers.BufferUsage;
|
|
import com.mojang.blaze3d.buffers.GpuBuffer;
|
|
import com.mojang.blaze3d.pipeline.RenderTarget;
|
|
import com.mojang.blaze3d.platform.NativeImage;
|
|
import com.mojang.blaze3d.systems.CommandEncoder;
|
|
import com.mojang.blaze3d.systems.RenderSystem;
|
|
import com.mojang.blaze3d.textures.GpuTexture;
|
|
import com.mojang.logging.LogUtils;
|
|
import java.io.File;
|
|
import java.util.function.Consumer;
|
|
import net.fabricmc.api.EnvType;
|
|
import net.fabricmc.api.Environment;
|
|
import net.minecraft.ChatFormatting;
|
|
import net.minecraft.Util;
|
|
import net.minecraft.network.chat.ClickEvent;
|
|
import net.minecraft.network.chat.Component;
|
|
import org.jetbrains.annotations.Nullable;
|
|
import org.slf4j.Logger;
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public class Screenshot {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
public static final String SCREENSHOT_DIR = "screenshots";
|
|
|
|
/**
|
|
* Saves a screenshot in the game directory with a time-stamped filename.
|
|
*/
|
|
public static void grab(File gameDirectory, RenderTarget buffer, Consumer<Component> messageConsumer) {
|
|
grab(gameDirectory, null, buffer, messageConsumer);
|
|
}
|
|
|
|
/**
|
|
* Saves a screenshot in the game directory with the given file name (or null to generate a time-stamped name).
|
|
*/
|
|
public static void grab(File gameDirectory, @Nullable String screenshotName, RenderTarget buffer, Consumer<Component> messageConsumer) {
|
|
takeScreenshot(
|
|
buffer,
|
|
nativeImage -> {
|
|
File file2 = new File(gameDirectory, "screenshots");
|
|
file2.mkdir();
|
|
File file3;
|
|
if (screenshotName == null) {
|
|
file3 = getFile(file2);
|
|
} else {
|
|
file3 = new File(file2, screenshotName);
|
|
}
|
|
|
|
Util.ioPool()
|
|
.execute(
|
|
() -> {
|
|
try {
|
|
NativeImage exception = nativeImage;
|
|
|
|
try {
|
|
nativeImage.writeToFile(file3);
|
|
Component component = Component.literal(file3.getName())
|
|
.withStyle(ChatFormatting.UNDERLINE)
|
|
.withStyle(style -> style.withClickEvent(new ClickEvent.OpenFile(file3.getAbsoluteFile())));
|
|
messageConsumer.accept(Component.translatable("screenshot.success", component));
|
|
} catch (Throwable var7) {
|
|
if (nativeImage != null) {
|
|
try {
|
|
exception.close();
|
|
} catch (Throwable var6) {
|
|
var7.addSuppressed(var6);
|
|
}
|
|
}
|
|
|
|
throw var7;
|
|
}
|
|
|
|
if (nativeImage != null) {
|
|
nativeImage.close();
|
|
}
|
|
} catch (Exception var8) {
|
|
LOGGER.warn("Couldn't save screenshot", (Throwable)var8);
|
|
messageConsumer.accept(Component.translatable("screenshot.failure", var8.getMessage()));
|
|
}
|
|
}
|
|
);
|
|
}
|
|
);
|
|
}
|
|
|
|
public static void takeScreenshot(RenderTarget renderTarget, Consumer<NativeImage> writer) {
|
|
int i = renderTarget.width;
|
|
int j = renderTarget.height;
|
|
GpuTexture gpuTexture = renderTarget.getColorTexture();
|
|
if (gpuTexture == null) {
|
|
throw new IllegalStateException("Tried to capture screenshot of an incomplete framebuffer");
|
|
} else {
|
|
GpuBuffer gpuBuffer = RenderSystem.getDevice()
|
|
.createBuffer(() -> "Screenshot buffer", BufferType.PIXEL_PACK, BufferUsage.STATIC_READ, i * j * gpuTexture.getFormat().pixelSize());
|
|
CommandEncoder commandEncoder = RenderSystem.getDevice().createCommandEncoder();
|
|
RenderSystem.getDevice().createCommandEncoder().copyTextureToBuffer(gpuTexture, gpuBuffer, 0, () -> {
|
|
try (GpuBuffer.ReadView readView = commandEncoder.readBuffer(gpuBuffer)) {
|
|
NativeImage nativeImage = new NativeImage(i, j, false);
|
|
|
|
for (int k = 0; k < j; k++) {
|
|
for (int l = 0; l < i; l++) {
|
|
int m = readView.data().getInt((l + k * i) * gpuTexture.getFormat().pixelSize());
|
|
nativeImage.setPixelABGR(l, j - k - 1, m | 0xFF000000);
|
|
}
|
|
}
|
|
|
|
writer.accept(nativeImage);
|
|
}
|
|
|
|
gpuBuffer.close();
|
|
}, 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a unique PNG file in the given directory named by a timestamp. Handles cases where the timestamp alone is not enough to create a uniquely named file, though it still might suffer from an unlikely race condition where the filename was unique when this method was called, but another process or thread created a file at the same path immediately after this method returned.
|
|
*/
|
|
private static File getFile(File gameDirectory) {
|
|
String string = Util.getFilenameFormattedDateTime();
|
|
int i = 1;
|
|
|
|
while (true) {
|
|
File file = new File(gameDirectory, string + (i == 1 ? "" : "_" + i) + ".png");
|
|
if (!file.exists()) {
|
|
return file;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
}
|