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;
}
#endifI 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.
Activity
5While I am currently unhappy with the use of io.FramebufferScale in general (I believe we should make the coordinates match, which I will do once I have a large DPI pass incl on OSX), i believe in the meanwhile we should add support for it in the capture tool.
Would you accept a PR with a polished version of this hack, or do you prefer to change the capture size (in which case, it will be too hard for me)?
By polished I mean that I could remove the unnecessary calls to lambdas (x_to_scaled), and add some pointer logic in the conversion loop, in order to make it faster. I could not remove the allocation, although.
I'd prefer to fix the capture size but I'll probably need your help to test it as I don't presently have a Mac.
Ok, ping me when you want me to run a test. My mac reports FrameBufferScale = (2, 2). I never encountered a situation where x_scale != y_scale and/or where they are not an integer.
For info, I wrote a FAQ where I grouped my findings for emscripten, macOS, linux and windows concerning the Dpi handling: https://pthom.github.io/imgui_bundle/faq.html
For information, I could implement a temporary hack. If you are interested, I include it below.
The principle is to acquire in a temporary buffer with the correct size, and then do a quick and dirty down-sampling.
Beware:
ImVec2 framebufferScale = ImGui::GetDrawData()->FramebufferScalemay fail because GetDrawData() returns null