OpenGL backend (no more software rendering!)

This commit is contained in:
2026-08-28 06:44:02 -07:00
parent 80e222fa04
commit e300335e8a
8 changed files with 426 additions and 41 deletions
+18 -2
View File
@@ -16,6 +16,8 @@ set(IMXUI_CORE_SOURCES
src/XzpPackage.cpp
src/XuiXmlWriter.cpp
src/XurWriter.cpp
src/Audio.cpp
src/ImageManager.cpp
)
@@ -29,6 +31,7 @@ set(IMXUI_BACKEND_SOURCES
src/Skin.cpp
)
set(IMXUI_HEADERS
include/ImXui/ImXui.h
include/ImXui/RenderCommands.h
@@ -38,12 +41,14 @@ set(IMXUI_HEADERS
include/ImXui/Skin.h
include/ImXui/XurWriter.h
include/ImXui/XuiXmlWriter.h
include/ImXui/Audio.h
)
add_library(ImXuiCore STATIC ${IMXUI_CORE_SOURCES} ${IMXUI_HEADERS})
target_include_directories(ImXuiCore PUBLIC include compat/include)
target_include_directories(ImXuiCore PUBLIC include compat/include third_party)
target_include_directories(ImXuiCore PRIVATE src)
target_include_directories(ImXuiCore PUBLIC include)
set(IMXUI_HEADERS ${IMXUI_HEADERS} compat/include/xui.h compat/include/xuiapp.h
compat/include/xuirender.h compat/include/xuiresource.h)
@@ -51,13 +56,16 @@ set(IMXUI_HEADERS ${IMXUI_HEADERS} compat/include/xui.h compat/include/xuiapp.h
add_library(ImXuiSoft STATIC
src/FontEngine.cpp
src/ImageManager.cpp
src/SoftwareRenderer.cpp
)
target_include_directories(ImXuiSoft PUBLIC include)
target_include_directories(ImXuiSoft PRIVATE src third_party)
target_link_libraries(ImXuiSoft PUBLIC ImXuiCore)
if(NOT WIN32)
find_package(OpenGL)
endif()
if(WIN32)
add_library(ImXui STATIC ${IMXUI_BACKEND_SOURCES} ${IMXUI_HEADERS})
target_include_directories(ImXui PUBLIC include)
@@ -78,6 +86,13 @@ if(WIN32)
endif()
if(UNIX AND NOT APPLE)
if(OpenGL_FOUND)
target_sources(ImXuiSoft PRIVATE src/OpenGLRenderer.cpp)
target_compile_definitions(ImXuiCore PRIVATE IMXUI_HAS_OPENGL IMXUI_HAS_GLX)
target_compile_definitions(ImXuiSoft PRIVATE IMXUI_HAS_OPENGL IMXUI_HAS_GLX)
target_compile_definitions(ImXuiSoft PRIVATE IMXUI_HAS_OPENGL IMXUI_HAS_GLX)
target_link_libraries(ImXuiSoft PUBLIC OpenGL::GL)
endif()
add_executable(NotepadDemo examples/NotepadDemo.cpp)
target_link_libraries(NotepadDemo PRIVATE ImXuiSoft ImXuiCore X11)
target_include_directories(NotepadDemo PRIVATE include)
@@ -127,4 +142,5 @@ target_include_directories(XUITool PRIVATE include third_party/zlib)
if(WIN32)
install(TARGETS ImXui LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
endif()
install(DIRECTORY include/ DESTINATION include)
+4 -38
View File
@@ -55,7 +55,7 @@
#ifdef _WIN32
static ImXui::D3D11Renderer s_renderer;
#else
static ImXui::SoftwareRenderer s_renderer;
static ImXui::OpenGLRenderer s_renderer;
#endif
static ImXui::Canvas s_canvas;
static bool s_running = true;
@@ -2773,30 +2773,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) {
CoUninitialize();
return 0;
}
#else
// 24-bit visuals expect BGRA byte order on x86, the renderer gives RGBA
static void swizzleToBlit() {
const uint8_t* fb = s_renderer.getFramebuffer();
size_t n = s_blit.size();
for (size_t i = 0; i < n; i += 4) {
s_blit[i] = fb[i + 2];
s_blit[i + 1] = fb[i + 1];
s_blit[i + 2] = fb[i];
s_blit[i + 3] = 255;
}
}
static void rebuildImage() {
if (s_img) XDestroyImage(s_img);
int w = s_renderer.getWidth();
int h = s_renderer.getHeight();
s_blit.resize((size_t)w * h * 4);
s_img = XCreateImage(s_dpy, s_visual, s_depth, ZPixmap, 0,
(char*)s_blit.data(), w, h, 32, w * 4);
}
static int x11Vk(KeySym ks, bool ctrl) {
switch (ks) {
case XK_Left: return VK_LEFT;
@@ -2838,7 +2815,6 @@ static void handleXEvent(XEvent& ev, Atom wmDelete) {
(ev.xconfigure.width != s_renderer.getWidth() ||
ev.xconfigure.height != s_renderer.getHeight())) {
s_renderer.resize(ev.xconfigure.width, ev.xconfigure.height);
rebuildImage();
layoutUI();
s_needRender = true;
}
@@ -3000,8 +2976,6 @@ int main() {
ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
StructureNotifyMask);
XMapWindow(s_dpy, s_win);
s_gc = XCreateGC(s_dpy, s_win, 0, nullptr);
s_curArrow = XCreateFontCursor(s_dpy, XC_left_ptr);
s_curIBeam = XCreateFontCursor(s_dpy, XC_xterm);
s_curHand = XCreateFontCursor(s_dpy, XC_hand2);
@@ -3018,7 +2992,7 @@ int main() {
XSetInputFocus(s_dpy, s_win, RevertToParent, CurrentTime);
XFlush(s_dpy);
if (!s_renderer.init(nullptr, 1280, 800)) {
if (!s_renderer.init(s_dpy, 1280, 800)) {
fprintf(stderr, "Renderer init failed\n");
return 1;
}
@@ -3038,9 +3012,7 @@ int main() {
setFontIdx(1);
layoutUI();
scrollToCaret();
s_rendererReady = true;
rebuildImage();
scrollToCaret(); s_rendererReady = true;
unsigned long lastBlink = nowMs();
while (s_running) {
@@ -3071,17 +3043,11 @@ int main() {
drawFileDialog();
s_renderer.endFrame(false);
s_blinkOnly = false;
swizzleToBlit();
XPutImage(s_dpy, s_win, s_gc, s_img, 0, 0, 0, 0,
s_renderer.getWidth(), s_renderer.getHeight());
XFlush(s_dpy);
}
usleep(5000);
if (!s_needRender && XPending(s_dpy) == 0) usleep(1000);
}
s_renderer.shutdown();
XDestroyImage(s_img);
XFreeGC(s_dpy, s_gc);
XDestroyWindow(s_dpy, s_win);
XCloseDisplay(s_dpy);
return 0;
+52
View File
@@ -0,0 +1,52 @@
#pragma once
#include "ImXui.h"
namespace ImXui {
class AudioBackend : public ISoundSink {
public:
virtual ~AudioBackend() {}
virtual bool init() = 0;
virtual void shutdown() = 0;
};
class NullAudioBackend : public AudioBackend {
public:
bool init() override { return true; }
void shutdown() override {}
void playCue(const std::wstring&, const std::wstring&, const std::wstring&, float, bool) override {}
void stopCue(const std::wstring&) override {}
};
#if defined(_WIN32)
class WinMMAudioBackend : public AudioBackend {
public:
bool init() override;
void shutdown() override;
void playCue(const std::wstring&, const std::wstring&, const std::wstring&, float, bool) override;
void stopCue(const std::wstring&) override;
};
#endif
#if defined(__APPLE__)
class CoreAudioBackend : public AudioBackend {
public:
bool init() override;
void shutdown() override;
void playCue(const std::wstring&, const std::wstring&, const std::wstring&, float, bool) override;
void stopCue(const std::wstring&) override;
};
#endif
#if defined(__linux__)
class ALSAAudioBackend : public AudioBackend {
public:
bool init() override;
void shutdown() override;
void playCue(const std::wstring&, const std::wstring&, const std::wstring&, float, bool) override;
void stopCue(const std::wstring&) override;
};
#endif
}
+56
View File
@@ -3,6 +3,7 @@
#include "ImXui.h"
#include "TextureManager.h"
#include "Skin.h"
#include "RenderCommands.h"
#include <cstdint>
#include <string>
#include <map>
@@ -77,6 +78,61 @@ public:
};
class OpenGLRenderer : public Renderer, public IRenderCommandSink {
public:
bool loadSystemFont(const std::string& name);
float measureText(const std::wstring& text, float sizePx) const;
float lineHeight(float sizePx) const;
const uint8_t* getFramebuffer() const;
OpenGLRenderer();
~OpenGLRenderer();
bool init(void* nativeWindow, int width, int height) override;
void shutdown() override;
bool resize(int width, int height) override;
bool beginFrame(float clearR = 0, float clearG = 0, float clearB = 0, float clearA = 1) override;
void endFrame(bool vsync = true) override;
void renderScene(const Scene* scene) override;
void setTextureManager(TextureManager* mgr) override;
void setSkin(const Skin* skin) override;
const Skin* getSkin() const override;
bool loadFont(const wchar_t* path) override;
bool initTextRendering() override;
void shutdownTextRendering() override;
void drawTexturedQuad(float x, float y, float w, float h, const wchar_t* texturePath,
float r = 1, float g = 1, float b = 1, float a = 1) override;
void drawFigure(float x, float y, float w, float h,
const std::vector<Vec2>& points, bool closed,
float fillR, float fillG, float fillB, float fillA,
float strokeR, float strokeG, float strokeB, float strokeA,
float strokeWidth) override;
void drawColoredQuad(float x, float y, float w, float h,
float r, float g, float b, float a) override;
void drawTextString(float x, float y, float w, float h,
const std::wstring& text, const std::wstring& fontName, float fontSize,
float r = 1, float g = 1, float b = 1, float a = 1,
bool centered = false) override;
int getWidth() const override;
int getHeight() const override;
void beginScene(float width, float height) override;
void drawColoredQuad(const Rect& rect, const Color& color, float opacity) override;
void drawTexturedQuad(const Rect& rect, const std::wstring& resource, const Color& color, float opacity) override;
void drawText(const Rect& rect, const std::wstring& text, const std::wstring& fontName, float fontSize, const Color& color, float opacity, bool centered) override;
void endScene() override;
void pushClip(const Rect& rect) override;
void popClip() override;
void drawNineGrid(const Rect& rect, const std::wstring& resource, const Color& color, float opacity, uint32_t leftInset, uint32_t topInset, uint32_t rightInset, uint32_t bottomInset, bool noCenter) override;
void drawFigure(const Rect& rect, const std::vector<Vec2>& points, bool closed, const Color& fillColor, const Color& strokeColor, float opacity, float strokeWidth, uint32_t blendMode) override;
void setBlendMode(uint32_t blendMode) override;
void drawCustom(const std::wstring& drawId, const Rect& rect, float opacity) override;
struct Impl;
private:
Impl* m_impl;
};
class D3D11Renderer : public Renderer {
public:
D3D11Renderer();
+3 -1
View File
@@ -1,5 +1,6 @@
#pragma once
#include "ImageManager.h"
#include <string>
#include <unordered_map>
#include <cstdint>
@@ -20,13 +21,14 @@ public:
void setBasePath(const std::string& path) { m_basePath = path; }
const std::string& getBasePath() const { return m_basePath; }
const ImageData* loadImage(const std::string& relativePath);
ID3D11ShaderResourceView* load(const std::string& relativePath);
bool getSize(const std::string& relativePath, int& w, int& h) const;
private:
ID3D11Device* m_device;
std::string m_basePath;
ImageManager m_images;
std::unordered_map<std::string, ID3D11ShaderResourceView*> m_cache;
std::unordered_map<std::string, int> m_widths;
std::unordered_map<std::string, int> m_heights;
+61
View File
@@ -0,0 +1,61 @@
#include "ImXui/Audio.h"
#if defined(_WIN32)
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
#endif
#if defined(__APPLE__)
#include <AudioToolbox/AudioToolbox.h>
#endif
#if defined(__linux__)
#include <cstdlib>
#endif
namespace ImXui {
#if defined(_WIN32)
bool WinMMAudioBackend::init() { return true; }
void WinMMAudioBackend::shutdown() { stopCue(std::wstring()); }
void WinMMAudioBackend::playCue(const std::wstring& cue, const std::wstring&, const std::wstring&, float, bool loop) {
if (cue.empty()) return;
DWORD flags = SND_FILENAME | SND_ASYNC | SND_NODEFAULT;
if (loop) flags |= SND_LOOP;
PlaySoundW(cue.c_str(), nullptr, flags);
}
void WinMMAudioBackend::stopCue(const std::wstring&) { PlaySoundW(nullptr, nullptr, 0); }
#endif
#if defined(__APPLE__)
bool CoreAudioBackend::init() { return true; }
void CoreAudioBackend::shutdown() {}
void CoreAudioBackend::playCue(const std::wstring& cue, const std::wstring&, const std::wstring&, float, bool) {
if (cue.empty()) return;
CFURLRef url = CFURLCreateFromFileSystemRepresentation(nullptr,
reinterpret_cast<const UInt8*>(cue.c_str()), cue.size() * sizeof(wchar_t), false);
if (!url) return;
SystemSoundID sound = 0;
if (AudioServicesCreateSystemSoundID(url, &sound) == kAudioServicesNoError) {
AudioServicesPlaySystemSound(sound);
AudioServicesDisposeSystemSoundID(sound);
}
CFRelease(url);
}
void CoreAudioBackend::stopCue(const std::wstring&) {}
#endif
#if defined(__linux__)
bool ALSAAudioBackend::init() { return true; }
void ALSAAudioBackend::shutdown() {}
void ALSAAudioBackend::playCue(const std::wstring& cue, const std::wstring&, const std::wstring&, float, bool) {
if (cue.empty()) return;
std::string path(cue.begin(), cue.end());
std::string command = "aplay -q \"" + path + "\" >/dev/null 2>&1 &";
std::system(command.c_str());
}
void ALSAAudioBackend::stopCue(const std::wstring&) {}
#endif
}
+223
View File
@@ -0,0 +1,223 @@
#include "ImXui/Renderer.h"
#include "ImXui/RenderCommands.h"
#include "ImXui/ImageManager.h"
#include "ImXui/FontEngine.h"
#if defined(IMXUI_HAS_OPENGL)
#define GL_GLEXT_PROTOTYPES 1
#include <GL/gl.h>
#include <GL/glext.h>
#if defined(IMXUI_HAS_GLX)
#include <GL/glx.h>
#include <X11/Xlib.h>
#endif
#endif
#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include <cwchar>
namespace ImXui {
struct OpenGLRenderer::Impl {
int width, height;
TextureManager* textureManager;
const Skin* skin;
ImageManager images;
FontEngine font;
std::map<std::string, unsigned> textures;
std::map<std::wstring, unsigned> textTextures;
unsigned white;
bool initialized;
#if defined(IMXUI_HAS_OPENGL)
unsigned program, vao, vbo;
int projection;
std::vector<RendererVertex2D> vertices;
unsigned batchTexture;
float batchBlendSrc, batchBlendDst;
#endif
#if defined(IMXUI_HAS_GLX)
Display* display;
GLXDrawable drawable;
#endif
Impl() : width(0), height(0), textureManager(nullptr), skin(nullptr), white(0), initialized(false)
#if defined(IMXUI_HAS_OPENGL)
, program(0), vao(0), vbo(0), projection(-1), batchTexture(0), batchBlendSrc(0), batchBlendDst(0)
#endif
#if defined(IMXUI_HAS_GLX)
, display(nullptr), drawable(0)
#endif
{}
};
namespace {
static std::string narrow(const std::wstring& s) { return std::string(s.begin(), s.end()); }
#if defined(IMXUI_HAS_OPENGL)
static unsigned compileShader(unsigned type, const char* source) {
unsigned shader = glCreateShader(type);
glShaderSource(shader, 1, &source, nullptr);
glCompileShader(shader);
int ok = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
if (!ok) { glDeleteShader(shader); return 0; }
return shader;
}
static unsigned createProgram() {
static const char* vs = "attribute vec2 a_position; attribute vec2 a_uv; attribute vec4 a_color; varying vec2 v_uv; varying vec4 v_color; uniform vec2 u_size; void main(){ vec2 p=a_position/u_size*2.0-1.0; gl_Position=vec4(p.x,-p.y,0.0,1.0); v_uv=a_uv; v_color=a_color; }";
static const char* fs = "uniform sampler2D u_texture; varying vec2 v_uv; varying vec4 v_color; void main(){ gl_FragColor=texture2D(u_texture,v_uv)*v_color; }";
unsigned v = compileShader(GL_VERTEX_SHADER, vs), f = compileShader(GL_FRAGMENT_SHADER, fs);
if (!v || !f) { if (v) glDeleteShader(v); if (f) glDeleteShader(f); return 0; }
unsigned p = glCreateProgram(); glAttachShader(p, v); glAttachShader(p, f); glBindAttribLocation(p, 0, "a_position"); glBindAttribLocation(p, 1, "a_uv"); glBindAttribLocation(p, 2, "a_color"); glLinkProgram(p); glDeleteShader(v); glDeleteShader(f);
int ok = 0; glGetProgramiv(p, GL_LINK_STATUS, &ok); if (!ok) { glDeleteProgram(p); return 0; } return p;
}
static unsigned makeTexture(const ImageData& image) {
unsigned texture = 0; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); 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_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.w, image.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.rgba.data()); return texture;
}
#endif
}
OpenGLRenderer::OpenGLRenderer() : m_impl(new Impl()) {}
OpenGLRenderer::~OpenGLRenderer() { shutdown(); delete m_impl; }
bool OpenGLRenderer::init(void* window, int w, int h) {
shutdown(); if (w <= 0 || h <= 0) return false; m_impl->width = w; m_impl->height = h;
#if defined(IMXUI_HAS_OPENGL)
#if defined(IMXUI_HAS_GLX)
m_impl->display = static_cast<Display*>(window); m_impl->drawable = m_impl->display ? glXGetCurrentDrawable() : 0;
#else
(void)window;
#endif
m_impl->program = createProgram(); if (!m_impl->program) return false;
glGenVertexArrays(1, &m_impl->vao); glGenBuffers(1, &m_impl->vbo);
glBindVertexArray(m_impl->vao); glBindBuffer(GL_ARRAY_BUFFER, m_impl->vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(RendererVertex2D) * 4096, nullptr, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(RendererVertex2D), (void*)0);
glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(RendererVertex2D), (void*)(sizeof(float) * 2));
glEnableVertexAttribArray(2); glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(RendererVertex2D), (void*)(sizeof(float) * 4));
m_impl->projection = glGetUniformLocation(m_impl->program, "u_size");
glUseProgram(m_impl->program); glUniform1i(glGetUniformLocation(m_impl->program, "u_texture"), 0);
ImageData white; white.w = 1; white.h = 1; white.rgba.assign(4, 255); m_impl->white = makeTexture(white);
m_impl->vertices.reserve(4096); m_impl->initialized = m_impl->white != 0; return m_impl->initialized;
#else
(void)window; return false;
#endif
}
void OpenGLRenderer::shutdown() {
#if defined(IMXUI_HAS_OPENGL)
if (!m_impl->initialized && !m_impl->program) return;
glUseProgram(0); for (auto& x : m_impl->textures) if (x.second) glDeleteTextures(1, &x.second); for (auto& x : m_impl->textTextures) if (x.second) glDeleteTextures(1, &x.second); if (m_impl->white) glDeleteTextures(1, &m_impl->white); if (m_impl->vbo) glDeleteBuffers(1, &m_impl->vbo); if (m_impl->vao) glDeleteVertexArrays(1, &m_impl->vao); if (m_impl->program) glDeleteProgram(m_impl->program);
#endif
m_impl->textures.clear(); m_impl->textTextures.clear(); m_impl->white = 0; m_impl->initialized = false;
#if defined(IMXUI_HAS_OPENGL)
m_impl->program = m_impl->vao = m_impl->vbo = 0; m_impl->vertices.clear();
#endif
}
bool OpenGLRenderer::resize(int w, int h) { if (w <= 0 || h <= 0) return false; m_impl->width = w; m_impl->height = h;
#if defined(IMXUI_HAS_OPENGL)
if (!m_impl->initialized) return false; glViewport(0, 0, w, h);
#endif
return true;
}
bool OpenGLRenderer::beginFrame(float r, float g, float b, float a) {
#if defined(IMXUI_HAS_OPENGL)
if (!m_impl->initialized) return false; glViewport(0, 0, m_impl->width, m_impl->height); glClearColor(r, g, b, a); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(m_impl->program); glUniform2f(m_impl->projection, (float)m_impl->width, (float)m_impl->height); glBindVertexArray(m_impl->vao); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); m_impl->vertices.clear(); m_impl->batchTexture = 0; return true;
#else
return false;
#endif
}
void OpenGLRenderer::endFrame(bool vsync) {
#if defined(IMXUI_HAS_OPENGL)
if (!m_impl->vertices.empty()) { glBindBuffer(GL_ARRAY_BUFFER, m_impl->vbo); glBufferSubData(GL_ARRAY_BUFFER, 0, (GLsizeiptr)(m_impl->vertices.size() * sizeof(RendererVertex2D)), m_impl->vertices.data()); glBindTexture(GL_TEXTURE_2D, m_impl->batchTexture); glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_impl->vertices.size()); m_impl->vertices.clear(); }
#if defined(IMXUI_HAS_GLX)
if (vsync && m_impl->display && m_impl->drawable) glXSwapBuffers(m_impl->display, m_impl->drawable);
#else
(void)vsync;
#endif
#else
(void)vsync;
#endif
}
void OpenGLRenderer::renderScene(const Scene* s) { if (s) emitRenderCommands(s, static_cast<IRenderCommandSink*>(this)); }
void OpenGLRenderer::setTextureManager(TextureManager* m) { m_impl->textureManager = m; }
void OpenGLRenderer::setSkin(const Skin* s) { m_impl->skin = s; }
const Skin* OpenGLRenderer::getSkin() const { return m_impl->skin; }
bool OpenGLRenderer::loadFont(const wchar_t* p) { return p && m_impl->font.loadFontFile(narrow(p)); }
bool OpenGLRenderer::loadSystemFont(const std::string& n) { return m_impl->font.loadSystemFont(n); }
float OpenGLRenderer::measureText(const std::wstring& t, float s) const { return m_impl->font.measureText(t, s); }
float OpenGLRenderer::lineHeight(float s) const { return m_impl->font.lineHeight(s); }
const uint8_t* OpenGLRenderer::getFramebuffer() const { return nullptr; }
bool OpenGLRenderer::initTextRendering() { return m_impl->font.isLoaded() || m_impl->font.loadSystemFont(""); }
void OpenGLRenderer::shutdownTextRendering() {}
#if defined(IMXUI_HAS_OPENGL)
static void appendQuad(OpenGLRenderer::Impl* i, unsigned texture, float x, float y, float w, float h, float u0, float v0, float u1, float v1, float r, float g, float b, float a) {
if (i->batchTexture != texture && !i->vertices.empty()) { glBindBuffer(GL_ARRAY_BUFFER, i->vbo); glBufferSubData(GL_ARRAY_BUFFER, 0, (GLsizeiptr)(i->vertices.size() * sizeof(RendererVertex2D)), i->vertices.data()); glBindTexture(GL_TEXTURE_2D, i->batchTexture); glDrawArrays(GL_TRIANGLES, 0, (GLsizei)i->vertices.size()); i->vertices.clear(); }
i->batchTexture = texture;
RendererVertex2D v[6] = {{x,y,u0,v0,r,g,b,a},{x+w,y,u1,v0,r,g,b,a},{x+w,y+h,u1,v1,r,g,b,a},{x,y,u0,v0,r,g,b,a},{x+w,y+h,u1,v1,r,g,b,a},{x,y+h,u0,v1,r,g,b,a}};
i->vertices.insert(i->vertices.end(), v, v + 6);
}
#endif
void OpenGLRenderer::drawTexturedQuad(float x, float y, float w, float h, const wchar_t* p, float r, float g, float b, float a) {
if (!p) return; std::string path(p, p + wcslen(p)); const ImageData* image = m_impl->images.load(path);
#if defined(IMXUI_HAS_OPENGL)
if (!m_impl->initialized || !image || !image->valid()) return; unsigned texture = 0; auto it = m_impl->textures.find(path); if (it != m_impl->textures.end()) texture = it->second; if (!texture) { texture = makeTexture(*image); m_impl->textures[path] = texture; } appendQuad(m_impl, texture, x, y, w, h, 0, 0, 1, 1, r, g, b, a);
#else
(void)x; (void)y; (void)w; (void)h; (void)r; (void)g; (void)b; (void)a;
#endif
}
void OpenGLRenderer::drawColoredQuad(float x, float y, float w, float h, float r, float g, float b, float a) {
#if defined(IMXUI_HAS_OPENGL)
if (m_impl->initialized) appendQuad(m_impl, m_impl->white, x, y, w, h, 0, 0, 1, 1, r, g, b, a);
#else
(void)x; (void)y; (void)w; (void)h; (void)r; (void)g; (void)b; (void)a;
#endif
}
void OpenGLRenderer::drawTextString(float x, float y, float w, float h, const std::wstring& text, const std::wstring& name, float size, float r, float g, float b, float a, bool centered) {
#if defined(IMXUI_HAS_OPENGL)
if (!m_impl->initialized || text.empty()) return; if (!m_impl->font.isLoaded() && !m_impl->font.loadSystemFont(narrow(name))) return; auto it = m_impl->textTextures.find(text + L"|" + name + L"|" + std::to_wstring((int)(size * 10))); unsigned texture = it == m_impl->textTextures.end() ? 0 : it->second; int tw = 0, th = 0; if (!texture) { std::vector<uint8_t> pixels; if (!m_impl->font.renderText(text, size, pixels, tw, th)) return; ImageData image; image.w = tw; image.h = th; image.rgba.swap(pixels); texture = makeTexture(image); m_impl->textTextures[text + L"|" + name + L"|" + std::to_wstring((int)(size * 10))] = texture; } else { std::vector<uint8_t> ignored; m_impl->font.renderText(text, size, ignored, tw, th); } float dx = centered ? x + std::max(0.0f, (w - tw) * .5f) : x, dy = centered ? y + std::max(0.0f, (h - th) * .5f) : y; appendQuad(m_impl, texture, dx, dy, (float)tw, (float)th, 0, 0, 1, 1, r, g, b, a);
#else
(void)x; (void)y; (void)w; (void)h; (void)text; (void)name; (void)size; (void)r; (void)g; (void)b; (void)a; (void)centered;
#endif
}
void OpenGLRenderer::drawFigure(float x, float y, float w, float h, const std::vector<Vec2>& points, bool closed, float fr, float fg, float fb, float fa, float sr, float sg, float sb, float sa, float sw) {
#if defined(IMXUI_HAS_OPENGL)
if (!m_impl->initialized || points.size() < 2) return; if (fa > .001f && points.size() > 2) { for (size_t n = 1; n + 1 < points.size(); ++n) { RendererVertex2D v[6] = {{x+points[0].x*w,y+points[0].y*h,0,0,fr,fg,fb,fa},{x+points[n].x*w,y+points[n].y*h,0,0,fr,fg,fb,fa},{x+points[n+1].x*w,y+points[n+1].y*h,0,0,fr,fg,fb,fa},{x+points[0].x*w,y+points[0].y*h,0,0,fr,fg,fb,fa},{x+points[n+1].x*w,y+points[n+1].y*h,0,0,fr,fg,fb,fa},{x+points[n].x*w,y+points[n].y*h,0,0,fr,fg,fb,fa}}; if (m_impl->batchTexture != m_impl->white && !m_impl->vertices.empty()) { glBindBuffer(GL_ARRAY_BUFFER, m_impl->vbo); glBufferSubData(GL_ARRAY_BUFFER,0,(GLsizeiptr)(m_impl->vertices.size()*sizeof(RendererVertex2D)),m_impl->vertices.data()); glBindTexture(GL_TEXTURE_2D,m_impl->batchTexture); glDrawArrays(GL_TRIANGLES,0,(GLsizei)m_impl->vertices.size()); m_impl->vertices.clear(); } m_impl->batchTexture=m_impl->white; m_impl->vertices.insert(m_impl->vertices.end(),v,v+6); } } if (sa > .001f && sw > 0) { glLineWidth(sw); std::vector<RendererVertex2D> v; for (size_t n=0; n + 1 < points.size(); ++n) { v.push_back({x+points[n].x*w,y+points[n].y*h,0,0,sr,sg,sb,sa}); v.push_back({x+points[n+1].x*w,y+points[n+1].y*h,0,0,sr,sg,sb,sa}); } if (closed) { v.push_back({x+points.back().x*w,y+points.back().y*h,0,0,sr,sg,sb,sa}); v.push_back({x+points[0].x*w,y+points[0].y*h,0,0,sr,sg,sb,sa}); } if (m_impl->batchTexture != m_impl->white && !m_impl->vertices.empty()) { glBindBuffer(GL_ARRAY_BUFFER,m_impl->vbo); glBufferSubData(GL_ARRAY_BUFFER,0,(GLsizeiptr)(m_impl->vertices.size()*sizeof(RendererVertex2D)),m_impl->vertices.data()); glBindTexture(GL_TEXTURE_2D,m_impl->batchTexture); glDrawArrays(GL_TRIANGLES,0,(GLsizei)m_impl->vertices.size()); m_impl->vertices.clear(); } m_impl->batchTexture=m_impl->white; glBindBuffer(GL_ARRAY_BUFFER,m_impl->vbo); glBufferSubData(GL_ARRAY_BUFFER,0,(GLsizeiptr)(v.size()*sizeof(RendererVertex2D)),v.data()); glDrawArrays(GL_LINES,0,(GLsizei)v.size()); }
#else
(void)x;(void)y;(void)w;(void)h;(void)points;(void)closed;(void)fr;(void)fg;(void)fb;(void)fa;(void)sr;(void)sg;(void)sb;(void)sa;(void)sw;
#endif
}
int OpenGLRenderer::getWidth() const { return m_impl->width; }
int OpenGLRenderer::getHeight() const { return m_impl->height; }
void OpenGLRenderer::beginScene(float, float) {}
void OpenGLRenderer::drawColoredQuad(const Rect& r, const Color& c, float o) { drawColoredQuad(r.x,r.y,r.w,r.h,c.r,c.g,c.b,c.a*o); }
void OpenGLRenderer::drawTexturedQuad(const Rect& r, const std::wstring& p, const Color& c, float o) { drawTexturedQuad(r.x,r.y,r.w,r.h,p.c_str(),c.r,c.g,c.b,c.a*o); }
void OpenGLRenderer::drawText(const Rect& r, const std::wstring& t, const std::wstring& f, float s, const Color& c, float o, bool center) { drawTextString(r.x,r.y,r.w,r.h,t,f,s,c.r,c.g,c.b,c.a*o,center); }
void OpenGLRenderer::endScene() {}
void OpenGLRenderer::pushClip(const Rect& r) {
#if defined(IMXUI_HAS_OPENGL)
glEnable(GL_SCISSOR_TEST); glScissor((int)r.x, (int)(m_impl->height-r.y-r.h), (int)r.w, (int)r.h);
#else
(void)r;
#endif
}
void OpenGLRenderer::popClip() {
#if defined(IMXUI_HAS_OPENGL)
glDisable(GL_SCISSOR_TEST);
#endif
}
void OpenGLRenderer::drawNineGrid(const Rect& r, const std::wstring& p, const Color& c, float o, uint32_t, uint32_t, uint32_t, uint32_t, bool) { drawTexturedQuad(r,p,c,o); }
void OpenGLRenderer::drawFigure(const Rect& r, const std::vector<Vec2>& points, bool closed, const Color& fill, const Color& stroke, float opacity, float strokeWidth, uint32_t blendMode) { setBlendMode(blendMode); drawFigure(r.x,r.y,r.w,r.h,points,closed,fill.r,fill.g,fill.b,fill.a*opacity,stroke.r,stroke.g,stroke.b,stroke.a*opacity,strokeWidth); }
void OpenGLRenderer::setBlendMode(uint32_t mode) {
#if defined(IMXUI_HAS_OPENGL)
glBlendFunc(GL_SRC_ALPHA, mode == 1 ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA);
#else
(void)mode;
#endif
}
void OpenGLRenderer::drawCustom(const std::wstring&, const Rect&, float) {}
}
+9
View File
@@ -10,9 +10,15 @@ namespace ImXui {
bool TextureManager::init(ID3D11Device* device) {
m_device = device;
m_images.setBasePath(m_basePath);
return true;
}
const ImageData* TextureManager::loadImage(const std::string& relativePath) {
m_images.setBasePath(m_basePath);
return m_images.load(relativePath);
}
void TextureManager::shutdown() {
for (std::unordered_map<std::string, ID3D11ShaderResourceView*>::iterator it = m_cache.begin(); it != m_cache.end(); ++it) {
if (it->second) it->second->Release();
@@ -29,6 +35,9 @@ ID3D11ShaderResourceView* TextureManager::load(const std::string& relativePath)
auto it = m_cache.find(relativePath);
if (it != m_cache.end()) return it->second;
const ImageData* decoded = loadImage(relativePath);
if (!decoded) return nullptr;
std::string fullPath;
if (!m_basePath.empty()) {
fullPath = m_basePath;