diff --git a/.gitignore b/.gitignore index 39df9edc..3b4fc22c 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,5 @@ out/ # Dear ImGui runtime layout file (written by ImGui on exit) imgui.ini + +bin/libimgui-java64.so diff --git a/bin/libimgui-java64.so b/bin/libimgui-java64.so deleted file mode 100644 index 55cce7eb..00000000 Binary files a/bin/libimgui-java64.so and /dev/null differ diff --git a/example/src/main/java/Main.java b/example/src/main/java/Main.java index 59822cb5..1f503939 100644 --- a/example/src/main/java/Main.java +++ b/example/src/main/java/Main.java @@ -2,11 +2,16 @@ import imgui.ImFontGlyphRangesBuilder; import imgui.ImGui; import imgui.ImGuiIO; +import imgui.ImGuiPlatformIO; +import imgui.ImTextureData; import imgui.app.Application; import imgui.app.Configuration; +import imgui.flag.ImGuiBackendFlags; import imgui.flag.ImGuiConfigFlags; import imgui.flag.ImGuiInputTextFlags; import imgui.flag.ImGuiWindowFlags; +import imgui.flag.ImTextureFormat; +import imgui.flag.ImTextureStatus; import imgui.type.ImString; import java.io.IOException; @@ -68,7 +73,9 @@ private void initFonts(final ImGuiIO io) { io.getFonts().addFontFromMemoryTTF(loadFromResources("NotoSansCJKjp-Medium.otf"), 14, fontConfig, glyphRanges); // japanese glyphs io.getFonts().addFontFromMemoryTTF(loadFromResources("fa-regular-400.ttf"), 14, fontConfig, glyphRanges); // font awesome io.getFonts().addFontFromMemoryTTF(loadFromResources("fa-solid-900.ttf"), 14, fontConfig, glyphRanges); // font awesome - io.getFonts().build(); + + // NB: With the 1.92 texture-management backend (ImGuiBackendFlags_RendererHasTextures) the atlas is baked + // lazily by the renderer — you must NOT call ImFontAtlas::build() yourself, or Dear ImGui will assert. fontConfig.destroy(); } @@ -100,10 +107,73 @@ public void process() { ImGui.separator(); ImGui.text("Extra"); Extra.show(this); + + ImGui.separator(); + showTextureManagement(); } ImGui.end(); } + /** + * Demonstrates Dear ImGui 1.92's texture-management system (ImGuiBackendFlags_RendererHasTextures). + *
+ * The GL3 backend now drives texture create/update/destroy through {@link ImTextureData}, so the font atlas is + * uploaded (and incrementally updated) automatically each frame. Here we simply inspect the live texture list that + * the backend maintains and render the managed atlas texture inline. + */ + private void showTextureManagement() { + if (!ImGui.collapsingHeader("Texture Management")) { + return; + } + + final ImGuiIO io = ImGui.getIO(); + final boolean hasTextures = io.hasBackendFlags(ImGuiBackendFlags.RendererHasTextures); + ImGui.text("RendererHasTextures: " + (hasTextures ? "enabled" : "disabled")); + + final ImGuiPlatformIO platformIO = ImGui.getPlatformIO(); + final int texturesCount = platformIO.getTexturesSize(); + ImGui.text("Textures managed by Dear ImGui: " + texturesCount); + + for (int i = 0; i < texturesCount; i++) { + final ImTextureData tex = platformIO.getTextures(i); + ImGui.bulletText(String.format( + "#%d %dx%d %s status=%s texID=%d", + tex.getUniqueId(), tex.getWidth(), tex.getHeight(), + formatName(tex.getFormat()), statusName(tex.getStatus()), tex.getTexID())); + + // Render any RGBA texture that has already been uploaded (the font atlas is the usual one). + if (tex.getStatus() == ImTextureStatus.OK && tex.getTexID() != 0 && tex.getFormat() == ImTextureFormat.RGBA32) { + final float preview = 256.0f; + final float aspect = tex.getHeight() / (float) tex.getWidth(); + ImGui.image(tex.getTexID(), preview, preview * aspect); + } + } + } + + private static String formatName(final int format) { + if (format == ImTextureFormat.RGBA32) { + return "RGBA32"; + } else if (format == ImTextureFormat.Alpha8) { + return "Alpha8"; + } + return "Unknown(" + format + ")"; + } + + private static String statusName(final int status) { + if (status == ImTextureStatus.OK) { + return "OK"; + } else if (status == ImTextureStatus.WantCreate) { + return "WantCreate"; + } else if (status == ImTextureStatus.WantUpdates) { + return "WantUpdates"; + } else if (status == ImTextureStatus.WantDestroy) { + return "WantDestroy"; + } else if (status == ImTextureStatus.Destroyed) { + return "Destroyed"; + } + return "Unknown(" + status + ")"; + } + private static byte[] loadFromResources(String name) { try { return Files.readAllBytes(Paths.get(Main.class.getResource(name).toURI())); diff --git a/imgui-binding/src/generated/java/imgui/ImDrawData.java b/imgui-binding/src/generated/java/imgui/ImDrawData.java index 75e71898..98829032 100644 --- a/imgui-binding/src/generated/java/imgui/ImDrawData.java +++ b/imgui-binding/src/generated/java/imgui/ImDrawData.java @@ -17,6 +17,8 @@ public final class ImDrawData extends ImGuiStruct { private static final int RESIZE_FACTOR = 5_000; private static ByteBuffer dataBuffer = ByteBuffer.allocateDirect(25_000).order(ByteOrder.nativeOrder()); + private static final ImTextureData _GETTEXTURES_1 = new ImTextureData(0); + public ImDrawData(final long ptr) { super(ptr); } @@ -134,6 +136,30 @@ public ByteBuffer getCmdListVtxBufferData(final int cmdListIdx) { memcpy(vtxBuffer, THIS->CmdLists[cmdListIdx]->VtxBuffer.Data, vtxBufferCapacity); */ + /** + * Number of textures to update before rendering this draw data. Most of the time the list is shared by all + * ImDrawData, has only one texture and does not need any update. This almost always mirrors + * {@link ImGuiPlatformIO#getTexturesSize()}. May be zero if texture updates are disabled (list set to NULL). + */ + public native int getTexturesSize(); /* + return THIS->Textures != NULL ? THIS->Textures->Size : 0; + */ + + /** + * Texture to update at the given index. The returned value is a shared instance, valid only until the next call to + * this method. + * + * @param idx index in {@code [0, getTexturesSize())} + */ + public ImTextureData getTextures(final int idx) { + _GETTEXTURES_1.ptr = nGetTextures(idx); + return _GETTEXTURES_1; + } + + private native long nGetTextures(int idx); /* + return (uintptr_t)(*THIS->Textures)[idx]; + */ + public static native int sizeOfImDrawVert(); /* return (int)sizeof(ImDrawVert); */ diff --git a/imgui-binding/src/generated/java/imgui/ImGuiPlatformIO.java b/imgui-binding/src/generated/java/imgui/ImGuiPlatformIO.java index 4dd498d5..31c254f0 100644 --- a/imgui-binding/src/generated/java/imgui/ImGuiPlatformIO.java +++ b/imgui-binding/src/generated/java/imgui/ImGuiPlatformIO.java @@ -73,6 +73,7 @@ public final class ImGuiPlatformIO extends ImGuiStruct { private static final ImGuiViewport TMP_VIEWPORT = new ImGuiViewport(0); private static final ImGuiPlatformMonitor TMP_MONITOR = new ImGuiPlatformMonitor(0); private static final ImVec2 TMP_IM_VEC2 = new ImVec2(); + private static final ImTextureData TMP_TEXTURE_DATA = new ImTextureData(0); public ImGuiPlatformIO(final long ptr) { super(ptr); @@ -545,4 +546,63 @@ public ImGuiViewport getViewports(final int idx) { private native long nGetViewports(int idx); /* return (uintptr_t)IMGUI_PLATFORM_IO->Viewports[idx]; */ + + //------------------------------------------------------------------ + // Textures list (updated by dear imgui, honored by the renderer backend) + //------------------------------------------------------------------ + + /** + * Maximum texture width the renderer backend supports. Set by the renderer backend during initialization so the + * core library can split the font atlas accordingly. Zero means unbounded. + */ + public native int getRendererTextureMaxWidth(); /* + return IMGUI_PLATFORM_IO->Renderer_TextureMaxWidth; + */ + + /** + * Maximum texture width the renderer backend supports. Set by the renderer backend during initialization so the + * core library can split the font atlas accordingly. Zero means unbounded. + */ + public native void setRendererTextureMaxWidth(int value); /* + IMGUI_PLATFORM_IO->Renderer_TextureMaxWidth = value; + */ + + /** + * Maximum texture height the renderer backend supports. Set by the renderer backend during initialization so the + * core library can split the font atlas accordingly. Zero means unbounded. + */ + public native int getRendererTextureMaxHeight(); /* + return IMGUI_PLATFORM_IO->Renderer_TextureMaxHeight; + */ + + /** + * Maximum texture height the renderer backend supports. Set by the renderer backend during initialization so the + * core library can split the font atlas accordingly. Zero means unbounded. + */ + public native void setRendererTextureMaxHeight(int value); /* + IMGUI_PLATFORM_IO->Renderer_TextureMaxHeight = value; + */ + + /** + * Number of textures used by Dear ImGui (most often one). The renderer backend iterates this list to + * create/update/destroy textures. + */ + public native int getTexturesSize(); /* + return IMGUI_PLATFORM_IO->Textures.Size; + */ + + /** + * Texture at the given index. The returned value is a shared instance, valid only until the next call to this + * method. + * + * @param idx index in {@code [0, getTexturesSize())} + */ + public ImTextureData getTextures(final int idx) { + TMP_TEXTURE_DATA.ptr = nGetTextures(idx); + return TMP_TEXTURE_DATA; + } + + private native long nGetTextures(int idx); /* + return (uintptr_t)IMGUI_PLATFORM_IO->Textures[idx]; + */ } diff --git a/imgui-binding/src/generated/java/imgui/ImTextureData.java b/imgui-binding/src/generated/java/imgui/ImTextureData.java new file mode 100644 index 00000000..7f7b5253 --- /dev/null +++ b/imgui-binding/src/generated/java/imgui/ImTextureData.java @@ -0,0 +1,311 @@ +package imgui; + +import imgui.binding.ImGuiStruct; +import imgui.flag.ImTextureFormat; +import imgui.flag.ImTextureStatus; + +import java.nio.ByteBuffer; + + +/** + * Specs and pixel storage for a texture used by Dear ImGui. + *
+ * This is only useful for (1) core library and (2) renderer backends. End-user/applications do not need to care about + * this. Renderer Backends will create a GPU-side version of this and communicate back through {@link #setTexID(long)} + * and {@link #setStatus(int)}. + *
+ * A texture stores two identifiers: {@code TexID} (the lower-level backend identifier stored in draw commands) and
+ * {@code BackendUserData} (higher-level opaque storage for the backend's own book-keeping).
+ */
+public final class ImTextureData extends ImGuiStruct {
+ private static final ImTextureRect TMP_USED_RECT = new ImTextureRect(0);
+ private static final ImTextureRect TMP_UPDATE_RECT = new ImTextureRect(0);
+ private static final ImTextureRect TMP_UPDATE = new ImTextureRect(0);
+
+ public ImTextureData(final long ptr) {
+ super(ptr);
+ }
+
+ /*JNI
+ #include "_common.h"
+ #define THIS ((ImTextureData*)STRUCT_PTR)
+ */
+
+
+ /**
+ * [DEBUG] Sequential index to facilitate identifying a texture when debugging/printing. Unique per atlas.
+ */
+ public int getUniqueId() {
+ return nGetUniqueId();
+ }
+
+ private native int nGetUniqueId(); /*
+ return THIS->UniqueID;
+ */
+
+
+
+ /**
+ * Texture status, one of the {@link ImTextureStatus} values. Always use {@link #setStatus(int)} to modify!
+ */
+ public int getStatus() {
+ return nGetStatus();
+ }
+
+ private native int nGetStatus(); /*
+ return THIS->Status;
+ */
+
+ /**
+ * Set the texture status. Called by the renderer backend after honoring a texture request.
+ *
+ * @param status
+ * one of the {@link ImTextureStatus} values
+ */
+ public void setStatus(final int status) {
+ nSetStatus(status);
+ }
+
+ private native void nSetStatus(int status); /*
+ THIS->SetStatus(static_cast
+ * When a texture is in {@code ImTextureStatus_WantUpdates} state, we provide a list of individual rectangles to copy to
+ * the graphics system. You may use {@link ImTextureData#getUpdates(int)} for the list, or
+ * {@link ImTextureData#getUpdateRect()} for a single bounding box.
+ */
+public final class ImTextureRect extends ImGuiStruct {
+ public ImTextureRect(final long ptr) {
+ super(ptr);
+ }
+
+ /*JNI
+ #include "_common.h"
+ #define THIS ((ImTextureRect*)STRUCT_PTR)
+ */
+
+ /**
+ * Upper-left x coordinate of rectangle
+ */
+ public int getX() {
+ return nGetX();
+ }
+
+ private native int nGetX(); /*
+ return THIS->x;
+ */
+
+ /**
+ * Upper-left y coordinate of rectangle
+ */
+ public int getY() {
+ return nGetY();
+ }
+
+ private native int nGetY(); /*
+ return THIS->y;
+ */
+
+ /**
+ * Width of rectangle (in pixels).
+ */
+ public int getW() {
+ return nGetW();
+ }
+
+ private native int nGetW(); /*
+ return THIS->w;
+ */
+
+ /**
+ * Height of rectangle (in pixels).
+ */
+ public int getH() {
+ return nGetH();
+ }
+
+ private native int nGetH(); /*
+ return THIS->h;
+ */
+
+ /*JNI
+ #undef THIS
+ */
+}
diff --git a/imgui-binding/src/generated/java/imgui/extension/texteditor/TextEditor.java b/imgui-binding/src/generated/java/imgui/extension/texteditor/TextEditor.java
index 1cb067b5..445fbe4a 100644
--- a/imgui-binding/src/generated/java/imgui/extension/texteditor/TextEditor.java
+++ b/imgui-binding/src/generated/java/imgui/extension/texteditor/TextEditor.java
@@ -2,16 +2,10 @@
import imgui.ImVec2;
import imgui.binding.ImGuiStructDestroyable;
-
-
-
-
-
import imgui.extension.texteditor.flag.TextEditorColor;
import imgui.extension.texteditor.flag.TextEditorScroll;
import imgui.internal.ImGuiContext;
-
public final class TextEditor extends ImGuiStructDestroyable {
public TextEditor() {
super();
diff --git a/imgui-binding/src/generated/java/imgui/extension/texteditor/TextEditorLanguage.java b/imgui-binding/src/generated/java/imgui/extension/texteditor/TextEditorLanguage.java
index 401d75f1..b8b759ab 100644
--- a/imgui-binding/src/generated/java/imgui/extension/texteditor/TextEditorLanguage.java
+++ b/imgui-binding/src/generated/java/imgui/extension/texteditor/TextEditorLanguage.java
@@ -2,11 +2,6 @@
import imgui.binding.ImGuiStruct;
-
-
-
-
-
public final class TextEditorLanguage extends ImGuiStruct {
public TextEditorLanguage(final long ptr) {
super(ptr);
diff --git a/imgui-binding/src/generated/java/imgui/flag/ImTextureFormat.java b/imgui-binding/src/generated/java/imgui/flag/ImTextureFormat.java
new file mode 100644
index 00000000..49c9ea9d
--- /dev/null
+++ b/imgui-binding/src/generated/java/imgui/flag/ImTextureFormat.java
@@ -0,0 +1,21 @@
+package imgui.flag;
+
+
+/**
+ * We intentionally support a limited amount of texture formats to limit burden on CPU-side code and extension.
+ * Most standard backends only support RGBA32 but we provide a single channel option for low-resource/embedded systems.
+ */
+public final class ImTextureFormat {
+ private ImTextureFormat() {
+ }
+
+ /**
+ * 4 components per pixel, each is unsigned 8-bit. Total size = TexWidth * TexHeight * 4
+ */
+ public static final int RGBA32 = 0;
+
+ /**
+ * 1 component per pixel, each is unsigned 8-bit. Total size = TexWidth * TexHeight
+ */
+ public static final int Alpha8 = 1;
+}
diff --git a/imgui-binding/src/generated/java/imgui/flag/ImTextureStatus.java b/imgui-binding/src/generated/java/imgui/flag/ImTextureStatus.java
new file mode 100644
index 00000000..c8a7c902
--- /dev/null
+++ b/imgui-binding/src/generated/java/imgui/flag/ImTextureStatus.java
@@ -0,0 +1,32 @@
+package imgui.flag;
+
+
+/**
+ * Status of a texture to communicate with Renderer Backend.
+ */
+public final class ImTextureStatus {
+ private ImTextureStatus() {
+ }
+
+ public static final int OK = 0;
+
+ /**
+ * Backend destroyed the texture.
+ */
+ public static final int Destroyed = 1;
+
+ /**
+ * Requesting backend to create the texture. Set status OK when done.
+ */
+ public static final int WantCreate = 2;
+
+ /**
+ * Requesting backend to update specific blocks of pixels (write to texture portions which have never been used before). Set status OK when done.
+ */
+ public static final int WantUpdates = 3;
+
+ /**
+ * Requesting backend to destroy the texture. Set status to Destroyed when done.
+ */
+ public static final int WantDestroy = 4;
+}
diff --git a/imgui-binding/src/main/java/imgui/ImDrawData.java b/imgui-binding/src/main/java/imgui/ImDrawData.java
index d52b332f..4affff89 100644
--- a/imgui-binding/src/main/java/imgui/ImDrawData.java
+++ b/imgui-binding/src/main/java/imgui/ImDrawData.java
@@ -21,6 +21,8 @@ public final class ImDrawData extends ImGuiStruct {
private static final int RESIZE_FACTOR = 5_000;
private static ByteBuffer dataBuffer = ByteBuffer.allocateDirect(25_000).order(ByteOrder.nativeOrder());
+ private static final ImTextureData _GETTEXTURES_1 = new ImTextureData(0);
+
public ImDrawData(final long ptr) {
super(ptr);
}
@@ -138,6 +140,30 @@ public ByteBuffer getCmdListVtxBufferData(final int cmdListIdx) {
memcpy(vtxBuffer, THIS->CmdLists[cmdListIdx]->VtxBuffer.Data, vtxBufferCapacity);
*/
+ /**
+ * Number of textures to update before rendering this draw data. Most of the time the list is shared by all
+ * ImDrawData, has only one texture and does not need any update. This almost always mirrors
+ * {@link ImGuiPlatformIO#getTexturesSize()}. May be zero if texture updates are disabled (list set to NULL).
+ */
+ public native int getTexturesSize(); /*
+ return THIS->Textures != NULL ? THIS->Textures->Size : 0;
+ */
+
+ /**
+ * Texture to update at the given index. The returned value is a shared instance, valid only until the next call to
+ * this method.
+ *
+ * @param idx index in {@code [0, getTexturesSize())}
+ */
+ public ImTextureData getTextures(final int idx) {
+ _GETTEXTURES_1.ptr = nGetTextures(idx);
+ return _GETTEXTURES_1;
+ }
+
+ private native long nGetTextures(int idx); /*
+ return (uintptr_t)(*THIS->Textures)[idx];
+ */
+
public static native int sizeOfImDrawVert(); /*
return (int)sizeof(ImDrawVert);
*/
diff --git a/imgui-binding/src/main/java/imgui/ImGuiPlatformIO.java b/imgui-binding/src/main/java/imgui/ImGuiPlatformIO.java
index 4dd498d5..31c254f0 100644
--- a/imgui-binding/src/main/java/imgui/ImGuiPlatformIO.java
+++ b/imgui-binding/src/main/java/imgui/ImGuiPlatformIO.java
@@ -73,6 +73,7 @@ public final class ImGuiPlatformIO extends ImGuiStruct {
private static final ImGuiViewport TMP_VIEWPORT = new ImGuiViewport(0);
private static final ImGuiPlatformMonitor TMP_MONITOR = new ImGuiPlatformMonitor(0);
private static final ImVec2 TMP_IM_VEC2 = new ImVec2();
+ private static final ImTextureData TMP_TEXTURE_DATA = new ImTextureData(0);
public ImGuiPlatformIO(final long ptr) {
super(ptr);
@@ -545,4 +546,63 @@ public ImGuiViewport getViewports(final int idx) {
private native long nGetViewports(int idx); /*
return (uintptr_t)IMGUI_PLATFORM_IO->Viewports[idx];
*/
+
+ //------------------------------------------------------------------
+ // Textures list (updated by dear imgui, honored by the renderer backend)
+ //------------------------------------------------------------------
+
+ /**
+ * Maximum texture width the renderer backend supports. Set by the renderer backend during initialization so the
+ * core library can split the font atlas accordingly. Zero means unbounded.
+ */
+ public native int getRendererTextureMaxWidth(); /*
+ return IMGUI_PLATFORM_IO->Renderer_TextureMaxWidth;
+ */
+
+ /**
+ * Maximum texture width the renderer backend supports. Set by the renderer backend during initialization so the
+ * core library can split the font atlas accordingly. Zero means unbounded.
+ */
+ public native void setRendererTextureMaxWidth(int value); /*
+ IMGUI_PLATFORM_IO->Renderer_TextureMaxWidth = value;
+ */
+
+ /**
+ * Maximum texture height the renderer backend supports. Set by the renderer backend during initialization so the
+ * core library can split the font atlas accordingly. Zero means unbounded.
+ */
+ public native int getRendererTextureMaxHeight(); /*
+ return IMGUI_PLATFORM_IO->Renderer_TextureMaxHeight;
+ */
+
+ /**
+ * Maximum texture height the renderer backend supports. Set by the renderer backend during initialization so the
+ * core library can split the font atlas accordingly. Zero means unbounded.
+ */
+ public native void setRendererTextureMaxHeight(int value); /*
+ IMGUI_PLATFORM_IO->Renderer_TextureMaxHeight = value;
+ */
+
+ /**
+ * Number of textures used by Dear ImGui (most often one). The renderer backend iterates this list to
+ * create/update/destroy textures.
+ */
+ public native int getTexturesSize(); /*
+ return IMGUI_PLATFORM_IO->Textures.Size;
+ */
+
+ /**
+ * Texture at the given index. The returned value is a shared instance, valid only until the next call to this
+ * method.
+ *
+ * @param idx index in {@code [0, getTexturesSize())}
+ */
+ public ImTextureData getTextures(final int idx) {
+ TMP_TEXTURE_DATA.ptr = nGetTextures(idx);
+ return TMP_TEXTURE_DATA;
+ }
+
+ private native long nGetTextures(int idx); /*
+ return (uintptr_t)IMGUI_PLATFORM_IO->Textures[idx];
+ */
}
diff --git a/imgui-binding/src/main/java/imgui/ImTextureData.java b/imgui-binding/src/main/java/imgui/ImTextureData.java
new file mode 100644
index 00000000..9350055e
--- /dev/null
+++ b/imgui-binding/src/main/java/imgui/ImTextureData.java
@@ -0,0 +1,233 @@
+package imgui;
+
+import imgui.binding.ImGuiStruct;
+import imgui.binding.annotation.ArgValue;
+import imgui.binding.annotation.BindingField;
+import imgui.binding.annotation.BindingMethod;
+import imgui.binding.annotation.BindingSource;
+import imgui.flag.ImTextureFormat;
+import imgui.flag.ImTextureStatus;
+
+import java.nio.ByteBuffer;
+
+import static imgui.binding.annotation.BindingField.Accessor.*;
+
+/**
+ * Specs and pixel storage for a texture used by Dear ImGui.
+ *
+ * This is only useful for (1) core library and (2) renderer backends. End-user/applications do not need to care about
+ * this. Renderer Backends will create a GPU-side version of this and communicate back through {@link #setTexID(long)}
+ * and {@link #setStatus(int)}.
+ *
+ * A texture stores two identifiers: {@code TexID} (the lower-level backend identifier stored in draw commands) and
+ * {@code BackendUserData} (higher-level opaque storage for the backend's own book-keeping).
+ */
+@BindingSource
+public final class ImTextureData extends ImGuiStruct {
+ private static final ImTextureRect TMP_USED_RECT = new ImTextureRect(0);
+ private static final ImTextureRect TMP_UPDATE_RECT = new ImTextureRect(0);
+ private static final ImTextureRect TMP_UPDATE = new ImTextureRect(0);
+
+ public ImTextureData(final long ptr) {
+ super(ptr);
+ }
+
+ /*JNI
+ #include "_common.h"
+ #define THIS ((ImTextureData*)STRUCT_PTR)
+ */
+
+
+ /**
+ * [DEBUG] Sequential index to facilitate identifying a texture when debugging/printing. Unique per atlas.
+ */
+ @BindingField(accessors = GETTER, callName = "UniqueID")
+ public int UniqueId;
+
+
+
+ /**
+ * Texture status, one of the {@link ImTextureStatus} values. Always use {@link #setStatus(int)} to modify!
+ */
+ @BindingField(accessors = GETTER)
+ public int Status;
+
+ /**
+ * Set the texture status. Called by the renderer backend after honoring a texture request.
+ *
+ * @param status one of the {@link ImTextureStatus} values
+ */
+ @BindingMethod(callName = "SetStatus")
+ public native void setStatus(@ArgValue(staticCast = "ImTextureStatus") int status);
+
+ /**
+ * Convenience storage for the backend. Some backends may have enough with {@code TexID}.
+ */
+ public native long getBackendUserData(); /*
+ return (uintptr_t)THIS->BackendUserData;
+ */
+
+ /**
+ * Convenience storage for the backend. Some backends may have enough with {@code TexID}.
+ */
+ public native void setBackendUserData(long backendUserData); /*
+ THIS->BackendUserData = (void*)backendUserData;
+ */
+
+ /**
+ * Backend-specific texture identifier, stored in {@code ImDrawCmd::GetTexID()} and passed to the backend's render
+ * function.
+ */
+ public native long getTexID(); /*
+ return (uintptr_t)THIS->TexID;
+ */
+
+ /**
+ * Set the backend-specific texture identifier. Called by the renderer backend after uploading the texture.
+ */
+ @BindingMethod(callName = "SetTexID")
+ public native void setTexID(long texID);
+
+ /**
+ * Texture format, one of the {@link ImTextureFormat} values.
+ */
+ @BindingField(accessors = GETTER)
+ public int Format;
+
+
+ /**
+ * Texture width, in pixels.
+ */
+ @BindingField(accessors = GETTER)
+ public int Width;
+
+ /**
+ * Texture height, in pixels.
+ */
+ @BindingField(accessors = GETTER)
+ public int Height;
+
+
+ /**
+ * Bytes per pixel: 4 for {@code ImTextureFormat_RGBA32} or 1 for {@code ImTextureFormat_Alpha8}.
+ */
+ @BindingField(accessors = GETTER)
+ public int BytesPerPixel;
+
+ /**
+ * Direct view over the CPU-side pixel buffer holding {@code Width*Height} pixels
+ * ({@code Width*Height*BytesPerPixel} bytes). The returned buffer wraps the native memory directly (no copy), so it
+ * is only valid while the texture keeps its pixels (i.e. before {@code DestroyPixels}).
+ *
+ * @return a direct {@link ByteBuffer} over the pixel data, or {@code null} if the pixels have been freed
+ */
+ public native ByteBuffer getPixels(); /*
+ if (THIS->Pixels == NULL) {
+ return NULL;
+ }
+ return env->NewDirectByteBuffer(THIS->Pixels, THIS->GetSizeInBytes());
+ */
+
+ /**
+ * Total size of the pixel buffer in bytes ({@code Width*Height*BytesPerPixel}).
+ */
+ @BindingMethod(callName = "GetSizeInBytes")
+ public native int getSizeInBytes();
+
+ /**
+ * Row pitch in bytes ({@code Width*BytesPerPixel}).
+ */
+ @BindingMethod(callName = "GetPitch")
+ public native int getPitch();
+
+ /**
+ * Bounding box encompassing all past and queued updates.
+ */
+ public ImTextureRect getUsedRect() {
+ TMP_USED_RECT.ptr = nGetUsedRect();
+ return TMP_USED_RECT;
+ }
+
+ private native long nGetUsedRect(); /*
+ return (uintptr_t)&THIS->UsedRect;
+ */
+
+ /**
+ * Bounding box encompassing all queued updates.
+ */
+ public ImTextureRect getUpdateRect() {
+ TMP_UPDATE_RECT.ptr = nGetUpdateRect();
+ return TMP_UPDATE_RECT;
+ }
+
+ private native long nGetUpdateRect(); /*
+ return (uintptr_t)&THIS->UpdateRect;
+ */
+
+ /**
+ * Number of individual update rectangles queued for this texture.
+ */
+ public native int getUpdatesSize(); /*
+ return THIS->Updates.Size;
+ */
+
+ /**
+ * Individual update rectangle at the given index. The returned value is a shared instance, valid only until the
+ * next call to this method.
+ *
+ * @param idx index in {@code [0, getUpdatesSize())}
+ */
+ public ImTextureRect getUpdates(final int idx) {
+ TMP_UPDATE.ptr = nGetUpdates(idx);
+ return TMP_UPDATE;
+ }
+
+ private native long nGetUpdates(int idx); /*
+ return (uintptr_t)&THIS->Updates[idx];
+ */
+
+ /**
+ * Count of successive frames where the texture was not used. Always {@code > 0} when the status is
+ * {@code ImTextureStatus_WantDestroy}.
+ */
+ @BindingField(accessors = GETTER)
+ public int UnusedFrames;
+
+ /**
+ * Number of contexts using this texture. Used during backend shutdown.
+ */
+ @BindingField(accessors = GETTER)
+ public int RefCount;
+
+ /**
+ * Whether the texture data is known to use colors (rather than just white + alpha).
+ */
+ @BindingField(accessors = GETTER)
+ public boolean UseColors;
+
+ /**
+ * [Internal] Whether the texture is queued to be destroyed next frame. May still be used in the current frame.
+ */
+ @BindingField(accessors = GETTER)
+ public boolean WantDestroyNextFrame;
+
+ /**
+ * Allocate the CPU-side pixel buffer for the given format and size. Generally called by the core library.
+ *
+ * @param format one of the {@link ImTextureFormat} values
+ * @param width texture width in pixels
+ * @param height texture height in pixels
+ */
+ @BindingMethod(callName = "Create")
+ public native void create(@ArgValue(staticCast = "ImTextureFormat") int format, int width, int height);
+
+ /**
+ * Free the CPU-side pixel buffer.
+ */
+ @BindingMethod(callName = "DestroyPixels")
+ public native void destroyPixels();
+
+ /*JNI
+ #undef THIS
+ */
+}
diff --git a/imgui-binding/src/main/java/imgui/ImTextureRect.java b/imgui-binding/src/main/java/imgui/ImTextureRect.java
new file mode 100644
index 00000000..cfb479ac
--- /dev/null
+++ b/imgui-binding/src/main/java/imgui/ImTextureRect.java
@@ -0,0 +1,54 @@
+package imgui;
+
+import imgui.binding.ImGuiStruct;
+import imgui.binding.annotation.BindingField;
+import imgui.binding.annotation.BindingSource;
+
+import static imgui.binding.annotation.BindingField.Accessor.*;
+
+/**
+ * Coordinates of a rectangle within a texture.
+ *
+ * When a texture is in {@code ImTextureStatus_WantUpdates} state, we provide a list of individual rectangles to copy to
+ * the graphics system. You may use {@link ImTextureData#getUpdates(int)} for the list, or
+ * {@link ImTextureData#getUpdateRect()} for a single bounding box.
+ */
+@BindingSource
+public final class ImTextureRect extends ImGuiStruct {
+ public ImTextureRect(final long ptr) {
+ super(ptr);
+ }
+
+ /*JNI
+ #include "_common.h"
+ #define THIS ((ImTextureRect*)STRUCT_PTR)
+ */
+
+ /**
+ * Upper-left x coordinate of rectangle
+ */
+ @BindingField(accessors = GETTER, callName = "x")
+ public int X;
+
+ /**
+ * Upper-left y coordinate of rectangle
+ */
+ @BindingField(accessors = GETTER, callName = "y")
+ public int Y;
+
+ /**
+ * Width of rectangle (in pixels).
+ */
+ @BindingField(accessors = GETTER, callName = "w")
+ public int W;
+
+ /**
+ * Height of rectangle (in pixels).
+ */
+ @BindingField(accessors = GETTER, callName = "h")
+ public int H;
+
+ /*JNI
+ #undef THIS
+ */
+}
diff --git a/imgui-binding/src/main/java/imgui/flag/ImTextureFormat.java b/imgui-binding/src/main/java/imgui/flag/ImTextureFormat.java
new file mode 100644
index 00000000..f7b5dcc4
--- /dev/null
+++ b/imgui-binding/src/main/java/imgui/flag/ImTextureFormat.java
@@ -0,0 +1,17 @@
+package imgui.flag;
+
+import imgui.binding.annotation.BindingAstEnum;
+import imgui.binding.annotation.BindingSource;
+
+/**
+ * We intentionally support a limited amount of texture formats to limit burden on CPU-side code and extension.
+ * Most standard backends only support RGBA32 but we provide a single channel option for low-resource/embedded systems.
+ */
+@BindingSource
+public final class ImTextureFormat {
+ private ImTextureFormat() {
+ }
+
+ @BindingAstEnum(file = "ast-imgui.json", qualType = "ImTextureFormat", sanitizeName = "ImTextureFormat_")
+ public Void __;
+}
diff --git a/imgui-binding/src/main/java/imgui/flag/ImTextureStatus.java b/imgui-binding/src/main/java/imgui/flag/ImTextureStatus.java
new file mode 100644
index 00000000..80aed169
--- /dev/null
+++ b/imgui-binding/src/main/java/imgui/flag/ImTextureStatus.java
@@ -0,0 +1,16 @@
+package imgui.flag;
+
+import imgui.binding.annotation.BindingAstEnum;
+import imgui.binding.annotation.BindingSource;
+
+/**
+ * Status of a texture to communicate with Renderer Backend.
+ */
+@BindingSource
+public final class ImTextureStatus {
+ private ImTextureStatus() {
+ }
+
+ @BindingAstEnum(file = "ast-imgui.json", qualType = "ImTextureStatus", sanitizeName = "ImTextureStatus_")
+ public Void __;
+}
diff --git a/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java b/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java
index 7a135a84..0b491252 100644
--- a/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java
+++ b/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java
@@ -4,12 +4,16 @@
import imgui.ImFontAtlas;
import imgui.ImGui;
import imgui.ImGuiIO;
+import imgui.ImGuiPlatformIO;
import imgui.ImGuiViewport;
+import imgui.ImTextureData;
+import imgui.ImTextureRect;
import imgui.ImVec4;
import imgui.callback.ImPlatformFuncViewport;
import imgui.flag.ImGuiBackendFlags;
import imgui.flag.ImGuiConfigFlags;
import imgui.flag.ImGuiViewportFlags;
+import imgui.flag.ImTextureStatus;
import imgui.type.ImInt;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GLCapabilities;
@@ -130,6 +134,7 @@
import static org.lwjgl.opengl.GL32.glShaderSource;
import static org.lwjgl.opengl.GL32.glTexImage2D;
import static org.lwjgl.opengl.GL32.glTexParameteri;
+import static org.lwjgl.opengl.GL32.glTexSubImage2D;
import static org.lwjgl.opengl.GL32.glUniform1i;
import static org.lwjgl.opengl.GL32.glUniformMatrix4fv;
import static org.lwjgl.opengl.GL32.glUseProgram;
@@ -316,15 +321,17 @@ public boolean init(final String glslVersion) {
io.addBackendFlags(ImGuiBackendFlags.RendererHasVtxOffset);
}
- // In C++: io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures — the renderer drives per-frame ImTextureData uploads.
- // In Java: ImTextureData is not exposed in imgui-binding yet, so we keep the legacy createFontsTexture path
- // and intentionally do not advertise the flag (follow-up: expose ImTextureData in the binding).
+ // We can honor ImGuiPlatformIO::Textures[] requests during render: the renderer drives per-frame
+ // ImTextureData create/update/destroy (see updateTexture / renderDrawData).
+ io.addBackendFlags(ImGuiBackendFlags.RendererHasTextures);
// We can create multi-viewports on the Renderer side (optional)
io.addBackendFlags(ImGuiBackendFlags.RendererHasViewports);
- // In C++: platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = bd->MaxTextureSize.
- // In Java: setters are not exposed on ImGuiPlatformIO in imgui-binding (follow-up). data.maxTextureSize is queried above for parity.
+ // Advertise the maximum texture size so the core library can split large font atlases accordingly.
+ final ImGuiPlatformIO platformIO = ImGui.getPlatformIO();
+ platformIO.setRendererTextureMaxWidth(data.maxTextureSize);
+ platformIO.setRendererTextureMaxHeight(data.maxTextureSize);
if (glslVersion == null) {
if (IS_APPLE) {
@@ -365,9 +372,9 @@ public void shutdown() {
destroyDeviceObjects();
io.setBackendRendererName(null);
- // In C++: io.BackendFlags also clears RendererHasTextures, then platform_io.ClearRendererHandlers() runs.
- // In Java: RendererHasTextures is never set (see init), and ClearRendererHandlers is not exposed in imgui-binding (follow-up).
- io.removeBackendFlags(ImGuiBackendFlags.RendererHasVtxOffset | ImGuiBackendFlags.RendererHasViewports);
+ // In C++: platform_io.ClearRendererHandlers() also runs here.
+ // In Java: ClearRendererHandlers is not exposed in imgui-binding (follow-up).
+ io.removeBackendFlags(ImGuiBackendFlags.RendererHasVtxOffset | ImGuiBackendFlags.RendererHasTextures | ImGuiBackendFlags.RendererHasViewports);
data = null;
}
@@ -375,9 +382,6 @@ public void newFrame() {
if (data.shaderHandle == 0) {
createDeviceObjects();
}
- if (data.fontTexture == 0) {
- createFontsTexture();
- }
}
protected void setupRenderState(final ImDrawData drawData, final int fbWidth, final int fbHeight, final int gVertexArrayObject) {
@@ -466,14 +470,20 @@ public void renderDrawData(final ImDrawData drawData) {
return;
}
+ // Catch up with texture updates. Most of the time the list has one element with an OK status (nothing to do).
+ // This almost always mirrors ImGui::GetPlatformIO().Textures[], but is carried on ImDrawData to allow
+ // overriding or disabling texture updates (in which case getTexturesSize() returns 0).
+ for (int i = 0; i < drawData.getTexturesSize(); i++) {
+ final ImTextureData tex = drawData.getTextures(i);
+ if (tex.getStatus() != ImTextureStatus.OK) {
+ updateTexture(tex);
+ }
+ }
+
if (drawData.getCmdListsCount() <= 0) {
return;
}
- // In C++: iterates draw_data->Textures and calls ImGui_ImplOpenGL3_UpdateTexture for each non-OK status.
- // In Java: ImTextureData is not exposed in imgui-binding (follow-up); we keep the legacy createFontsTexture
- // path triggered from newFrame(), so dynamic atlas updates are not honored here yet.
-
glGetIntegerv(GL_ACTIVE_TEXTURE, props.lastActiveTexture);
glActiveTexture(GL_TEXTURE0);
glGetIntegerv(GL_CURRENT_PROGRAM, props.lastProgram);
@@ -622,8 +632,106 @@ public void renderDrawData(final ImDrawData drawData) {
glScissor(props.lastScissorBox[0], props.lastScissorBox[1], props.lastScissorBox[2], props.lastScissorBox[3]);
}
- // In C++: the legacy CreateFontsTexture has been removed in favor of UpdateTexture(WantCreate), driven by ImGuiPlatformIO::Textures.
- // In Java: ImTextureData is not exposed in imgui-binding yet, so we keep the legacy path here (follow-up: migrate once exposed).
+ /**
+ * Honor a single texture request coming from Dear ImGui. Called for every {@link ImTextureData} whose status is not
+ * {@code ImTextureStatus_OK} at the start of {@link #renderDrawData(ImDrawData)}. Creates, updates or destroys the
+ * backing OpenGL texture and reports the new state back to the core library.
+ *
+ * @param tex the texture data to reconcile with the GPU
+ */
+ public void updateTexture(final ImTextureData tex) {
+ final int status = tex.getStatus();
+
+ // Backup GL_UNPACK state that we modify, restore on exit (mirrors the C++ backend).
+ final int[] lastUnpackRowLength = new int[1];
+ final int[] lastUnpackAlignment = new int[1];
+ final boolean unpackStateSaveAndRestore = status == ImTextureStatus.WantCreate || status == ImTextureStatus.WantUpdates;
+ if (unpackStateSaveAndRestore) {
+ glGetIntegerv(GL_UNPACK_ROW_LENGTH, lastUnpackRowLength); // Not on WebGL/ES
+ glGetIntegerv(GL_UNPACK_ALIGNMENT, lastUnpackAlignment);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ }
+
+ if (status == ImTextureStatus.WantCreate) {
+ // Create and upload a new texture to the graphics system.
+ // (Bilinear sampling is required by default. Set 'io.Fonts.Flags |= ImFontAtlasFlags_NoBakedLines' or
+ // 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling.)
+ final int[] lastTexture = new int[1];
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, lastTexture);
+ final int glTextureId = glGenTextures();
+ glBindTexture(GL_TEXTURE_2D, glTextureId);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); // Not on WebGL/ES
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.getWidth(), tex.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.getPixels());
+
+ // Store identifiers back into the shared ImTextureData so draw commands resolve to this texture.
+ tex.setTexID(glTextureId);
+ tex.setStatus(ImTextureStatus.OK);
+
+ glBindTexture(GL_TEXTURE_2D, lastTexture[0]);
+ } else if (status == ImTextureStatus.WantUpdates) {
+ // Update selected blocks. We only ever write to texture regions which have never been used before.
+ // This backend uses tex.getUpdates(), but tex.getUpdateRect() could be used to upload a single region.
+ final int[] lastTexture = new int[1];
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, lastTexture);
+ glBindTexture(GL_TEXTURE_2D, (int) tex.getTexID());
+
+ final int width = tex.getWidth();
+ final int bytesPerPixel = tex.getBytesPerPixel();
+ final ByteBuffer pixels = tex.getPixels();
+
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, width); // Not on WebGL/ES
+ for (int i = 0; i < tex.getUpdatesSize(); i++) {
+ final ImTextureRect r = tex.getUpdates(i);
+ final int rx = r.getX();
+ final int ry = r.getY();
+ final int rw = r.getW();
+ final int rh = r.getH();
+ // Mirror tex->GetPixelsAt(r.x, r.y): point the buffer at the region's upper-left pixel;
+ // UNPACK_ROW_LENGTH handles the source stride.
+ pixels.position((rx + ry * width) * bytesPerPixel);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, rx, ry, rw, rh, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ }
+
+ tex.setStatus(ImTextureStatus.OK);
+ glBindTexture(GL_TEXTURE_2D, lastTexture[0]);
+ } else if (status == ImTextureStatus.WantDestroy && tex.getUnusedFrames() > 0) {
+ destroyTexture(tex);
+ }
+
+ // Restore GL_UNPACK state.
+ if (unpackStateSaveAndRestore) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, lastUnpackRowLength[0]);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, lastUnpackAlignment[0]);
+ }
+ }
+
+ /**
+ * Destroy the OpenGL texture backing the given {@link ImTextureData} and mark it as destroyed so the core library
+ * may release it (or re-create it on demand).
+ *
+ * @param tex the texture data whose GPU resource should be freed
+ */
+ public void destroyTexture(final ImTextureData tex) {
+ glDeleteTextures((int) tex.getTexID());
+
+ // Clear identifiers and mark as destroyed (allows e.g. destroying device objects while running).
+ tex.setTexID(0); // ImTextureID_Invalid
+ tex.setStatus(ImTextureStatus.Destroyed);
+ }
+
+ /**
+ * Legacy single-texture font-atlas upload.
+ *
+ * @deprecated Since Dear ImGui 1.92 the font atlas is uploaded through {@link #updateTexture(ImTextureData)} driven
+ * by {@code ImGuiBackendFlags_RendererHasTextures}. This method is retained only for source compatibility and is
+ * no longer called by the backend.
+ * @return true when the texture was created
+ */
+ @Deprecated
public boolean createFontsTexture() {
final ImFontAtlas fontAtlas = ImGui.getIO().getFonts();
@@ -656,6 +764,13 @@ public boolean createFontsTexture() {
return true;
}
+ /**
+ * Legacy font-atlas texture teardown.
+ *
+ * @deprecated Since Dear ImGui 1.92 textures are destroyed through {@link #destroyTexture(ImTextureData)}. This
+ * method is retained only for source compatibility and is no longer called by the backend.
+ */
+ @Deprecated
public void destroyFontsTexture() {
final ImGuiIO io = ImGui.getIO();
if (data.fontTexture != 0) {
@@ -773,10 +888,8 @@ protected boolean createDeviceObjects() {
data.vboHandle = glGenBuffers();
data.elementsHandle = glGenBuffers();
- // In C++: the font texture is now created lazily through ImGui_ImplOpenGL3_UpdateTexture(WantCreate),
- // driven by the platform_io.Textures iteration in RenderDrawData.
- // In Java: ImTextureData is not exposed in imgui-binding (follow-up); keep the legacy createFontsTexture call here.
- createFontsTexture();
+ // The font texture is created lazily through updateTexture(WantCreate), driven by the per-frame texture
+ // iteration in renderDrawData(); nothing to upload here.
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, lastTexture[0]);
@@ -802,9 +915,15 @@ public void destroyDeviceObjects() {
glDeleteProgram(data.shaderHandle);
data.shaderHandle = 0;
}
- // In C++: iterates ImGui::GetPlatformIO().Textures and calls ImGui_ImplOpenGL3_DestroyTexture for each entry with RefCount == 1.
- // In Java: ImTextureData is not exposed in imgui-binding (follow-up); we delete the legacy fontTexture directly.
- destroyFontsTexture();
+ // Destroy all textures we still own. RefCount == 1 means this context is the last user, so it is safe to free
+ // the GPU resource (mirrors ImGui_ImplOpenGL3_DestroyDeviceObjects).
+ final ImGuiPlatformIO platformIO = ImGui.getPlatformIO();
+ for (int i = 0; i < platformIO.getTexturesSize(); i++) {
+ final ImTextureData tex = platformIO.getTextures(i);
+ if (tex.getRefCount() == 1) {
+ destroyTexture(tex);
+ }
+ }
}
//--------------------------------------------------------------------------------------------------------