Skip to content

Commit a9e8462

Browse files
Copilotriccardobl
andauthored
Add flipY option to StbImageLoader public API (#2758)
* Initial plan * Add flipY option to StbImageLoader, update MipMapGenerator and TestBitmapFontLayout" Agent-Logs-Url: https://github.com/jMonkeyEngine/jmonkeyengine/sessions/ecd90ba7-f7da-4527-8100-c0bdbf4b65ab Co-authored-by: riccardobl <4943530+riccardobl@users.noreply.github.com> * Fix code style: add spaces after if keyword in StbImageLoader Agent-Logs-Url: https://github.com/jMonkeyEngine/jmonkeyengine/sessions/ecd90ba7-f7da-4527-8100-c0bdbf4b65ab Co-authored-by: riccardobl <4943530+riccardobl@users.noreply.github.com> * Revert MipMapGenerator.java to keep AWTLoader as requested Agent-Logs-Url: https://github.com/jMonkeyEngine/jmonkeyengine/sessions/6069a6e1-4ac9-47f2-a2c5-356b19b5f9c1 Co-authored-by: riccardobl <4943530+riccardobl@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: riccardobl <4943530+riccardobl@users.noreply.github.com>
1 parent 902769b commit a9e8462

2 files changed

Lines changed: 114 additions & 82 deletions

File tree

jme3-examples/src/main/java/jme3test/gui/TestBitmapFontLayout.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,11 @@
4141
import java.awt.Graphics2D;
4242
import java.awt.RenderingHints;
4343
import java.awt.image.BufferedImage;
44+
import java.io.ByteArrayInputStream;
45+
import java.io.ByteArrayOutputStream;
4446
import java.io.IOException;
4547
import java.util.*;
48+
import javax.imageio.ImageIO;
4649

4750
import com.jme3.app.DebugKeysAppState;
4851
import com.jme3.app.StatsAppState;
@@ -64,7 +67,7 @@
6467
import com.jme3.texture.Image;
6568
import com.jme3.texture.Texture;
6669
import com.jme3.texture.Texture2D;
67-
import com.jme3.texture.plugins.AWTLoader;
70+
import com.jme3.texture.plugins.StbImageLoader;
6871

6972
/**
7073
*
@@ -152,8 +155,14 @@ private Texture renderAwtFont( TestConfig test, int width, int height, BitmapFon
152155

153156
g2.dispose();
154157

155-
Image jmeImage = new AWTLoader().load(image, true);
156-
return new Texture2D(jmeImage);
158+
try {
159+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
160+
ImageIO.write(image, "png", baos);
161+
Image jmeImage = new StbImageLoader().load(new ByteArrayInputStream(baos.toByteArray()), true);
162+
return new Texture2D(jmeImage);
163+
} catch (IOException e) {
164+
throw new RuntimeException("Failed to convert AWT image to jME Image", e);
165+
}
157166
}
158167

159168
private Node createVisual( TestConfig test ) {

jme3-plugins/src/main/java/com/jme3/texture/plugins/StbImageLoader.java

Lines changed: 102 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,107 @@
2222
public class StbImageLoader implements AssetLoader {
2323
private final StbImage stbImage = new StbImage(BufferUtils::createByteBuffer);
2424

25+
/**
26+
* Loads an image from an {@link InputStream} with optional vertical flip.
27+
*
28+
* @param in the input stream containing the encoded image data (e.g. PNG, JPEG)
29+
* @param flipY {@code true} to flip the image vertically on load
30+
* @return the decoded {@link Image}
31+
* @throws IOException if the stream cannot be read or the image format is unsupported
32+
*/
33+
public Image load(InputStream in, boolean flipY) throws IOException {
34+
byte[] data = ByteUtils.getByteContent(in);
35+
return load(data, flipY);
36+
}
37+
38+
/**
39+
* Loads an image from a byte array with optional vertical flip.
40+
*
41+
* @param data the raw encoded image data (e.g. PNG, JPEG)
42+
* @param flipY {@code true} to flip the image vertically on load
43+
* @return the decoded {@link Image}
44+
* @throws IOException if the image data cannot be decoded or the format is unsupported
45+
*/
46+
public Image load(byte[] data, boolean flipY) throws IOException {
47+
ByteBuffer buffer = ByteBuffer.wrap(data);
48+
stbImage.setConvertIphonePngToRgb(true);
49+
stbImage.setUnpremultiplyOnLoad(true);
50+
51+
StbDecoder decoder = stbImage.getDecoder(buffer, flipY);
52+
StbImageInfo info = decoder.info();
53+
int channels = info.getChannels();
54+
55+
int width = info.getWidth();
56+
int height = info.getHeight();
57+
int desiredChannels = channels;
58+
59+
boolean is16bit = info.is16Bit();
60+
boolean isFloat = info.isFloat();
61+
boolean sRGB = false;
62+
63+
64+
Image.Format jmeFormat;
65+
if (is16bit || isFloat) {
66+
switch (channels) {
67+
case 1:
68+
jmeFormat = Image.Format.Luminance16F;
69+
desiredChannels = 1;
70+
break;
71+
case 2:
72+
jmeFormat = Image.Format.Luminance16FAlpha16F;
73+
desiredChannels = 2;
74+
break;
75+
case 3:
76+
jmeFormat = Image.Format.RGB16F;
77+
desiredChannels = 3;
78+
break;
79+
case 4:
80+
jmeFormat = Image.Format.RGBA16F;
81+
desiredChannels = 4;
82+
break;
83+
default:
84+
throw new IOException("Unsupported number of channels: " + channels);
85+
86+
}
87+
} else {
88+
switch (channels) {
89+
case 1:
90+
jmeFormat = Image.Format.Luminance8;
91+
desiredChannels = 1;
92+
break;
93+
case 2:
94+
jmeFormat = Image.Format.Luminance8Alpha8;
95+
desiredChannels = 2;
96+
break;
97+
case 3:
98+
jmeFormat = Image.Format.RGB8;
99+
desiredChannels = 3;
100+
sRGB = true;
101+
break;
102+
case 4:
103+
jmeFormat = Image.Format.RGBA8;
104+
desiredChannels = 4;
105+
sRGB = true;
106+
break;
107+
default:
108+
throw new IOException("Unsupported number of channels: " + channels);
109+
}
110+
}
111+
112+
StbImageResult imgData;
113+
if (isFloat){
114+
imgData = decoder.loadf(desiredChannels);
115+
} else if (is16bit){
116+
imgData = decoder.load16(desiredChannels);
117+
} else {
118+
imgData = decoder.load(desiredChannels);
119+
}
120+
121+
ByteBuffer jmeImageBuffer = convertImageData(imgData, jmeFormat);
122+
123+
return new Image(jmeFormat, width, height, jmeImageBuffer, sRGB ? ColorSpace.sRGB : ColorSpace.Linear);
124+
}
125+
25126
@Override
26127
public Object load(AssetInfo assetInfo) throws IOException {
27128
AssetKey<?> key = assetInfo.getKey();
@@ -33,85 +134,7 @@ public Object load(AssetInfo assetInfo) throws IOException {
33134
boolean flip = textureKey != null && textureKey.isFlipY();
34135

35136
try(InputStream is = assetInfo.openStream()) {
36-
byte[] data = ByteUtils.getByteContent(is);
37-
ByteBuffer buffer = ByteBuffer.wrap(data);
38-
stbImage.setConvertIphonePngToRgb(true);
39-
stbImage.setUnpremultiplyOnLoad(true);
40-
41-
StbDecoder decoder = stbImage.getDecoder(buffer, flip);
42-
StbImageInfo info = decoder.info();
43-
int channels = info.getChannels();
44-
45-
int width = info.getWidth();
46-
int height = info.getHeight();
47-
int desiredChannels = channels;
48-
49-
boolean is16bit = info.is16Bit();
50-
boolean isFloat = info.isFloat();
51-
boolean sRGB = false;
52-
53-
54-
Image.Format jmeFormat;
55-
if (is16bit || isFloat) {
56-
switch (channels) {
57-
case 1:
58-
jmeFormat = Image.Format.Luminance16F;
59-
desiredChannels = 1;
60-
break;
61-
case 2:
62-
jmeFormat = Image.Format.Luminance16FAlpha16F;
63-
desiredChannels = 2;
64-
break;
65-
case 3:
66-
jmeFormat = Image.Format.RGB16F;
67-
desiredChannels = 3;
68-
break;
69-
case 4:
70-
jmeFormat = Image.Format.RGBA16F;
71-
desiredChannels = 4;
72-
break;
73-
default:
74-
throw new IOException("Unsupported number of channels: " + channels);
75-
76-
}
77-
} else {
78-
switch (channels) {
79-
case 1:
80-
jmeFormat = Image.Format.Luminance8;
81-
desiredChannels = 1;
82-
break;
83-
case 2:
84-
jmeFormat = Image.Format.Luminance8Alpha8;
85-
desiredChannels = 2;
86-
break;
87-
case 3:
88-
jmeFormat = Image.Format.RGB8;
89-
desiredChannels = 3;
90-
sRGB = true;
91-
break;
92-
case 4:
93-
jmeFormat = Image.Format.RGBA8;
94-
desiredChannels = 4;
95-
sRGB = true;
96-
break;
97-
default:
98-
throw new IOException("Unsupported number of channels: " + channels);
99-
}
100-
}
101-
102-
StbImageResult imgData;
103-
if(isFloat){
104-
imgData = decoder.loadf(desiredChannels);
105-
} else if(is16bit){
106-
imgData = decoder.load16(desiredChannels);
107-
} else {
108-
imgData = decoder.load(desiredChannels);
109-
}
110-
111-
ByteBuffer jmeImageBuffer = convertImageData(imgData, jmeFormat);
112-
113-
Image jmeImage = new Image(jmeFormat, width, height, jmeImageBuffer, sRGB ? ColorSpace.sRGB : ColorSpace.Linear);
114-
return jmeImage;
137+
return load(is, flip);
115138
}
116139
}
117140

0 commit comments

Comments
 (0)