Skip to content

Commit

Permalink
sRGB support
Browse files Browse the repository at this point in the history
  • Loading branch information
20k committed Dec 23, 2019
1 parent 061650b commit 46c42b5
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 0 deletions.
18 changes: 18 additions & 0 deletions examples/imgui_impl_opengl3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 1
#endif

// sRGB works differently in OpenGL ES, this is a desktop-only implementation
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
#define SRGB_FRAMEBUFFERS_ARE_POSSIBLE 1
#else
#define SRGB_FRAMEBUFFERS_ARE_POSSIBLE 0
#endif

// OpenGL Data
static GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries.
static char g_GlslVersionString[32] = ""; // Specified by user or detected based on compile time GL settings.
Expand Down Expand Up @@ -222,6 +229,10 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
#if SRGB_FRAMEBUFFERS_ARE_POSSIBLE
if(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_IsSRGB)
glEnable(GL_FRAMEBUFFER_SRGB);
#endif // SRGB_FRAMEBUFFERS_ARE_POSSIBLE

// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
Expand Down Expand Up @@ -298,6 +309,10 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
#if SRGB_FRAMEBUFFERS_ARE_POSSIBLE
GLboolean last_enable_framebuffer_srgb = glIsEnabled(GL_FRAMEBUFFER_SRGB);
#endif

bool clip_origin_lower_left = true;
#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__)
GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
Expand Down Expand Up @@ -391,6 +406,9 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
#if SRGB_FRAMEBUFFERS_ARE_POSSIBLE
if (last_enable_framebuffer_srgb) glEnable(GL_FRAMEBUFFER_SRGB); else glDisable(GL_FRAMEBUFFER_SRGB);
#endif // SRGB_FRAMEBUFFERS_ARE_POSSIBLE
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
#endif
Expand Down
119 changes: 119 additions & 0 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2283,6 +2283,26 @@ const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
return style.Colors[idx];
}

ImVec4 ImGui::GetStyleLinearColor(ImGuiCol idx)
{
ImGuiStyle& style = GImGui->Style;

if(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_IsSRGB)
return ImGui::SRGBToLinear(style.Colors[idx]);
else
return style.Colors[idx];
}

ImVec4 ImGui::GetStyleSRGBColor(ImGuiCol idx)
{
ImGuiStyle& style = GImGui->Style;

if(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_IsSRGB)
return style.Colors[idx];
else
return ImGui::LinearToSRGB(style.Colors[idx]);
}

ImU32 ImGui::GetColorU32(ImU32 col)
{
ImGuiStyle& style = GImGui->Style;
Expand Down Expand Up @@ -6264,6 +6284,73 @@ void ImGui::PopTextWrapPos()
window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
}

// These functions are quite slow because they are 'correct', but currently they are only used for conversions within ImGui itself in a one-off manner
float ImGui::SRGBToLinear(float in)
{
if(in <= 0.04045f)
return in / 12.92f;
else
return powf((in + 0.055f) / 1.055f, 2.4f);
}

float ImGui::LinearToSRGB(float in)
{
if (in <= 0.0031308f)
return in * 12.92f;
else
return 1.055f * powf(in, 1.0f / 2.4f) - 0.055f;
}

ImVec4 ImGui::SRGBToLinear(ImVec4 col)
{
col.x = SRGBToLinear(col.x);
col.y = SRGBToLinear(col.y);
col.z = SRGBToLinear(col.z);
// Alpha component is already linear

return col;
}

ImVec4 ImGui::LinearToSRGB(ImVec4 col)
{
col.x = LinearToSRGB(col.x);
col.y = LinearToSRGB(col.y);
col.z = LinearToSRGB(col.z);
// Alpha component is already linear

return col;
}

void ImGui::ForceStyleColorSpaceConversion(ImGuiStyle* style, bool to_linear)
{
// This is a bit of a hack
// Converts all ImGui's current styling to the correct color space
// Given that you generally don't need to toggle sRGB <-> linear color in a performant way this is probably acceptable
// But done a lot of times small precision issues will crop up eventually
for(int i=0; i < ImGuiCol_COUNT; i++)
{
ImVec4& col = style->Colors[i];

if(to_linear)
col = SRGBToLinear(col);
else
col = LinearToSRGB(col);
}
}

void ImGui::SetStyleLinearColor(bool is_linear)
{
if(is_linear == (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_IsSRGB))
return;

ImGui::ForceStyleColorSpaceConversion(&ImGui::GetStyle(), is_linear);

if(is_linear)
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_IsSRGB;
else
ImGui::GetIO().ConfigFlags &= (~ImGuiConfigFlags_IsSRGB);
}

// FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32
void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
{
Expand All @@ -6285,6 +6372,38 @@ void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
g.Style.Colors[idx] = col;
}

void ImGui::PushStyleSRGBColor(ImGuiCol idx, ImU32 col)
{
if(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_IsSRGB)
PushStyleColor(idx, SRGBToLinear(ColorConvertU32ToFloat4(col)));
else
PushStyleColor(idx, col);
}

void ImGui::PushStyleSRGBColor(ImGuiCol idx, const ImVec4& col)
{
if(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_IsSRGB)
PushStyleColor(idx, SRGBToLinear(col));
else
PushStyleColor(idx, col);
}

void ImGui::PushStyleLinearColor(ImGuiCol idx, ImU32 col)
{
if(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_IsSRGB)
PushStyleColor(idx, ColorConvertU32ToFloat4(col));
else
PushStyleColor(idx, LinearToSRGB(ColorConvertU32ToFloat4(col)));
}

void ImGui::PushStyleLinearColor(ImGuiCol idx, const ImVec4& col)
{
if(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_IsSRGB)
PushStyleColor(idx, col);
else
PushStyleColor(idx, LinearToSRGB(col));
}

void ImGui::PopStyleColor(int count)
{
ImGuiContext& g = *GImGui;
Expand Down
13 changes: 13 additions & 0 deletions imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,13 +318,20 @@ namespace ImGui
// Parameters stacks (shared)
IMGUI_API void PushFont(ImFont* font); // use NULL as a shortcut to push default font
IMGUI_API void PopFont();
IMGUI_API void SetStyleLinearColor(bool is_linear); // configures styling to work correctly with sRGB
IMGUI_API void PushStyleColor(ImGuiCol idx, ImU32 col);
IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col);
IMGUI_API void PushStyleLinearColor(ImGuiCol idx, ImU32 col); // same as PushStyleColor, but performs colour space conversions if necessary from linear color
IMGUI_API void PushStyleLinearColor(ImGuiCol idx, const ImVec4& col);
IMGUI_API void PushStyleSRGBColor(ImGuiCol idx, ImU32 col); // same as PushStyleColor, but performs colour space conversions if necessary from sRGB
IMGUI_API void PushStyleSRGBColor(ImGuiCol idx, const ImVec4& col);
IMGUI_API void PopStyleColor(int count = 1);
IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val);
IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val);
IMGUI_API void PopStyleVar(int count = 1);
IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwise use GetColorU32() to get style color with style alpha baked in.
IMGUI_API ImVec4 GetStyleLinearColor(ImGuiCol idx); // same as above, but always in the linear color space
IMGUI_API ImVec4 GetStyleSRGBColor(ImGuiCol idx); // same as above, but always in the sRGB color space
IMGUI_API ImFont* GetFont(); // get current font
IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied
IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API
Expand Down Expand Up @@ -673,6 +680,12 @@ namespace ImGui
IMGUI_API void ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v);
IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b);

IMGUI_API float SRGBToLinear(float srgb_color);
IMGUI_API float LinearToSRGB(float linear_color);

IMGUI_API ImVec4 SRGBToLinear(ImVec4 srgb_color);
IMGUI_API ImVec4 LinearToSRGB(ImVec4 linear_color);

// Inputs Utilities: Keyboard
// - For 'int user_key_index' you can use your own indices/enums according to how your backend/engine stored them in io.KeysDown[].
// - We don't know the meaning of those value. You can use GetKeyIndex() to map a ImGuiKey_ value into the user index.
Expand Down
9 changes: 9 additions & 0 deletions imgui_draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);

if(GImGui && (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_IsSRGB))
ImGui::ForceStyleColorSpaceConversion(style, true);
}

void ImGui::StyleColorsClassic(ImGuiStyle* dst)
Expand Down Expand Up @@ -281,6 +284,9 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);

if(GImGui && (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_IsSRGB))
ImGui::ForceStyleColorSpaceConversion(style, true);
}

// Those light colors are better suited with a thicker font than the default one + FrameBorder
Expand Down Expand Up @@ -337,6 +343,9 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.20f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);

if(GImGui && (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_IsSRGB))
ImGui::ForceStyleColorSpaceConversion(style, true);
}

//-----------------------------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions imgui_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1802,6 +1802,9 @@ namespace ImGui
IMGUI_API void ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags);
IMGUI_API void ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags);

// True to force conversion to linear, false to force conversion to sRGB
IMGUI_API void ForceStyleColorSpaceConversion(ImGuiStyle* which, bool to_linear);

// Plot
IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size);

Expand Down

0 comments on commit 46c42b5

Please sign in to comment.