diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 63b89456b1..5151b44419 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -48,6 +48,9 @@ nifty-style-black = { module = "com.github.nifty-gui:nifty-style-black",
spotbugs-gradle-plugin = "com.github.spotbugs.snom:spotbugs-gradle-plugin:6.4.8"
vecmath = "javax.vecmath:vecmath:1.5.2"
+stb-image = "org.ngengine:stb-image:2.30.4"
+imagewebp = "org.ngengine:image-webp-decoder:1.3.0"
+
[bundles]
[plugins]
diff --git a/jme3-android/src/main/resources/com/jme3/asset/Android.cfg b/jme3-android/src/main/resources/com/jme3/asset/Android.cfg
index 0d58c3a960..fffd15bb9e 100644
--- a/jme3-android/src/main/resources/com/jme3/asset/Android.cfg
+++ b/jme3-android/src/main/resources/com/jme3/asset/Android.cfg
@@ -3,6 +3,4 @@ INCLUDE com/jme3/asset/General.cfg
# Android specific locators
LOCATOR / com.jme3.asset.plugins.AndroidLocator
-# Android specific loaders
-LOADER com.jme3.texture.plugins.AndroidNativeImageLoader : jpg, bmp, gif, png, jpeg
-LOADER com.jme3.texture.plugins.AndroidBufferImageLoader : webp, heic, heif
+LOADER com.jme3.texture.plugins.AndroidBufferImageLoader : heic, heif
diff --git a/jme3-core/src/main/java/com/jme3/texture/image/ImageCodec.java b/jme3-core/src/main/java/com/jme3/texture/image/ImageCodec.java
index 993ce508b4..0fe86270d8 100644
--- a/jme3-core/src/main/java/com/jme3/texture/image/ImageCodec.java
+++ b/jme3-core/src/main/java/com/jme3/texture/image/ImageCodec.java
@@ -103,13 +103,21 @@ public ImageCodec(int bpp, int flags, int maxAlpha, int maxRed, int maxGreen, in
params.put(Format.RGB8, new ByteOffsetImageCodec(3, 0, -1, 0, 1, 2));
- params.put(Format.RGB32F, new ByteAlignedImageCodec(12, FLAG_F32,
- 0, 4, 4, 4,
- 0, 0, 4, 8));
-
- ByteAlignedImageCodec rgb16f = new ByteAlignedImageCodec(6, FLAG_F16,
- 0, 2, 2, 2,
- 0, 0, 2, 4);
+ params.put(Format.RGB32F, new ByteAlignedImageCodec(12, FLAG_F32,
+ 0, 4, 4, 4,
+ 0, 0, 4, 8));
+
+ params.put(Format.R16F, new ByteAlignedImageCodec(2, FLAG_F16,
+ 0, 2, 0, 0,
+ 0, 0, 0, 0));
+
+ params.put(Format.RG16F, new ByteAlignedImageCodec(4, FLAG_F16,
+ 0, 2, 2, 0,
+ 0, 0, 2, 0));
+
+ ByteAlignedImageCodec rgb16f = new ByteAlignedImageCodec(6, FLAG_F16,
+ 0, 2, 2, 2,
+ 0, 0, 2, 4);
params.put(Format.RGB16F, rgb16f);
params.put(Format.RGB16F_to_RGB111110F, rgb16f);
params.put(Format.RGB16F_to_RGB9E5, rgb16f);
diff --git a/jme3-core/src/main/resources/com/jme3/asset/Desktop.cfg b/jme3-core/src/main/resources/com/jme3/asset/Desktop.cfg
index bf18898811..b350b696f6 100644
--- a/jme3-core/src/main/resources/com/jme3/asset/Desktop.cfg
+++ b/jme3-core/src/main/resources/com/jme3/asset/Desktop.cfg
@@ -1,5 +1,4 @@
INCLUDE com/jme3/asset/General.cfg
# Desktop-specific loaders
-LOADER com.jme3.texture.plugins.AWTLoader : jpg, bmp, gif, png, jpeg
LOADER com.jme3.cursors.plugins.CursorLoader : ani, cur, ico
diff --git a/jme3-core/src/main/resources/com/jme3/asset/General.cfg b/jme3-core/src/main/resources/com/jme3/asset/General.cfg
index 0cec3717e0..a7c8664e35 100644
--- a/jme3-core/src/main/resources/com/jme3/asset/General.cfg
+++ b/jme3-core/src/main/resources/com/jme3/asset/General.cfg
@@ -9,8 +9,6 @@ LOADER com.jme3.material.plugins.ShaderNodeDefinitionLoader : j3sn
LOADER com.jme3.font.plugins.BitmapFontLoader : fnt
LOADER com.jme3.texture.plugins.DDSLoader : dds
LOADER com.jme3.texture.plugins.PFMLoader : pfm
-LOADER com.jme3.texture.plugins.HDRLoader : hdr
-LOADER com.jme3.texture.plugins.TGALoader : tga
LOADER com.jme3.export.binary.BinaryLoader : j3o
LOADER com.jme3.export.binary.BinaryLoader : j3f
LOADER com.jme3.scene.plugins.OBJLoader : obj
@@ -23,4 +21,6 @@ LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, geom, tsctrl, tseval, gl
LOADER com.jme3.scene.plugins.gltf.GltfLoader : gltf
LOADER com.jme3.scene.plugins.gltf.BinLoader : bin
LOADER com.jme3.scene.plugins.gltf.GlbLoader : glb
+LOADER com.jme3.texture.plugins.StbImageLoader : jpg, bmp, gif, png, jpeg, tga, psd, hdr
LOADER com.jme3.audio.plugins.OGGLoader : ogg
+LOADER com.jme3.texture.plugins.WebpImageLoader : webp
\ No newline at end of file
diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/HDRLoader.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/HDRLoader.java
index e93c3b038f..0e1bf901b2 100644
--- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/HDRLoader.java
+++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/HDRLoader.java
@@ -46,6 +46,11 @@
import java.util.logging.Level;
import java.util.logging.Logger;
+/**
+ * @deprecated use {@link StbImageLoader} instead, which supports HDR images and more formats. This loader is
+ * kept for backward compatibility but may be removed in future versions.
+ */
+@Deprecated
public class HDRLoader implements AssetLoader {
private static final Logger logger = Logger.getLogger(HDRLoader.class.getName());
diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/TGALoader.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/TGALoader.java
index 6ef6272ea0..66211d1e7c 100644
--- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/TGALoader.java
+++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/TGALoader.java
@@ -46,15 +46,16 @@
import java.nio.ByteBuffer;
/**
- * TextureManager provides static methods for building a
- * Texture object. Typically, the information supplied is the
- * filename and the texture properties.
+ * TextureManager provides static methods for building a Texture object. Typically,
+ * the information supplied is the filename and the texture properties.
*
* @author Mark Powell
* @author Joshua Slack - cleaned, commented, added ability to read 16bit true color and color-mapped TGAs.
* @author Kirill Vainer - ported to jME3
* @version $Id: TGALoader.java 4131 2009-03-19 20:15:28Z blaine.dev $
+ * @deprecated use {@link StbImageLoader} instead
*/
+@Deprecated
public final class TGALoader implements AssetLoader {
// 0 - no image data in file
diff --git a/jme3-desktop/build.gradle b/jme3-desktop/build.gradle
index bfc5138e77..f6a7426712 100644
--- a/jme3-desktop/build.gradle
+++ b/jme3-desktop/build.gradle
@@ -1,3 +1,4 @@
dependencies {
api project(':jme3-core')
+ api project(':jme3-plugins')
}
\ No newline at end of file
diff --git a/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java b/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java
index 926e7305af..49e318b59e 100644
--- a/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java
+++ b/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java
@@ -47,6 +47,12 @@
import java.nio.ByteBuffer;
import javax.imageio.ImageIO;
+/**
+ * @deprecated use {@link StbImageLoader} instead, which supports more formats and is more efficient and
+ * platform agnostic. This loader is kept for backward compatibility but may be removed in future
+ * versions.
+ */
+@Deprecated
public class AWTLoader implements AssetLoader {
public static final ColorModel AWT_RGBA4444 = new DirectColorModel(16,
diff --git a/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java b/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java
index 0ba1cb10f7..9afed11229 100644
--- a/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java
+++ b/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java
@@ -150,13 +150,13 @@ public void simpleInitApp() {
// Test for normalized texture coordinates in draco
//loadModelFromPath("Models/gltf/unitSquare11x11_unsignedShortTexCoords-draco.glb");
- // Uses EXT_texture_webp - not supported yet
- //loadModelSample("SunglassesKhronos", "draco");
+ // Uses EXT_texture_webp
+ // loadModelSample("SunglassesKhronos", "draco");
+ // loadModelSample("CarConcept", "webp");
// Probably invalid model
// See https://github.com/KhronosGroup/glTF-Sample-Assets/issues/264
// loadModelSample("VirtualCity", "draco");
-
probeNode.attachChild(assets.get(0));
inputManager.addMapping("autorotate", new KeyTrigger(KeyInput.KEY_SPACE));
@@ -213,10 +213,15 @@ private void loadModelSample(String name, String type) {
path += "/glTF-Binary/";
ext = "glb";
break;
+ case "webp":
+ path += "/glTF-WEBP/";
+ ext = "gltf";
+ break;
default:
path += "/glTF/";
ext = "gltf";
break;
+
}
path += name + "." + ext;
loadModelFromPath(path);
diff --git a/jme3-plugins/build.gradle b/jme3-plugins/build.gradle
index 44e80f017f..b1e436faf5 100644
--- a/jme3-plugins/build.gradle
+++ b/jme3-plugins/build.gradle
@@ -14,5 +14,8 @@ dependencies {
implementation "org.jmonkeyengine:drako:1.4.5-jme"
implementation project(':jme3-plugins-json')
implementation project(':jme3-plugins-json-gson')
+ implementation libs.stb.image
+ implementation libs.imagewebp
+
testRuntimeOnly project(':jme3-desktop')
}
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java
index e4e504e1ef..d9c4cbf7d8 100644
--- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java
@@ -73,6 +73,7 @@ public class CustomContentManager {
defaultExtensionLoaders.put("KHR_texture_transform", TextureTransformExtensionLoader.class);
defaultExtensionLoaders.put("KHR_materials_emissive_strength", PBREmissiveStrengthExtensionLoader.class);
defaultExtensionLoaders.put("KHR_draco_mesh_compression", DracoMeshCompressionExtensionLoader.class);
+ defaultExtensionLoaders.put("EXT_texture_webp", TextureWebpExtensionLoader.class);
}
/**
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java
index 0a39689bd7..b5a0d23bda 100644
--- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java
@@ -940,8 +940,9 @@ public Texture2D readTexture(JsonObject texture, boolean flip) throws IOExceptio
}
JsonObject textureData = textures.get(textureIndex).getAsJsonObject();
- Integer sourceIndex = getAsInteger(textureData, "source");
+ Integer sourceIndex = TextureWebpExtensionLoader.getTextureSourceIndex(textureData);
Integer samplerIndex = getAsInteger(textureData, "sampler");
+ assertNotNull(sourceIndex, "Texture has no source");
texture2d = readImage(sourceIndex, flip);
@@ -1708,4 +1709,4 @@ public static void registerDefaultExtrasLoader(Class extends ExtrasLoader> loa
public static void unregisterDefaultExtrasLoader() {
CustomContentManager.defaultExtraLoaderClass = UserDataLoader.class;
}
-}
\ No newline at end of file
+}
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/TextureWebpExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/TextureWebpExtensionLoader.java
new file mode 100644
index 0000000000..63ea2c323e
--- /dev/null
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/TextureWebpExtensionLoader.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2009-2024 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.gltf;
+
+import static com.jme3.scene.plugins.gltf.GltfUtils.assertNotNull;
+import static com.jme3.scene.plugins.gltf.GltfUtils.getAsInteger;
+
+import com.jme3.plugins.json.JsonElement;
+import com.jme3.plugins.json.JsonObject;
+import java.io.IOException;
+
+/**
+ * Handles EXT_texture_webp texture source selection.
+ */
+public class TextureWebpExtensionLoader implements ExtensionLoader {
+
+ static final String EXTENSION_NAME = "EXT_texture_webp";
+
+ static Integer getTextureSourceIndex(JsonObject textureData) {
+ Integer sourceIndex = getAsInteger(textureData, "source");
+
+ JsonObject extensions = textureData.getAsJsonObject("extensions");
+ if (extensions == null) {
+ return sourceIndex;
+ }
+
+ JsonObject webpExtension = extensions.getAsJsonObject(EXTENSION_NAME);
+ if (webpExtension == null) {
+ return sourceIndex;
+ }
+
+ Integer webpSourceIndex = getAsInteger(webpExtension, "source");
+ assertNotNull(webpSourceIndex, EXTENSION_NAME + " extension has no source");
+ return webpSourceIndex;
+ }
+
+ @Override
+ public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension,
+ Object input) throws IOException {
+ return input;
+ }
+}
diff --git a/jme3-plugins/src/main/java/com/jme3/texture/plugins/StbImageLoader.java b/jme3-plugins/src/main/java/com/jme3/texture/plugins/StbImageLoader.java
new file mode 100644
index 0000000000..99258c5eeb
--- /dev/null
+++ b/jme3-plugins/src/main/java/com/jme3/texture/plugins/StbImageLoader.java
@@ -0,0 +1,146 @@
+package com.jme3.texture.plugins;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+import org.ngengine.stbimage.StbDecoder;
+import org.ngengine.stbimage.StbImage;
+import org.ngengine.stbimage.StbImageInfo;
+import org.ngengine.stbimage.StbImageResult;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.TextureKey;
+import com.jme3.export.binary.ByteUtils;
+import com.jme3.math.FastMath;
+import com.jme3.texture.Image;
+import com.jme3.texture.image.ColorSpace;
+import com.jme3.util.BufferUtils;
+
+public class StbImageLoader implements AssetLoader {
+ private final StbImage stbImage = new StbImage(BufferUtils::createByteBuffer);
+
+ @Override
+ public Object load(AssetInfo assetInfo) throws IOException {
+ AssetKey> key = assetInfo.getKey();
+ TextureKey textureKey = null;
+ if(key instanceof TextureKey){
+ textureKey = (TextureKey) key;
+ }
+
+ boolean flip = textureKey != null && textureKey.isFlipY();
+
+ try(InputStream is = assetInfo.openStream()) {
+ byte[] data = ByteUtils.getByteContent(is);
+ ByteBuffer buffer = BufferUtils.createByteBuffer(data);
+ stbImage.setConvertIphonePngToRgb(true);
+ stbImage.setUnpremultiplyOnLoad(true);
+
+ StbDecoder decoder = stbImage.getDecoder(buffer, flip);
+ StbImageInfo info = decoder.info();
+ int channels = info.getChannels();
+
+ int width = info.getWidth();
+ int height = info.getHeight();
+ int desiredChannels = channels;
+
+ boolean is16bit = info.is16Bit();
+ boolean isFloat = info.isFloat();
+ boolean sRGB = false;
+
+
+ Image.Format jmeFormat;
+ if (is16bit || isFloat) {
+ switch (channels) {
+ case 1:
+ jmeFormat = Image.Format.R16F;
+ desiredChannels = 1;
+ break;
+ case 2:
+ jmeFormat = Image.Format.RG16F;
+ desiredChannels = 2;
+ break;
+ case 3:
+ jmeFormat = Image.Format.RGB16F;
+ desiredChannels = 3;
+ break;
+ case 4:
+ jmeFormat = Image.Format.RGBA16F;
+ desiredChannels = 4;
+ break;
+ default:
+ throw new IOException("Unsupported number of channels: " + channels);
+
+ }
+ } else {
+ switch (channels) {
+ case 1:
+ jmeFormat = Image.Format.Luminance8;
+ desiredChannels = 1;
+ break;
+ case 2:
+ jmeFormat = Image.Format.Luminance8Alpha8;
+ desiredChannels = 2;
+ break;
+ case 3:
+ jmeFormat = Image.Format.RGB8;
+ desiredChannels = 3;
+ sRGB = true;
+ break;
+ case 4:
+ jmeFormat = Image.Format.RGBA8;
+ desiredChannels = 4;
+ sRGB = true;
+ break;
+ default:
+ throw new IOException("Unsupported number of channels: " + channels);
+ }
+ }
+
+ StbImageResult imgData;
+ if(isFloat){
+ imgData = decoder.loadf(desiredChannels);
+ } else if(is16bit){
+ imgData = decoder.load16(desiredChannels);
+ } else {
+ imgData = decoder.load(desiredChannels);
+ }
+
+ ByteBuffer jmeImageBuffer = convertImageData(imgData, jmeFormat);
+
+ Image jmeImage = new Image(jmeFormat, width, height, jmeImageBuffer, sRGB?ColorSpace.sRGB:ColorSpace.Linear);
+ return jmeImage;
+ }
+ }
+
+ private ByteBuffer convertImageData(StbImageResult imgData, Image.Format jmeFormat) {
+ int outputSize = jmeFormat.getBitsPerPixel() / 8 * imgData.getWidth() * imgData.getHeight();
+ ByteBuffer jmeImageBuffer = BufferUtils.createByteBuffer(outputSize);
+ ByteBuffer source = imgData.getData().duplicate();
+ source.order(imgData.getData().order());
+ source.position(0).limit(imgData.getDataSize());
+
+ if (!imgData.is16Bit() && !imgData.isFloat()) {
+ jmeImageBuffer.put(source);
+ jmeImageBuffer.flip();
+ return jmeImageBuffer;
+ }
+
+ int sampleCount = imgData.getWidth() * imgData.getHeight() * imgData.getRequestedChannels();
+ if (imgData.is16Bit()) {
+ for (int i = 0; i < sampleCount; i++) {
+ float value = (source.getShort() & 0xFFFF) / 65535f;
+ jmeImageBuffer.putShort(FastMath.convertFloatToHalf(value));
+ }
+ } else {
+ for (int i = 0; i < sampleCount; i++) {
+ jmeImageBuffer.putShort(FastMath.convertFloatToHalf(source.getFloat()));
+ }
+ }
+
+ jmeImageBuffer.flip();
+ return jmeImageBuffer;
+ }
+}
diff --git a/jme3-plugins/src/main/java/com/jme3/texture/plugins/WebpImageLoader.java b/jme3-plugins/src/main/java/com/jme3/texture/plugins/WebpImageLoader.java
new file mode 100644
index 0000000000..3244a6abd1
--- /dev/null
+++ b/jme3-plugins/src/main/java/com/jme3/texture/plugins/WebpImageLoader.java
@@ -0,0 +1,56 @@
+package com.jme3.texture.plugins;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+import org.ngengine.webp.decoder.DecodedWebP;
+import org.ngengine.webp.decoder.WebPDecoder;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.TextureKey;
+import com.jme3.export.binary.ByteUtils;
+import com.jme3.texture.Image;
+import com.jme3.texture.image.ColorSpace;
+import com.jme3.util.BufferUtils;
+
+public class WebpImageLoader implements AssetLoader{
+
+ @Override
+ public Object load(AssetInfo assetInfo) throws IOException {
+ try{
+ AssetKey> key = assetInfo.getKey();
+ TextureKey textureKey = null;
+ if(key instanceof TextureKey){
+ textureKey = (TextureKey) key;
+ }
+ boolean flip = textureKey != null && textureKey.isFlipY();
+ try(InputStream is = assetInfo.openStream()) {
+ byte[] data = ByteUtils.getByteContent(is);
+ DecodedWebP decoded = WebPDecoder.decode(data, BufferUtils::createByteBuffer);
+ int w = decoded.width;
+ int h = decoded.height;
+ ByteBuffer rgba = decoded.rgba;
+ if(flip){
+ ByteBuffer flipped = BufferUtils.createByteBuffer(rgba.capacity());
+ for(int y = h - 1; y >= 0; y--){
+ int rowStart = y * w * 4;
+ for(int x = 0; x < w * 4; x++){
+ flipped.put(rgba.get(rowStart + x));
+ }
+ }
+ flipped.flip();
+ rgba = flipped;
+ }
+ Image jmeImage = new Image(Image.Format.RGBA8, w, h, rgba, ColorSpace.sRGB);
+ return jmeImage;
+ }
+ }catch(Exception e){
+ throw new IOException("Failed to load WebP image", e);
+ }
+
+ }
+
+}
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.animation.TestIssue2076.testIssue2076_f1.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.animation.TestIssue2076.testIssue2076_f1.png
index f4cbc42002..d52542ca4f 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.animation.TestIssue2076.testIssue2076_f1.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.animation.TestIssue2076.testIssue2076_f1.png differ
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.effects.TestIssue1773.testIssue1773_localSpace_f45.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.effects.TestIssue1773.testIssue1773_localSpace_f45.png
index bdeeb01eb1..6f2e398f43 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.effects.TestIssue1773.testIssue1773_localSpace_f45.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.effects.TestIssue1773.testIssue1773_localSpace_f45.png differ
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.effects.TestIssue1773.testIssue1773_worldSpace_f45.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.effects.TestIssue1773.testIssue1773_worldSpace_f45.png
index 620d7bb3f2..a5793cebc9 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.effects.TestIssue1773.testIssue1773_worldSpace_f45.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.effects.TestIssue1773.testIssue1773_worldSpace_f45.png differ
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.export.TestOgreConvert.testOgreConvert_f1.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.export.TestOgreConvert.testOgreConvert_f1.png
index 61bd29c0b4..25781bd290 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.export.TestOgreConvert.testOgreConvert_f1.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.export.TestOgreConvert.testOgreConvert_f1.png differ
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.export.TestOgreConvert.testOgreConvert_f5.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.export.TestOgreConvert.testOgreConvert_f5.png
index 603680a81d..c92b216404 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.export.TestOgreConvert.testOgreConvert_f5.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.export.TestOgreConvert.testOgreConvert_f5.png differ
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_DefaultDirectionalLight_f12.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_DefaultDirectionalLight_f12.png
index 28a15327ec..72783ea3e3 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_DefaultDirectionalLight_f12.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_DefaultDirectionalLight_f12.png differ
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_HighRoughness_f12.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_HighRoughness_f12.png
index 0a67bb7b0c..61d206486d 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_HighRoughness_f12.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_HighRoughness_f12.png differ
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_LowRoughness_f12.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_LowRoughness_f12.png
index 9ced70ea22..8f593f6256 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_LowRoughness_f12.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_LowRoughness_f12.png differ
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_UpdatedDirectionalLight_f12.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_UpdatedDirectionalLight_f12.png
index 90dbfa7414..0dc0aa2b35 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_UpdatedDirectionalLight_f12.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_UpdatedDirectionalLight_f12.png differ
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRSimple.testPBRSimple_WithRealtimeBaking_f10.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRSimple.testPBRSimple_WithRealtimeBaking_f10.png
index e5a00bf998..cdbb2427a8 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRSimple.testPBRSimple_WithRealtimeBaking_f10.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRSimple.testPBRSimple_WithRealtimeBaking_f10.png differ
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRSimple.testPBRSimple_WithoutRealtimeBaking_f10.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRSimple.testPBRSimple_WithoutRealtimeBaking_f10.png
index e5a00bf998..cdbb2427a8 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRSimple.testPBRSimple_WithoutRealtimeBaking_f10.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRSimple.testPBRSimple_WithoutRealtimeBaking_f10.png differ
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.post.TestCartoonEdge.testCartoonEdge_f1.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.post.TestCartoonEdge.testCartoonEdge_f1.png
index 0623641582..9149cc1d74 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.post.TestCartoonEdge.testCartoonEdge_f1.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.post.TestCartoonEdge.testCartoonEdge_f1.png differ
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.post.TestFog.testFog_f1.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.post.TestFog.testFog_f1.png
index 39ac7fa79f..658b0c2d27 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.post.TestFog.testFog_f1.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.post.TestFog.testFog_f1.png differ
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.post.TestLightScattering.testLightScattering_f1.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.post.TestLightScattering.testLightScattering_f1.png
index f8aff97dfd..a32b8322f5 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.post.TestLightScattering.testLightScattering_f1.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.post.TestLightScattering.testLightScattering_f1.png differ
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_NormalMap_f5.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_NormalMap_f5.png
index 791339d4c3..a7c42c0bcd 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_NormalMap_f5.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_NormalMap_f5.png differ
diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.water.TestPostWater.testPostWater_f1.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.water.TestPostWater.testPostWater_f1.png
index cfc9503619..8fc09b6554 100644
Binary files a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.water.TestPostWater.testPostWater_f1.png and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.water.TestPostWater.testPostWater_f1.png differ