Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: drawing images #57

Closed
joffreybesos opened this issue Nov 23, 2022 · 10 comments · Fixed by #146
Closed

Feature request: drawing images #57

joffreybesos opened this issue Nov 23, 2022 · 10 comments · Fixed by #146
Labels
enhancement New feature or request
Milestone

Comments

@joffreybesos
Copy link
Contributor

To draw images using ImGui from what I can tell you need to load textures before you can do draw_bg.add_image(texture_id, p_min, p_max).
To accomplish this, from what I can understand, you would basically need to do what's happening in create_font_texture but for an image file or array of bytes, is that correct?

@veeenu
Copy link
Owner

veeenu commented Nov 23, 2022

Yes, I think that's about right! I have never used images myself but it would indeed be great if we could support those, so thank you for opening this ticket.

This should not be a particularly complicated feature, but it would require a good amount of testing, adding texture management to each of the renderers we currently support, and additions to the render loop API.

For this reason I'm hesitant to schedule this feature for the upcoming 0.3.0 release, but it will definitely happen at some point!

@veeenu veeenu added the enhancement New feature or request label Nov 23, 2022
@joffreybesos
Copy link
Contributor Author

I actually tried to cheat at this by importing an image as a bunch of raw bytes, then doing a draw.draw_rect() for each pixel. This actually works however it seems there's a hard ceiling on the amount of draw commands. Not recommending this strategy just something consider in case someone else thinks of doing the same.

@veeenu
Copy link
Owner

veeenu commented Dec 2, 2022

That's clever 😅

But long term I think implementing an API around textures is definitely more desirable.

@vars1ty
Copy link
Contributor

vars1ty commented Dec 15, 2023

I've gotten it working in OpenGL by finding the function addresses of the draw functions needed for OpenGL to upload the texture to the GPU, then just called them and add_image.

Might release source soon, probably won't add support for anything but OpenGL though as I have zero experience with other renderers.

Reference for this: /~https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples#example-for-opengl-users

image

@vars1ty
Copy link
Contributor

vars1ty commented Dec 18, 2023

Alright, so here's the code, you'll need the Windows and image crate for it to work.

Getting module symbol function:

    /// Gets the address to a function inside of a module.
    pub fn get_module_symbol_address(module: &str, symbol: &str) -> Option<usize> {
        let module_utf16_bytes = module
            .encode_utf16()
            .chain(std::iter::once(0))
            .collect::<Vec<u16>>();
        let w_module = PCWSTR(module_utf16_bytes.as_ptr());
        let c_symbol = CString::new(symbol).unwrap();
        let w_symbol = PCSTR(c_symbol.as_ptr() as _);
        unsafe {
            let handle = GetModuleHandleW(w_module).expect("Failed getting module handle!");
            GetProcAddress(handle, w_symbol).map(|result| result as usize)
        }
    }

Load image function:

    /// OpenGL Only: Uploads the specified image to the GPU, then returns the pointer to that
    /// image, which you may use with ImGui using a TextureId.
    pub fn ogl_load_image(path: &str) -> u32 {
        let image = image::open(path).expect("Failed loading desired image!");
        let image_data = image.to_rgba8().into_raw();
        let mut texture_id = 0;

        unsafe {
            // Constants needed for uploading an image to the GPU.
            const TEXTURE_MAG_FILTER: u32 = 10240;
            const TEXTURE_MIN_FILTER: u32 = 10241;
            const TEXTURE_WRAP_S: u32 = 10242;
            const TEXTURE_WRAP_T: u32 = 10243;
            const CLAMP_TO_EDGE: u32 = 33071;
            const UNSIGNED_BYTE: u32 = 5121;
            const TEXTURE_2D: u32 = 3553;
            const LINEAR: u32 = 9729;
            const RGBA: u32 = 6408;

            // OnceLock for each function we'll be using.
            // Since they're static, they are only assigned once and remain alive until the end of
            // the process.
            static GEN_TEXTURES: OnceLock<usize> = OnceLock::new();
            static BIND_TEXTURE: OnceLock<usize> = OnceLock::new();
            static TEX_PARAMETER_I: OnceLock<usize> = OnceLock::new();
            static TEX_IMAGE_2D: OnceLock<usize> = OnceLock::new();
            static FLUSH: OnceLock<usize> = OnceLock::new();

            // Find the functions needed, crash otherwise.
            let gen_textures = *GEN_TEXTURES.get_or_init(|| {
                WinUtils::get_module_symbol_address("opengl32.dll", "glGenTextures").expect("glGenTextures not found!")
            });
            let bind_texture = *BIND_TEXTURE.get_or_init(|| {
                WinUtils::get_module_symbol_address("opengl32.dll", "glBindTexture").expect("glBindTexture not found!")
            });
            let tex_parameter_i = *TEX_PARAMETER_I.get_or_init(|| {
                WinUtils::get_module_symbol_address("opengl32.dll", "glTexParameteri").expect("glTexParameteri not found!")
            });
            let tex_image_2d = *TEX_IMAGE_2D.get_or_init(|| {
                WinUtils::get_module_symbol_address("opengl32.dll", "glTexImage2D").expect("glTexImage2D not found!")
            });
            let flush = *FLUSH.get_or_init(|| {
                WinUtils::get_module_symbol_address("opengl32.dll", "glFlush").expect("glFlush not found!")
            });

            // Transmute functions so we can use them.
            let gen_textures: fn(i32, *mut u32) = std::mem::transmute(gen_textures);
            let bind_texture: fn(u32, u32) = std::mem::transmute(bind_texture);
            let tex_parameter_i: fn(u32, u32, i32) = std::mem::transmute(tex_parameter_i);
            let tex_image_2d: fn(u32, i32, i32, i32, i32, i32, u32, u32, *const c_void) =
                std::mem::transmute(tex_image_2d);
            let flush: fn() = std::mem::transmute(flush);

            // Upload the texture to the GPU.
            gen_textures(1, &mut texture_id);
            bind_texture(TEXTURE_2D, texture_id);

            tex_parameter_i(TEXTURE_2D, TEXTURE_MIN_FILTER, LINEAR as i32);
            tex_parameter_i(TEXTURE_2D, TEXTURE_MAG_FILTER, LINEAR as i32);
            tex_parameter_i(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE as i32);
            tex_parameter_i(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE as i32);

            tex_image_2d(
                TEXTURE_2D,
                0,
                RGBA as i32,
                image.width() as i32,
                image.height() as i32,
                0,
                RGBA,
                UNSIGNED_BYTE,
                image_data.as_ptr() as _,
            );

            // Flush just in case we have unused data.
            flush();
        }

        texture_id
    }

The load image function gives you the pointer to pass into TextureId, after that it'll "just work".

Video showcasing it where I cached a bunch of frames extracted from a GIF, then iterated through them:
/~https://github.com/veeenu/hudhook/assets/54314240/352fb04f-3b07-421c-800d-d302d9c55a0b

@veeenu
Copy link
Owner

veeenu commented Dec 18, 2023

Awesome, thank you!

I see there are other snippets in imgui's repo for the other renderers, might end up supporting those as well.

Hopefully I'll resume maintenance of this pretty soon.

@vars1ty
Copy link
Contributor

vars1ty commented Dec 18, 2023

Yeah most of the work is just making it be suited for the other renderers. I was considering doing DirectX, but after I saw the chaos of the DX12 sample, I just closed it and moved on until I'm just that bored to fix it.

@veeenu
Copy link
Owner

veeenu commented Jan 2, 2024

#141 could make this (and a bunch of other stuff) pretty simple.

@veeenu
Copy link
Owner

veeenu commented Jan 26, 2024

#141 WILL definitely make this pretty simple.

Leaving this here and I will probably implement it shortly.

@veeenu veeenu added this to the 0.6.0 milestone Jan 30, 2024
@veeenu veeenu mentioned this issue Jan 30, 2024
@veeenu
Copy link
Owner

veeenu commented Jan 30, 2024

2024-01-30_105822.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants