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 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 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 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++; } } }