Add option to preserve alpha channel in export (closes #661)

Default off cause it being on by default had confused quite a few people.
This commit is contained in:
Jonas Herzig
2022-03-02 16:39:30 +01:00
parent d9da2e135c
commit 7615499cef
8 changed files with 36 additions and 11 deletions

View File

@@ -40,7 +40,7 @@ public class GuiCreateScreenshot extends GuiRenderSettings implements Loadable {
new GuiLabel().setI18nText("replaymod.gui.advancedscreenshots.resolution"), videoResolutionPanel, new GuiLabel().setI18nText("replaymod.gui.advancedscreenshots.resolution"), videoResolutionPanel,
new GuiLabel().setI18nText("replaymod.gui.rendersettings.outputfile"), outputFileButton); new GuiLabel().setI18nText("replaymod.gui.rendersettings.outputfile"), outputFileButton);
resetChildren(advancedPanel).addElements(null, nametagCheckbox, new GuiPanel().setLayout( resetChildren(advancedPanel).addElements(null, nametagCheckbox, alphaCheckbox , new GuiPanel().setLayout(
new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(5).setSpacingY(15)) new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(5).setSpacingY(15))
.addElements(new GridLayout.Data(0, 0.5), .addElements(new GridLayout.Data(0, 0.5),
new GuiLabel().setI18nText("replaymod.gui.rendersettings.stabilizecamera"), stabilizePanel, new GuiLabel().setI18nText("replaymod.gui.rendersettings.stabilizecamera"), stabilizePanel,

View File

@@ -28,9 +28,11 @@ import static org.lwjgl.util.tinyexr.TinyEXR.*;
public class EXRWriter implements FrameConsumer<BitmapFrame> { public class EXRWriter implements FrameConsumer<BitmapFrame> {
private final Path outputFolder; private final Path outputFolder;
private final boolean keepAlpha;
public EXRWriter(Path outputFolder) throws IOException { public EXRWriter(Path outputFolder, boolean keepAlpha) throws IOException {
this.outputFolder = outputFolder; this.outputFolder = outputFolder;
this.keepAlpha = keepAlpha;
Files.createDirectories(outputFolder); Files.createDirectories(outputFolder);
} }
@@ -92,11 +94,15 @@ public class EXRWriter implements FrameConsumer<BitmapFrame> {
bgrChannels[(i + 3) % 4] = channel; bgrChannels[(i + 3) % 4] = channel;
} }
} }
int alphaMask = keepAlpha ? 0 : 0xff;
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
for (FloatBuffer channel : bgrChannels) { bgrChannels[0].put(((int) bgra.get() & 0xff) / 255f); // b
channel.put(((int) bgra.get() & 0xff) / 255f); bgrChannels[1].put(((int) bgra.get() & 0xff) / 255f); // g
} bgrChannels[2].put(((int) bgra.get() & 0xff) / 255f); // r
bgrChannels[3].put(((int) bgra.get() & 0xff | alphaMask) / 255f); // a
} }
} }
if (depthFrame != null && depthChannel != null) { if (depthFrame != null && depthChannel != null) {

View File

@@ -19,9 +19,11 @@ import java.util.Map;
public class PNGWriter implements FrameConsumer<BitmapFrame> { public class PNGWriter implements FrameConsumer<BitmapFrame> {
private final Path outputFolder; private final Path outputFolder;
private final boolean keepAlpha;
public PNGWriter(Path outputFolder) throws IOException { public PNGWriter(Path outputFolder, boolean keepAlpha) throws IOException {
this.outputFolder = outputFolder; this.outputFolder = outputFolder;
this.keepAlpha = keepAlpha;
Files.createDirectories(outputFolder); Files.createDirectories(outputFolder);
} }
@@ -47,6 +49,7 @@ public class PNGWriter implements FrameConsumer<BitmapFrame> {
} }
private void withImage(BitmapFrame frame, IOConsumer<Image> consumer) throws IOException { private void withImage(BitmapFrame frame, IOConsumer<Image> consumer) throws IOException {
byte alphaMask = (byte) (keepAlpha ? 0 : 0xff);
ByteBuffer buffer = frame.getByteBuffer(); ByteBuffer buffer = frame.getByteBuffer();
ReadableDimension size = frame.getSize(); ReadableDimension size = frame.getSize();
int width = size.getWidth(); int width = size.getWidth();
@@ -58,7 +61,7 @@ public class PNGWriter implements FrameConsumer<BitmapFrame> {
byte g = buffer.get(); byte g = buffer.get();
byte r = buffer.get(); byte r = buffer.get();
byte a = buffer.get(); byte a = buffer.get();
image.setRGBA(x, y, r, g, b, a); image.setRGBA(x, y, r, g, b, a | alphaMask);
} }
} }
consumer.accept(image); consumer.accept(image);

View File

@@ -151,6 +151,7 @@ public class RenderSettings {
private final File outputFile; private final File outputFile;
private final boolean renderNameTags; private final boolean renderNameTags;
private final boolean includeAlphaChannel;
private final boolean stabilizeYaw; private final boolean stabilizeYaw;
private final boolean stabilizePitch; private final boolean stabilizePitch;
private final boolean stabilizeRoll; private final boolean stabilizeRoll;
@@ -187,6 +188,7 @@ public class RenderSettings {
false, false,
false, false,
false, false,
false,
null, null,
360, 360,
180, 180,
@@ -209,6 +211,7 @@ public class RenderSettings {
int bitRate, int bitRate,
File outputFile, File outputFile,
boolean renderNameTags, boolean renderNameTags,
boolean includeAlphaChannel,
boolean stabilizeYaw, boolean stabilizeYaw,
boolean stabilizePitch, boolean stabilizePitch,
boolean stabilizeRoll, boolean stabilizeRoll,
@@ -231,6 +234,7 @@ public class RenderSettings {
this.bitRate = bitRate; this.bitRate = bitRate;
this.outputFile = outputFile; this.outputFile = outputFile;
this.renderNameTags = renderNameTags; this.renderNameTags = renderNameTags;
this.includeAlphaChannel = includeAlphaChannel;
this.stabilizeYaw = stabilizeYaw; this.stabilizeYaw = stabilizeYaw;
this.stabilizePitch = stabilizePitch; this.stabilizePitch = stabilizePitch;
this.stabilizeRoll = stabilizeRoll; this.stabilizeRoll = stabilizeRoll;
@@ -256,6 +260,7 @@ public class RenderSettings {
bitRate, bitRate,
outputFile, outputFile,
renderNameTags, renderNameTags,
includeAlphaChannel,
stabilizeYaw, stabilizeYaw,
stabilizePitch, stabilizePitch,
stabilizeRoll, stabilizeRoll,
@@ -415,6 +420,10 @@ public class RenderSettings {
return renderNameTags; return renderNameTags;
} }
public boolean isIncludeAlphaChannel() {
return includeAlphaChannel;
}
public boolean isStabilizeYaw() { public boolean isStabilizeYaw() {
return stabilizeYaw; return stabilizeYaw;
} }
@@ -478,6 +487,7 @@ public class RenderSettings {
", bitRate=" + bitRate + ", bitRate=" + bitRate +
", outputFile=" + outputFile + ", outputFile=" + outputFile +
", renderNameTags=" + renderNameTags + ", renderNameTags=" + renderNameTags +
", includeAlphaChannel=" + includeAlphaChannel +
", stabilizeYaw=" + stabilizeYaw + ", stabilizeYaw=" + stabilizeYaw +
", stabilizePitch=" + stabilizePitch + ", stabilizePitch=" + stabilizePitch +
", stabilizeRoll=" + stabilizeRoll + ", stabilizeRoll=" + stabilizeRoll +

View File

@@ -90,6 +90,7 @@ public class GuiExportFailed extends GuiScreen {
oldSettings.getBitRate(), oldSettings.getBitRate(),
oldSettings.getOutputFile(), oldSettings.getOutputFile(),
oldSettings.isRenderNameTags(), oldSettings.isRenderNameTags(),
oldSettings.isIncludeAlphaChannel(),
oldSettings.isStabilizeYaw(), oldSettings.isStabilizeYaw(),
oldSettings.isStabilizePitch(), oldSettings.isStabilizePitch(),
oldSettings.isStabilizeRoll(), oldSettings.isStabilizeRoll(),

View File

@@ -148,6 +148,9 @@ public class GuiRenderSettings extends AbstractGuiPopup<GuiRenderSettings> {
public final GuiCheckbox nametagCheckbox = new GuiCheckbox() public final GuiCheckbox nametagCheckbox = new GuiCheckbox()
.setI18nLabel("replaymod.gui.rendersettings.nametags"); .setI18nLabel("replaymod.gui.rendersettings.nametags");
public final GuiCheckbox alphaCheckbox = new GuiCheckbox()
.setI18nLabel("replaymod.gui.rendersettings.includealpha");
public final GuiPanel stabilizePanel = new GuiPanel().setLayout(new HorizontalLayout().setSpacing(10)); public final GuiPanel stabilizePanel = new GuiPanel().setLayout(new HorizontalLayout().setSpacing(10));
public final GuiCheckbox stabilizeYaw = new GuiCheckbox(stabilizePanel) public final GuiCheckbox stabilizeYaw = new GuiCheckbox(stabilizePanel)
.setI18nLabel("replaymod.gui.yaw"); .setI18nLabel("replaymod.gui.yaw");
@@ -187,7 +190,7 @@ public class GuiRenderSettings extends AbstractGuiPopup<GuiRenderSettings> {
.setSize(200, 20).setValues(RenderSettings.AntiAliasing.values()).setSelected(RenderSettings.AntiAliasing.NONE); .setSize(200, 20).setValues(RenderSettings.AntiAliasing.values()).setSelected(RenderSettings.AntiAliasing.NONE);
public final GuiPanel advancedPanel = new GuiPanel().setLayout(new VerticalLayout().setSpacing(15)) public final GuiPanel advancedPanel = new GuiPanel().setLayout(new VerticalLayout().setSpacing(15))
.addElements(null, nametagCheckbox, new GuiPanel().setLayout( .addElements(null, nametagCheckbox, alphaCheckbox, new GuiPanel().setLayout(
new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(5).setSpacingY(15)) new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(5).setSpacingY(15))
.addElements(new GridLayout.Data(0, 0.5), .addElements(new GridLayout.Data(0, 0.5),
new GuiLabel().setI18nText("replaymod.gui.rendersettings.stabilizecamera"), stabilizePanel, new GuiLabel().setI18nText("replaymod.gui.rendersettings.stabilizecamera"), stabilizePanel,
@@ -522,6 +525,7 @@ public class GuiRenderSettings extends AbstractGuiPopup<GuiRenderSettings> {
} }
outputFileButton.setLabel(this.outputFile.getName()); outputFileButton.setLabel(this.outputFile.getName());
nametagCheckbox.setChecked(settings.isRenderNameTags()); nametagCheckbox.setChecked(settings.isRenderNameTags());
alphaCheckbox.setChecked(settings.isIncludeAlphaChannel());
stabilizeYaw.setChecked(settings.isStabilizeYaw()); stabilizeYaw.setChecked(settings.isStabilizeYaw());
stabilizePitch.setChecked(settings.isStabilizePitch()); stabilizePitch.setChecked(settings.isStabilizePitch());
stabilizeRoll.setChecked(settings.isStabilizeRoll()); stabilizeRoll.setChecked(settings.isStabilizeRoll());
@@ -560,6 +564,7 @@ public class GuiRenderSettings extends AbstractGuiPopup<GuiRenderSettings> {
bitRateField.getInteger() << (10 * bitRateUnit.getSelected()), bitRateField.getInteger() << (10 * bitRateUnit.getSelected()),
serialize && !userDefinedOutputFileName ? getParentFile(outputFile) : outputFile, serialize && !userDefinedOutputFileName ? getParentFile(outputFile) : outputFile,
nametagCheckbox.isChecked(), nametagCheckbox.isChecked(),
alphaCheckbox.isChecked(),
stabilizeYaw.isChecked() && (serialize || stabilizeYaw.isEnabled()), stabilizeYaw.isChecked() && (serialize || stabilizeYaw.isEnabled()),
stabilizePitch.isChecked() && (serialize || stabilizePitch.isEnabled()), stabilizePitch.isChecked() && (serialize || stabilizePitch.isEnabled()),
stabilizeRoll.isChecked() && (serialize || stabilizeRoll.isEnabled()), stabilizeRoll.isChecked() && (serialize || stabilizeRoll.isEnabled()),

View File

@@ -136,12 +136,12 @@ public class VideoRenderer implements RenderInfo {
FrameConsumer<BitmapFrame> frameConsumer; FrameConsumer<BitmapFrame> frameConsumer;
if (settings.getEncodingPreset() == RenderSettings.EncodingPreset.EXR) { if (settings.getEncodingPreset() == RenderSettings.EncodingPreset.EXR) {
//#if MC>=11400 //#if MC>=11400
frameConsumer = new EXRWriter(settings.getOutputFile().toPath()); frameConsumer = new EXRWriter(settings.getOutputFile().toPath(), settings.isIncludeAlphaChannel());
//#else //#else
//$$ throw new UnsupportedOperationException("EXR requires LWJGL3"); //$$ throw new UnsupportedOperationException("EXR requires LWJGL3");
//#endif //#endif
} else if (settings.getEncodingPreset() == RenderSettings.EncodingPreset.PNG) { } else if (settings.getEncodingPreset() == RenderSettings.EncodingPreset.PNG) {
frameConsumer = new PNGWriter(settings.getOutputFile().toPath()); frameConsumer = new PNGWriter(settings.getOutputFile().toPath(), settings.isIncludeAlphaChannel());
} else { } else {
frameConsumer = new FFmpegWriter(this); frameConsumer = new FFmpegWriter(this);
} }