ocornut / ocornut/imgui_test_engine
Screen capture on macOS with HighDPI fails because of FrameBufferScale!=1
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 629
- Forks
- 83
- PR merge metrics
- No merged PRs in 30d
Description
Bonjour Omar,
Under macOS with HighDpi, ImGui::GetDrawData().FrameBufferScale is equal to (2., 2.)
The window coordinates are correctly shown by the capture tool, however when capturing we get a capture with bad coordinates
edges shown by capture tool
capture that I get
This is due to the fact that the FramebufferScale is not 1.
This is probably related to https://github.com/ocornut/imgui_test_engine/issues/8
See the current capture implementation in imgui_app.cpp: it cannot handle the scale
#if defined(IMGUI_APP_GL2) || defined(IMGUI_APP_GL3)
static bool ImGuiApp_ImplGL_CaptureFramebuffer(ImGuiApp* app, int x, int y, int w, int h, unsigned int* pixels, void* user_data)
{
IM_UNUSED(app);
IM_UNUSED(user_data);
#ifdef __linux__
// FIXME: Odd timing issue is observed on linux (Plasma/X11 specifically), which causes outdated frames to be captured, unless we give compositor some time to update screen.
// glFlush() didn't seem enough. Will probably need to revisit that.
usleep(1000); // 1ms
#endif
int y2 = (int)ImGui::GetIO().DisplaySize.y - (y + h);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(x, y2, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Flip vertically
size_t comp = 4;
size_t stride = (size_t)w * comp;
unsigned char* line_tmp = new unsigned char[stride];
unsigned char* line_a = (unsigned char*)pixels;
unsigned char* line_b = (unsigned char*)pixels + (stride * ((size_t)h - 1));
while (line_a < line_b)
{
memcpy(line_tmp, line_a, stride);
memcpy(line_a, line_b, stride);
memcpy(line_b, line_tmp, stride);
line_a += stride;
line_b -= stride;
}
delete[] line_tmp;
return true;
}
#endif
I previously implemented a screen capture utility in HelloImGui, and I had to take the FrameBufferScale into account:
see opengl_screenshot.cpp in the hello imgui code.
Basically, it multiplies the capture size, like this:
auto draw_data = ImGui::GetDrawData();
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
However this solution cannot apply here for two main reasons:
- the pixel buffer is preallocated, and thus cannot fit the OpenGL capture (which will be 4 times as big as the allocated buffer, since width and height are twice bigger)
- when inside ImGuiApp_ImplGL_CaptureFramebuffer,
GetDrawData()may return null so that we cannot reliably access the FrameBufferScale
I tried a dirty hack that consists of creating an intermediary buffer for the capture, and then do a simple resize. It can work, but it is not very nice, and require to allocate an intermediary buffer.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in imgui_app.cpp at ImGuiApp_ImplGL_CaptureFramebuffer and compare its coordinate and buffer assumptions with ImGui::GetDrawData()->FramebufferScale. Review the linked HelloImGui opengl_screenshot.cpp example for how scaling is handled. Done means macOS HighDPI captures have correct coordinates without overrunning the preallocated pixel buffer, including when GetDrawData() is null.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- devtools, testing
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100