From 1c309b2c89f6bc23c0f26453b22ce2e9f175b617 Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Wed, 1 Mar 2023 23:24:26 +0100 Subject: [PATCH] Added WhiteBalance to RenderingWindow and Display View Render output frame to output window using a Shader implementing white balance correction. Adjusting parameters of white balance from Display View with color picker (white) and slider (temperature). GLSL filter for white balance created from ShaderToy. --- CMakeLists.txt | 1 + rsc/images/icons.dds | Bin 1638528 -> 1638528 bytes rsc/shaders/filters/whitebalance.glsl | 262 ++++++++++++++++++++++++++ src/DisplaysView.cpp | 127 ++++++++----- src/DisplaysView.h | 1 + src/ImageFilter.cpp | 36 ---- src/ImageFilter.h | 36 +++- src/RenderingManager.cpp | 40 +++- src/RenderingManager.h | 5 + src/TextureView.cpp | 2 +- 10 files changed, 428 insertions(+), 82 deletions(-) create mode 100644 rsc/shaders/filters/whitebalance.glsl diff --git a/CMakeLists.txt b/CMakeLists.txt index ed74e46..20fae6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -613,6 +613,7 @@ set(VMIX_RSC_FILES ./rsc/shaders/filters/resample_quarter.glsl ./rsc/shaders/filters/earlybird.glsl ./rsc/shaders/filters/logo.glsl + ./rsc/shaders/filters/whitebalance.glsl ) cmrc_add_resource_library(vmix-resources ALIAS vmix::rc NAMESPACE vmix WHENCE rsc ${VMIX_RSC_FILES}) diff --git a/rsc/images/icons.dds b/rsc/images/icons.dds index 6654a7be066efabac0cfa7e210bf9029985c5cf2..4c190c2599bb2e24b7c518087bd2c2ca93ca251b 100644 GIT binary patch delta 5918 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 TZ5*^nXyc(x5bkFiE9?FZQ+4mr delta 5465 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 ZHIN1<0cZhGB2W_0LZD>fR;jkO@jplh?Ro$J diff --git a/rsc/shaders/filters/whitebalance.glsl b/rsc/shaders/filters/whitebalance.glsl new file mode 100644 index 0000000..01117cc --- /dev/null +++ b/rsc/shaders/filters/whitebalance.glsl @@ -0,0 +1,262 @@ +// From https://www.shadertoy.com/view/Ml2BRD +// and https://www.shadertoy.com/view/MtcfDr +// By Tynach. +// Adapted by Bruno Herbelin for vimix + +precision highp float; + +/* + * Settings + */ + +// Minimum temperature in the range (defined in the standard as 4000) +const float minTemp = 4000.0; + +// Maximum temperature in the range (defined in the standard as 25000) +const float maxTemp = 9000.0; + +// [0 1] value slider between min and max Temp +uniform float Temperature; + +// Color for picked 'grey' +uniform float Red; +uniform float Green; +uniform float Blue; + + +// Parameters for transfer characteristics (gamma curves) +struct transfer { + // Exponent used to linearize the signal + float power; + + // Offset from 0.0 for the exponential curve + float off; + + // Slope of linear segment near 0 + float slope; + + // Values below this are divided by slope during linearization + float cutoffToLinear; + + // Values below this are multiplied by slope during gamma correction + float cutoffToGamma; +}; + +// Parameters for a colorspace +struct rgb_space { + // Chromaticity coordinates (xyz) for Red, Green, and Blue primaries + mat4 primaries; + + // Chromaticity coordinates (xyz) for white point + vec4 white; + + // Linearization and gamma correction parameters + transfer trc; +}; + + +/* + * Preprocessor 'functions' that help build colorspaces as constants + */ + +// Turns 6 chromaticity coordinates into a 3x3 matrix +#define Primaries(r1, r2, g1, g2, b1, b2)\ + mat4(\ + (r1), (r2), 1.0 - (r1) - (r2), 0,\ + (g1), (g2), 1.0 - (g1) - (g2), 0,\ + (b1), (b2), 1.0 - (b1) - (b2), 0,\ + 0, 0, 0, 1) + +// Creates a whitepoint's xyz chromaticity coordinates from the given xy coordinates +#define White(x, y)\ + vec4(vec3((x), (y), 1.0 - (x) - (y))/(y), 1) + +// Automatically calculate the slope and cutoffs for transfer characteristics +#define Transfer(po, of)\ +transfer(\ + (po),\ + (of),\ + (pow((po)*(of)/((po) - 1.0), 1.0 - (po))*pow(1.0 + (of), (po)))/(po),\ + (of)/((po) - 1.0),\ + (of)/((po) - 1.0)*(po)/(pow((po)*(of)/((po) - 1.0), 1.0 - (po))*pow(1.0 + (of), (po)))\ +) + +// Creates a scaling matrix using a vec4 to set the xyzw scalars +#define diag(v)\ + mat4(\ + (v).x, 0, 0, 0,\ + 0, (v).y, 0, 0,\ + 0, 0, (v).z, 0,\ + 0, 0, 0, (v).w) + +// Creates a conversion matrix that turns RGB colors into XYZ colors +#define rgbToXyz(space)\ + (space.primaries*diag(inverse((space).primaries)*(space).white)) + +// Creates a conversion matrix that turns XYZ colors into RGB colors +#define xyzToRgb(space)\ + inverse(rgbToXyz(space)) + + +/* + * Chromaticities for RGB primaries + */ + + +// Rec. 709 (HDTV) and sRGB primaries +const mat4 primaries709 = Primaries( + 0.64, 0.33, + 0.3, 0.6, + 0.15, 0.06 +); + + +// Same as above, but in fractional form +const mat4 primariesLms = Primaries( + 194735469.0/263725741.0, 68990272.0/263725741.0, + 141445123.0/106612934.0, -34832189.0/106612934.0, + 36476327.0/229961670.0, 0.0 +); + + +/* + * Chromaticities for white points + */ + +// Standard illuminant E (also known as the 'equal energy' white point) +const vec4 whiteE = White(1.0/3.0, 1.0/3.0); + +// Standard Illuminant D65 according to the Rec. 709 and sRGB standards +const vec4 whiteD65S = White(0.3127, 0.3290); + +/* + * Gamma curve parameters + */ +// Linear gamma +const transfer gam10 = transfer(1.0, 0.0, 1.0, 0.0, 0.0); +// Gamma for sRGB +const transfer gamSrgb = transfer(2.4, 0.055, 12.92, 0.04045, 0.0031308); + +/* + * RGB Colorspaces + */ +// sRGB (mostly the same as Rec. 709, but different gamma and full range values) +const rgb_space Srgb = rgb_space(primaries709, whiteD65S, gamSrgb); + +// Lms primaries, balanced against equal energy white point +const rgb_space LmsRgb = rgb_space(primariesLms, whiteE, gam10); + + +/* + * Colorspace conversion functions + */ + +// Converts RGB colors to a linear light scale +vec4 toLinear(vec4 color, const transfer trc) +{ + bvec4 cutoff = lessThan(color, vec4(trc.cutoffToLinear)); + bvec4 negCutoff = lessThanEqual(color, vec4(-1.0*trc.cutoffToLinear)); + vec4 higher = pow((color + trc.off)/(1.0 + trc.off), vec4(trc.power)); + vec4 lower = color/trc.slope; + vec4 neg = -1.0*pow((color - trc.off)/(-1.0 - trc.off), vec4(trc.power)); + + vec4 result = mix(higher, lower, cutoff); + return mix(result, neg, negCutoff); +} + +// Gamma-corrects RGB colors to be sent to a display +vec4 toGamma(vec4 color, const transfer trc) +{ + bvec4 cutoff = lessThan(color, vec4(trc.cutoffToGamma)); + bvec4 negCutoff = lessThanEqual(color, vec4(-1.0*trc.cutoffToGamma)); + vec4 higher = (1.0 + trc.off)*pow(color, vec4(1.0/trc.power)) - trc.off; + vec4 lower = color*trc.slope; + vec4 neg = (-1.0 - trc.off)*pow(-1.0*color, vec4(1.0/trc.power)) + trc.off; + + vec4 result = mix(higher, lower, cutoff); + return mix(result, neg, negCutoff); +} + +// Scales a color to the closest in-gamut representation of that color +vec4 gamutScale(vec4 color, float luma) +{ + float low = min(color.r, min(color.g, min(color.b, 0.0))); + float high = max(color.r, max(color.g, max(color.b, 1.0))); + + float lowScale = low/(low - luma); + float highScale = max((high - 1.0)/(high - luma), 0.0); + float scale = max(lowScale, highScale); + color.rgb += scale*(luma - color.rgb); + + return color; +} +// Calculate Standard Illuminant Series D light source XYZ values +vec4 temperatureToXyz(float temperature) +{ + // Calculate terms to be added up. Since only the coefficients aren't + // known ahead of time, they're the only thing determined by mix() + float x = dot(mix( + vec4(0.244063, 99.11, 2967800.0, -4607000000.0), + vec4(0.23704, 247.48, 1901800.0, -2006400000.0), + bvec4(temperature > 7000.0) + )/vec4(1, temperature, pow(temperature, 2.0), pow(temperature, 3.0)), vec4(1)); + + return White(x, -3.0*pow(x, 2.0) + 2.87*x - 0.275); +} + + +// XYZ conversion matrices for the display colorspace +const mat4 toXyz = rgbToXyz(Srgb); +const mat4 toRgb = xyzToRgb(Srgb); + +// LMS conversion matrices for white point adaptation +const mat4 toLms = xyzToRgb(LmsRgb); +const mat4 frLms = rgbToXyz(LmsRgb); + + +// Converts from one RGB colorspace to another, output as linear light +vec4 convert(vec4 color, vec4 whiteNew) +{ + color = toXyz*color; + + color= toLms*color; + color = diag(((toLms*Srgb.white)/(toLms*whiteNew)))*color; + color = inverse(toLms)*color; + float luma = color.y; + + color = toRgb*color; + color = gamutScale(color, luma); + + return color; +} + +// Main function +void mainImage(out vec4 color, in vec2 coord) +{ + vec2 uv = coord.xy / iResolution.xy; + vec4 texColor = texture(iChannel0, uv); + + texColor = toLinear(texColor, Srgb.trc); + + // Calculate temperature conversion + vec4 convWhite = temperatureToXyz(Temperature*(maxTemp - minTemp) + minTemp); + mat4 adapt = toRgb*frLms*diag((toLms*convWhite)/(toLms*Srgb.white))*toLms*toXyz; + texColor = adapt*texColor; + + // Darken values to fit within gamut (for texture) + vec4 newWhite = adapt*vec4(1); + texColor.rgb /= max(newWhite.r, max(newWhite.g, newWhite.b)); + color = texColor; + + // color balance by color picker + vec4 whiteNew = vec4( Red, Green, Blue, 1.0); + whiteNew = toXyz*whiteNew; + whiteNew /= dot(whiteNew, vec4(1.0)); + whiteNew /= whiteNew.y; + color = convert(color, whiteNew); + + // Convert to display gamma curve + color = toGamma(color, Srgb.trc); +} + + diff --git a/src/DisplaysView.cpp b/src/DisplaysView.cpp index 4799f86..7b41655 100644 --- a/src/DisplaysView.cpp +++ b/src/DisplaysView.cpp @@ -119,8 +119,8 @@ DisplaysView::DisplaysView() : View(DISPLAYS) // initial behavior: no window selected, no menu show_window_menu_ = false; current_window_ = -1; - current_window_status_ = new Group; - + current_window_status_ = new Group; + current_window_whitebalance = glm::vec4(1.f, 1.f, 1.f, 0.5f); draw_pending_ = false; // display actions : 0 = move output, 1 paint, 2 erase @@ -377,15 +377,19 @@ void DisplaysView::draw() // Add / Remove windows ImGui::SameLine(); if ( Settings::application.num_output_windows < MAX_OUTPUT_WINDOW) { - if (ImGuiToolkit::IconButton(18, 4, "More windows")) + if (ImGuiToolkit::IconButton(18, 4, "More windows")) { ++Settings::application.num_output_windows; + current_window_ = Settings::application.num_output_windows-1; + } } else ImGuiToolkit::Icon(18, 4, false); ImGui::SameLine(); if ( Settings::application.num_output_windows > 0 ) { - if (ImGuiToolkit::IconButton(19, 4, "Less windows")) + if (ImGuiToolkit::IconButton(19, 4, "Less windows")) { --Settings::application.num_output_windows; + current_window_ = -1; + } } else ImGuiToolkit::Icon(19, 4, false); @@ -393,47 +397,73 @@ void DisplaysView::draw() // Modify current window if (current_window_ > -1) { - // Pattern output - ImGui::SameLine(0, 50); + ImGuiContext& g = *GImGui; + + // title output + ImGui::SameLine(0, 5.f * g.Style.FramePadding.x); + ImGui::Text("Output %d", current_window_ + 1); + + // Output options + ImGui::SameLine(0, 2.f * g.Style.FramePadding.x); + ImGuiToolkit::ButtonIconToggle(8,5,9,5, &Settings::application.windows[1+current_window_].scaled, "Scaled to window"); + + ImGui::SameLine(0, g.Style.FramePadding.x); ImGuiToolkit::ButtonIconToggle(10,1,11,1, &Settings::application.windows[1+current_window_].show_pattern, "Test pattern"); -// // White ballance -// static DialogToolkit::ColorPickerDialog whitedialog; -// ImGui::SameLine(0, 30); -// ImGuiToolkit::Icon(5, 4); -// static ImVec4 white = ImVec4(1.f, 1.f, 1.f, 1.f); -// ImGui::SameLine(); -// ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT); -// if (ImGui::ColorButton("White", white, ImGuiColorEditFlags_NoAlpha)) { -// whitedialog.setRGB( std::make_tuple(white.x, white.y, white.z) ); -// whitedialog.open(); -// } -// ImGui::PopFont(); -// // get picked color if dialog finished -// if (whitedialog.closed()){ -// std::tuple c = whitedialog.RGB(); -// white.x = std::get<0>(c); -// white.y = std::get<1>(c); -// white.z = std::get<2>(c); -// } + // White ballance color + static DialogToolkit::ColorPickerDialog whitebalancedialog; -// ImGui::SameLine(); -// ImGuiToolkit::Icon(3,4); -// static ImVec4 grey = ImVec4(0.5f, 0.5f, 0.5f, 1.f); -// ImGui::SameLine(); -// ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT); -// ImGui::ColorButton("Grey", grey, ImGuiColorEditFlags_NoAlpha); -// ImGui::PopFont(); + ImGui::SameLine(0, 1.5f * g.Style.FramePadding.x); + ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT); -// ImGui::SameLine(); -// ImGuiToolkit::Icon(4,4); -// static ImVec4 black = ImVec4(0.f, 0.f, 0.f, 1.f); -// ImGui::SameLine(); -// ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT); -// ImGui::ColorButton("Black", black, ImGuiColorEditFlags_NoAlpha); -// ImGui::PopFont(); + // hack to re-align color button to text + ImGuiWindow* window = ImGui::GetCurrentWindow(); + window->DC.CursorPos.y += g.Style.FramePadding.y; + if (ImGui::ColorButton("White balance", ImVec4(current_window_whitebalance.x, + current_window_whitebalance.y, + current_window_whitebalance.z, 1.f), + ImGuiColorEditFlags_NoAlpha)) { + whitebalancedialog.setRGB( std::make_tuple(current_window_whitebalance.x, + current_window_whitebalance.y, + current_window_whitebalance.z) ); + whitebalancedialog.open(); + } + ImGui::PopFont(); + // get picked color if dialog finished + if (whitebalancedialog.closed()){ + std::tuple c = whitebalancedialog.RGB(); + current_window_whitebalance.x = std::get<0>(c); + current_window_whitebalance.y = std::get<1>(c); + current_window_whitebalance.z = std::get<2>(c); + + // set White Balance Color matrix to shader + Rendering::manager().outputWindow(current_window_).setWhiteBalance( current_window_whitebalance ); + } + + // White ballance temperature + ImGui::SameLine(); + window->DC.CursorPos.y -= g.Style.FramePadding.y; + if (ImGui::Button(ICON_FA_THERMOMETER_HALF ICON_FA_SORT_DOWN )) + ImGui::OpenPopup("temperature_popup"); + if (ImGui::BeginPopup("temperature_popup", ImGuiWindowFlags_NoMove)) + { + ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT); + ImGuiToolkit::Indication("9000 K", " " ICON_FA_THERMOMETER_FULL); + if ( ImGui::VSliderFloat("##Temperature", ImVec2(30,260), ¤t_window_whitebalance.w, 0.0, 1.0, "") ){ + + Rendering::manager().outputWindow(current_window_).setWhiteBalance( current_window_whitebalance ); + } + if (ImGui::IsItemHovered() || ImGui::IsItemActive() ) { + ImGui::BeginTooltip(); + ImGui::Text("%d K", 4000 + (int) ceil(current_window_whitebalance.w * 5000.f)); + ImGui::EndTooltip(); + } + ImGuiToolkit::Indication("4000 K", " " ICON_FA_THERMOMETER_EMPTY); + ImGui::PopFont(); + ImGui::EndPopup(); + } } @@ -482,7 +512,7 @@ void DisplaysView::draw() Rendering::manager().outputWindow(current_window_).setDecoration(!_borderless); } - if (ImGui::MenuItem( ICON_FA_EXPAND_ALT " Buffer aspect ratio" , nullptr, false, _windowed )){ + if (ImGui::MenuItem( ICON_FA_EXPAND_ALT " Reset aspect ratio" , nullptr, false, _windowed )){ // reset aspect ratio glm::ivec4 rect = windowCoordinates(current_window_); float ar = Mixer::manager().session()->frame()->aspectRatio(); @@ -493,7 +523,7 @@ void DisplaysView::draw() Rendering::manager().outputWindow(current_window_).setCoordinates( rect ); } - if (ImGui::MenuItem( ICON_FA_COMPRESS " Buffer size", nullptr, false, _windowed )){ + if (ImGui::MenuItem( ICON_FA_RULER_COMBINED " Reset to pixel size", nullptr, false, _windowed )){ // reset resolution to 1:1 glm::ivec4 rect = windowCoordinates(current_window_); rect.p = Mixer::manager().session()->frame()->width(); @@ -517,7 +547,17 @@ void DisplaysView::draw() } ImGui::Separator(); - ImGui::MenuItem( ICON_FA_EXPAND_ARROWS_ALT " Scaled content", nullptr, &Settings::application.windows[1].scaled ); + if ( ImGui::MenuItem( ICON_FA_REPLY " Reset") ) { + glm::ivec4 rect (0, 0, 800, 600); + rect.p = Mixer::manager().session()->frame()->width(); + rect.q = Mixer::manager().session()->frame()->height(); + Rendering::manager().outputWindow(current_window_).setDecoration(true); + Rendering::manager().outputWindow(current_window_).setWhiteBalance( glm::vec4(1.f, 1.f, 1.f, 0.5f) ); + if (Settings::application.windows[current_window_+1].fullscreen) + Rendering::manager().outputWindow(current_window_).exitFullscreen(); + else + Rendering::manager().outputWindow(current_window_).setCoordinates( rect ); + } ImGui::PopStyleColor(2); ImGui::EndPopup(); @@ -538,6 +578,8 @@ std::pair DisplaysView::pick(glm::vec2 P) // test all windows current_window_ = -1; + current_window_whitebalance = glm::vec4(1.f, 1.f, 1.f, 0.5f); + for (int i = 0; i < Settings::application.num_output_windows; ++i) { // ignore pick on render surface: it's the same as output surface @@ -555,6 +597,7 @@ std::pair DisplaysView::pick(glm::vec2 P) (pick.first == windows_[i].handles_) || (pick.first == windows_[i].menu_) ) { current_window_ = i; + current_window_whitebalance = Rendering::manager().outputWindow(current_window_).whiteBalance(); windows_[i].overlays_->setActive(1); } else diff --git a/src/DisplaysView.h b/src/DisplaysView.h index c34e214..a793fdc 100644 --- a/src/DisplaysView.h +++ b/src/DisplaysView.h @@ -80,6 +80,7 @@ private: std::vector windows_; int current_window_; Group *current_window_status_; + glm::vec4 current_window_whitebalance; bool show_window_menu_; // Group *window_; diff --git a/src/ImageFilter.cpp b/src/ImageFilter.cpp index f7551c0..093d1e5 100644 --- a/src/ImageFilter.cpp +++ b/src/ImageFilter.cpp @@ -19,18 +19,14 @@ #include #include -#include #include #include #include "defines.h" #include "Resource.h" -#include "ImageShader.h" #include "Visitor.h" #include "FrameBuffer.h" -#include "ImageShader.h" #include "Primitives.h" -#include "Log.h" #include "ImageFilter.h" @@ -162,38 +158,6 @@ bool FilteringProgram::operator!= (const FilteringProgram& other) const /// //// //////////////////////////////////////// -class ImageFilteringShader : public ImageShader -{ - // GLSL Program - ShadingProgram custom_shading_; - - // fragment shader GLSL code - std::string shader_code_; - std::string code_; - -public: - // for iTimedelta - GTimer *timer_; - double iTime_; - uint iFrame_; - - // list of uniforms to control shader - std::map< std::string, float > uniforms_; - - ImageFilteringShader(); - ~ImageFilteringShader(); - - void update(float dt); - - void use() override; - void reset() override; - void copy(ImageFilteringShader const& S); - - // set the code of the filter - void setCode(const std::string &code, std::promise *ret = nullptr); - -}; - ImageFilteringShader::ImageFilteringShader(): ImageShader() { diff --git a/src/ImageFilter.h b/src/ImageFilter.h index 7426fd4..ba34418 100644 --- a/src/ImageFilter.h +++ b/src/ImageFilter.h @@ -7,8 +7,10 @@ #include #include +#include #include +#include "ImageShader.h" #include "FrameBufferFilter.h" class FilteringProgram @@ -65,7 +67,39 @@ public: class Surface; class FrameBuffer; -class ImageFilteringShader; + +class ImageFilteringShader : public ImageShader +{ + // GLSL Program + ShadingProgram custom_shading_; + + // fragment shader GLSL code + std::string shader_code_; + std::string code_; + +public: + // for iTimedelta + GTimer *timer_; + double iTime_; + uint iFrame_; + + // list of uniforms to control shader + std::map< std::string, float > uniforms_; + + ImageFilteringShader(); + ~ImageFilteringShader(); + + void update(float dt); + + void use() override; + void reset() override; + void copy(ImageFilteringShader const& S); + + // set the code of the filter + void setCode(const std::string &code, std::promise *ret = nullptr); + +}; + class ImageFilter : public FrameBufferFilter { diff --git a/src/RenderingManager.cpp b/src/RenderingManager.cpp index fb8e873..18f8646 100644 --- a/src/RenderingManager.cpp +++ b/src/RenderingManager.cpp @@ -69,6 +69,7 @@ #include "GstToolkit.h" #include "UserInterfaceManager.h" #include "ControlManager.h" +#include "ImageFilter.h" #include "RenderingManager.h" @@ -1042,6 +1043,7 @@ void RenderingWindow::terminate() // invalidate window_ = NULL; + shader_ = nullptr; surface_ = nullptr; fbo_ = 0; index_ = -1; @@ -1088,6 +1090,33 @@ void RenderingWindow::swap() } +FilteringProgram whitebalance("Whitebalance", "shaders/filters/whitebalance.glsl", "", { { "Red", 1.0}, { "Green", 1.0}, { "Blue", 1.0}, { "Temperature", 0.5} }); + + +void RenderingWindow::setWhiteBalance(glm::vec4 colorcorrection) +{ + if (shader_) + shader_->uniforms_ = std::map< std::string, float >{ + { "Red", colorcorrection.x}, + { "Green", colorcorrection.y}, + { "Blue", colorcorrection.z}, + { "Temperature", colorcorrection.w} + }; + +} + +glm::vec4 RenderingWindow::whiteBalance() const +{ + glm::vec4 ret(1.f, 1.f, 1.f, 0.5f); + + ret.x = shader_->uniforms_["Red"]; + ret.y = shader_->uniforms_["Green"]; + ret.z = shader_->uniforms_["Blue"]; + ret.w = shader_->uniforms_["Temperature"]; + + return ret; +} + bool RenderingWindow::draw(FrameBuffer *fb) { // cannot draw if there is no window or invalid framebuffer @@ -1119,8 +1148,15 @@ bool RenderingWindow::draw(FrameBuffer *fb) // VAO is not shared between multiple contexts of different windows // so we have to create a new VAO for rendering the surface in this window - if (surface_ == nullptr) - surface_ = new WindowSurface; + if (surface_ == nullptr) { + + const std::pair codes = whitebalance.code(); + shader_ = new ImageFilteringShader; + shader_->setCode( codes.first ); + shader_->uniforms_ = whitebalance.parameters(); + + surface_ = new WindowSurface(shader_); + } // calculate scaling factor of frame buffer inside window const float windowAspectRatio = aspectRatio(); diff --git a/src/RenderingManager.h b/src/RenderingManager.h index f39acdc..19bf015 100644 --- a/src/RenderingManager.h +++ b/src/RenderingManager.h @@ -40,6 +40,7 @@ class RenderingWindow uint fbo_; Stream *pattern_; class WindowSurface *surface_; + class ImageFilteringShader *shader_; protected: void setTitle(const std::string &title = ""); @@ -82,6 +83,10 @@ public: void setCoordinates(glm::ivec4 rect); void setDecoration (bool on); + // set color correction + void setWhiteBalance(glm::vec4 colorcorrection); + glm::vec4 whiteBalance() const; + // get width of rendering area int width(); // get height of rendering area diff --git a/src/TextureView.cpp b/src/TextureView.cpp index ed30a3e..d328efd 100644 --- a/src/TextureView.cpp +++ b/src/TextureView.cpp @@ -916,7 +916,7 @@ void TextureView::draw() s->touch(); Action::manager().store(s->name() + std::string(": Texture Reset rotation")); } - if (ImGui::Selectable( ICON_FA_EXPAND_ALT " Reset aspect ratio" )){ + if (ImGui::Selectable( ICON_FA_EXPAND_ALT " Reset aspect ratio" )){ s->group(mode_)->scale_.x = s->group(mode_)->scale_.y; s->group(mode_)->scale_.x *= s->group(mode_)->crop_.x / s->group(mode_)->crop_.y; s->touch();