Added: #2504 WebP image format for export/import (not animated)

Limitation: It's not available on Mac x86-64 platform
This commit is contained in:
Jindra Petřík
2025-08-10 18:45:12 +02:00
parent d8dc85109b
commit 2df87d1a0a
29 changed files with 344 additions and 30 deletions

View File

@@ -63,6 +63,7 @@ import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import com.jpexs.helpers.SerializableImage;
import com.jpexs.helpers.utf8.Utf8Helper;
import dev.matrixlab.webp4j.WebPCodec;
import gnu.jpdf.PDFGraphics;
import gnu.jpdf.PDFJob;
import java.awt.AlphaComposite;
@@ -120,6 +121,9 @@ public class FrameExporter {
case SVG:
fem = FrameExportMode.SVG;
break;
case WEBP:
fem = FrameExportMode.WEBP;
break;
case SWF:
fem = FrameExportMode.SWF;
break;
@@ -160,6 +164,9 @@ public class FrameExporter {
case BMP:
fem = FrameExportMode.BMP;
break;
case WEBP:
fem = FrameExportMode.WEBP;
break;
case SWF:
fem = FrameExportMode.SWF;
break;
@@ -513,7 +520,9 @@ public class FrameExporter {
}
final Color fbackgroundColor = backgroundColor;
final boolean usesTransparency = settings.mode == FrameExportMode.PNG || settings.mode == FrameExportMode.GIF;
final boolean usesTransparency = settings.mode == FrameExportMode.PNG
|| settings.mode == FrameExportMode.GIF
|| settings.mode == FrameExportMode.WEBP;
final MyFrameIterator frameImages = new MyFrameIterator(tim, fframes, evl, usesTransparency, backgroundColor, settings, subFramesLength);
switch (settings.mode) {
@@ -545,6 +554,26 @@ public class FrameExporter {
}
}
break;
case WEBP:
for (File foutdir : foutdirs) {
frameImages.reset();
for (int i = 0; frameImages.hasNext(); i++) {
final int fi = i;
new RetryTask(() -> {
int fileNum = subFramesLength > 1 ? fi + 1 : (fframes.get(fi) + 1);
File f = new File(foutdir + File.separator + fileNum + ".webp");
BufferedImage img = frameImages.next();
if (img != null) {
try(FileOutputStream fos = new FileOutputStream(f)) {
fos.write(WebPCodec.encodeImage(img, 100f));
}
}
ret.add(f);
}, handler).run();
}
}
break;
case PNG:
for (File foutdir : foutdirs) {
frameImages.reset();

View File

@@ -33,6 +33,7 @@ import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import dev.matrixlab.webp4j.WebPCodec;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
@@ -110,6 +111,11 @@ public class ImageExporter {
if (settings.mode == ImageExportMode.BMP) {
fileFormat = ImageFormat.BMP;
}
if (settings.mode == ImageExportMode.WEBP) {
fileFormat = ImageFormat.WEBP;
}
final File file = new File(outdir + File.separator + Helper.makeFileName(imageTag.getCharacterExportFileName() + "." + ImageHelper.getImageFormatString(fileFormat)));
final ImageFormat ffileFormat = fileFormat;
@@ -121,6 +127,10 @@ public class ImageExporter {
}
} else if (ffileFormat == ImageFormat.BMP) {
BMPFile.saveBitmap(imageTag.getImageCached().getBufferedImage(), file);
} else if (ffileFormat == ImageFormat.WEBP) {
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(file))) {
fos.write(WebPCodec.encodeImage(imageTag.getImageCached().getBufferedImage(), 100f));
}
} else {
ImageHelper.write(imageTag.getImageCached().getBufferedImage(), ffileFormat, file);
}

View File

@@ -48,6 +48,7 @@ import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import com.jpexs.helpers.SerializableImage;
import com.jpexs.helpers.utf8.Utf8Helper;
import dev.matrixlab.webp4j.WebPCodec;
import java.awt.Graphics2D;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
@@ -158,6 +159,7 @@ public class MorphShapeExporter {
break;
case PNG_START_END:
case BMP_START_END:
case WEBP_START_END:
double unzoom = settings.zoom;
st = mst.getStartShapeTag();
rect = st.getRect();
@@ -178,6 +180,10 @@ public class MorphShapeExporter {
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true);
if (settings.mode == MorphShapeExportMode.PNG_START_END) {
ImageHelper.write(img.getBufferedImage(), ImageFormat.PNG, fileStart);
} else if (settings.mode == MorphShapeExportMode.WEBP_START_END) {
try(FileOutputStream fos = new FileOutputStream(fileStart)) {
fos.write(WebPCodec.encodeImage(img.getBufferedImage(), 100f));
}
} else {
BMPFile.saveBitmap(img.getBufferedImage(), fileStart);
}
@@ -201,6 +207,10 @@ public class MorphShapeExporter {
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true);
if (settings.mode == MorphShapeExportMode.PNG_START_END) {
ImageHelper.write(img.getBufferedImage(), ImageFormat.PNG, fileEnd);
} else if (settings.mode == MorphShapeExportMode.WEBP_START_END) {
try(FileOutputStream fos = new FileOutputStream(fileEnd)) {
fos.write(WebPCodec.encodeImage(img.getBufferedImage(), 100f));
}
} else {
BMPFile.saveBitmap(img.getBufferedImage(), fileEnd);
}

View File

@@ -46,6 +46,7 @@ import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import com.jpexs.helpers.SerializableImage;
import com.jpexs.helpers.utf8.Utf8Helper;
import dev.matrixlab.webp4j.WebPCodec;
import java.awt.Graphics2D;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
@@ -122,6 +123,7 @@ public class ShapeExporter {
break;
case PNG:
case BMP:
case WEBP:
int newWidth = (int) (rect.getWidth() * settings.zoom / SWF.unitDivisor) + 1;
int newHeight = (int) (rect.getHeight() * settings.zoom / SWF.unitDivisor) + 1;
SerializableImage img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB_PRE);
@@ -137,6 +139,10 @@ public class ShapeExporter {
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true);
if (settings.mode == ShapeExportMode.PNG) {
ImageHelper.write(img.getBufferedImage(), ImageFormat.PNG, file);
} else if (settings.mode == ShapeExportMode.WEBP) {
try(FileOutputStream fos = new FileOutputStream(file)) {
fos.write(WebPCodec.encodeImage(img.getBufferedImage(), 100f));
}
} else {
BMPFile.saveBitmap(img.getBufferedImage(), file);
}

View File

@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.exporters.modes;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
/**
* Button export mode.
*
@@ -34,8 +36,19 @@ public enum ButtonExportMode {
* BMP - Windows Bitmap
*/
BMP,
/**
* WEBP
*/
WEBP,
/**
* SWF - Shockwave Flash
*/
SWF,
SWF;
public boolean available() {
if (this == WEBP) {
return ImageFormat.WEBP.available();
}
return true;
}
}

View File

@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.exporters.modes;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
/**
* Frame export mode.
*
@@ -50,8 +52,19 @@ public enum FrameExportMode {
* BMP - Windows Bitmap
*/
BMP,
/**
* WEBP
*/
WEBP,
/**
* SWF - Shockwave Flash
*/
SWF,
SWF;
public boolean available() {
if (this == WEBP) {
return ImageFormat.WEBP.available();
}
return true;
}
}

View File

@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.exporters.modes;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
/**
* Image export mode.
*
@@ -42,5 +44,16 @@ public enum ImageExportMode {
/**
* PNG, GIF or JPEG, depending on what suits the best, plus alpha channel
*/
PNG_GIF_JPEG_ALPHA
PNG_GIF_JPEG_ALPHA,
/*
* WEBP
*/
WEBP;
public boolean available() {
if (this == WEBP) {
return ImageFormat.WEBP.available();
}
return true;
}
}

View File

@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.exporters.modes;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
/**
* Morph shape export mode.
*
@@ -44,10 +46,21 @@ public enum MorphShapeExportMode {
* BMP start and end frames - Windows Bitmap
*/
BMP_START_END,
/**
* WEBP start and end frames
*/
WEBP_START_END,
//GIF,
//AVI,
/**
* SWF - Shockwave Flash
*/
SWF,
SWF;
public boolean available() {
if (this == WEBP_START_END) {
return ImageFormat.WEBP.available();
}
return true;
}
}

View File

@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.exporters.modes;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
/**
* Shape export mode.
*
@@ -38,8 +40,19 @@ public enum ShapeExportMode {
* BMP - Windows Bitmap
*/
BMP,
/**
* WEBP
*/
WEBP,
/**
* SWF - Shockwave Flash
*/
SWF,
SWF;
public boolean available() {
if (this == WEBP) {
return ImageFormat.WEBP.available();
}
return true;
}
}

View File

@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.exporters.modes;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
/**
* Sprite export mode.
*
@@ -50,8 +52,19 @@ public enum SpriteExportMode {
* BMP - Windows Bitmap
*/
BMP,
/**
* WEBP
*/
WEBP,
/**
* SWF - Shockwave Flash
*/
SWF,
SWF;
public boolean available() {
if (this == WEBP) {
return ImageFormat.WEBP.available();
}
return true;
}
}

View File

@@ -60,6 +60,8 @@ public class MorphShapeExportSettings {
return ".png";
case BMP_START_END:
return ".bmp";
case WEBP_START_END:
return ".webp";
case SVG:
case SVG_START_END:
return ".svg";

View File

@@ -64,6 +64,8 @@ public class ShapeExportSettings {
return ".bmp";
case CANVAS:
return ".html";
case WEBP:
return ".webp";
case SWF:
return ".swf";
default:

View File

@@ -226,6 +226,8 @@ public class ImageHelper {
return "png";
case BMP:
return "bmp";
case WEBP:
return "webp";
}
throw new Error("Unsupported image format: " + format);

View File

@@ -31,6 +31,7 @@ import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import dev.matrixlab.webp4j.WebPCodec;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -77,6 +78,20 @@ public class ImageImporter extends TagImporter {
ImageHelper.write(b, ImageFormat.PNG, baos);
newData = baos.toByteArray();
}
if (newData.length >= 4
&& newData[0] == 'R'
&& newData[1] == 'I'
&& newData[2] == 'F'
&& newData[3] == 'F')
{
if (!ImageFormat.WEBP.available()) {
throw new RuntimeException("WEBP format is not supported on your platform");
}
BufferedImage b = WebPCodec.decodeImage(newData);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageHelper.write(b, ImageFormat.PNG, baos);
newData = baos.toByteArray();
}
if (tagType == 0) {
if (it instanceof DefineBitsTag) {
@@ -94,7 +109,7 @@ public class ImageImporter extends TagImporter {
&& newData[3] == (byte) 0xe0) {
tagType = DefineBitsJPEG2Tag.ID;
} else {
tagType = DefineBitsLosslessTag.ID;
tagType = DefineBitsLossless2Tag.ID;
}
}

View File

@@ -38,10 +38,12 @@ import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import dev.matrixlab.webp4j.WebPCodec;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
@@ -162,6 +164,21 @@ public class ShapeImporter {
ImageHelper.write(b, ImageFormat.PNG, baos);
newData = baos.toByteArray();
}
if (newData.length >= 4
&& newData[0] == 'R'
&& newData[1] == 'I'
&& newData[2] == 'F'
&& newData[3] == 'F')
{
if (!ImageFormat.WEBP.available()) {
throw new RuntimeException("WEBP format is not supported on your platform");
}
BufferedImage b = WebPCodec.decodeImage(newData);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageHelper.write(b, ImageFormat.PNG, baos);
newData = baos.toByteArray();
}
if (tagType == 0) {
if (ImageTag.getImageFormat(newData) == ImageFormat.JPEG) {

View File

@@ -16,6 +16,9 @@
*/
package com.jpexs.decompiler.flash.tags.enums;
import dev.matrixlab.webp4j.NativeWebP;
import dev.matrixlab.webp4j.WebPCodec;
/**
* Image format.
*
@@ -42,7 +45,11 @@ public enum ImageFormat {
/**
* BMP
*/
BMP(".bmp");
BMP(".bmp"),
/**
* WEBP
*/
WEBP(".webp");
private final String extension;
@@ -57,4 +64,15 @@ public enum ImageFormat {
public String getExtension() {
return extension;
}
public boolean available() {
if (this == WEBP) {
try {
new NativeWebP();
} catch (Throwable t) {
return false;
}
}
return true;
}
}