I renamed it in the FuRegisterClass to match the file name: "Vibrance".
Added in 4 minutes 3 seconds:
In this version I added Clamping to some of the Vibrance methods for testing.
I may be a good idea in the future to add "Clamp" as an option.
For not I'm just using it for testing some ideas while keeping the output values between 0 and 1.
- FuRegisterClass("Vibrance", CT_Tool, {
- REGS_Category = "Fuses\\Color",
- REGS_OpIconString = "TNT",
- REGS_OpDescription = "Vibrance",
- REG_Version = 1.0,
- REGS_Company = "Learn Now FX",
- REGS_URL = "http://www.youtube.com/LearnNowFX",
- })
- VibranceParams = [[
- float red;
- float green;
- float blue;
- float method;
- float vibrance;
- float saturation;
- float gain;
- float gamma;
- int unmult;
- int invert;
- float again;
- int clip;
- int srcCompOrder;
- ]]
- VibranceKernel = [[
- __KERNEL__ void VibranceKernel(__CONSTANTREF__ VibranceParams *params, __TEXTURE2D__ src, __TEXTURE2D_WRITE__ dst) {
- DEFINE_KERNEL_ITERATORS_XY(x, y);
- float4 col = _tex2DVecN(src, x, y, params->srcCompOrder);
- //Clamp
- // col.x = _fmaxf(0, _fminf(1, col.x));
- // col.y = _fmaxf(0, _fminf(1, col.y));
- // col.z = _fmaxf(0, _fminf(1, col.z));
- // The Invert Control math goes here:
- if (params->invert == 1) {
- col.x = 1 - col.x;
- col.y = 1 - col.y;
- col.z = 1 - col.z;
- }
- // The Color Picker math goes here:
- if (params->invert == 0) {
- col.x *= params->red;
- col.y *= params->green;
- col.z *= params->blue;
- }
- if (params->invert == 1) {
- float nRed = params->red + 0.0001f;
- float nGreen = params->green + 0.0001f;
- float nBlue = params->blue + 0.0001f;
- col.x = powr(col.x, 1/nRed);
- col.y = powr(col.y, 1/nGreen);
- col.z = powr(col.z, 1/nBlue);
- }
- // The Gain control math goes here:
- col.x *= params->gain;
- col.y *= params->gain;
- col.z *= params->gain;
- // The Gamma control math goes here:
- col.x = powr(col.x, 1/_fmaxf(params->gamma, 0.01f));
- col.y = powr(col.y, 1/_fmaxf(params->gamma, 0.01f));
- col.z = powr(col.z, 1/_fmaxf(params->gamma, 0.01f));
- // The UnMultiply math goes here:
- float luma = col.x * 0.299f + col.y * 0.587f + col.z * 0.114f;
- if (params->unmult == 1) {
- col.w *= (luma + params->again);
- }
- //The Vibrance math goes here:
- float cMax = _fmaxf(col.x, _fmaxf(col.y, col.z)); //find the strongest color
- float cMin = _fminf(col.x, _fminf(col.y, col.z)); //find the weakest color
- float cSat = cMax - cMin; //The diff of min and max color is the Saturation
- float cAverage = (col.x + col.y + col.z) / 3.0f;
- float vibrance = params->vibrance;
- float scale = 1.0;
- //custom version - special treatemnt for reds
- if (params->method == 0) {
- scale = vibrance * (2 * (1 - cSat)); //Less Saturated given higher priority
- if (cMax == col.x) { //special treatment when red is max
- scale = vibrance * (abs(col.y - col.z) / (cSat / cAverage)) * (1 - cSat);
- }
- //Clamp
- scale = _fminf(1, scale);
- //Lerp
- col.x = (col.x - cMax) * scale + col.x;
- col.y = (col.y - cMax) * scale + col.y;
- col.z = (col.z - cMax) * scale + col.z;
- }
- //Simple
- if (params->method == 1) {
- scale = vibrance * ( 0.25 * (1 - cAverage));
- //Clamp
- // scale = _fminf(1, scale);
- //Lerp
- col.x = (col.x - cMin) * scale + col.x;
- col.y = (col.y - cMin) * scale + col.y;
- col.z = (col.z - cMin) * scale + col.z;
- }
- //complex
- if (params->method == 2) {
- scale = vibrance * (1.0 - (sign(vibrance) * cAverage));
- //Clamp
- // scale = _fminf(1, scale);
- //Lerp
- col.x = (col.x - cAverage) * scale + col.x;
- col.y = (col.y - cAverage) * scale + col.y;
- col.z = (col.z - cAverage) * scale + col.z;
- }
- //pow version
- if (params->method == 3) {
- scale = 1.0 - powr(cSat, 1.0 - (1.0 - vibrance));
- //Clamp
- scale = _fminf(1, scale);
- //Lerp
- col.x = (col.x - cSat) * scale + col.x;
- col.y = (col.y - cSat) * scale + col.y;
- col.z = (col.z - cSat) * scale + col.z;
- //Clamp
- col.x = _fmaxf(0, _fminf(1, col.x));
- col.y = _fmaxf(0, _fminf(1, col.y));
- col.z = _fmaxf(0, _fminf(1, col.z));
- }
- // The Saturation math goes here:
- col.x = params->saturation * col.x + (1-params->saturation) * luma;
- col.y = params->saturation * col.y + (1-params->saturation) * luma;
- col.z = params->saturation * col.z + (1-params->saturation) * luma;
- //the Clip negative Pixels math goes here:
- if (params->clip == 1) {
- if (col.x < 0) { col.x = 0; }
- if (col.y < 0) { col.y = 0; }
- if (col.z < 0) { col.z = 0; }
- }
- _tex2DVec4Write(dst, x, y, col);
- }
- ]]
- OpenDemoURL = [[
- -- Open a webpage window up using your default web browser
- platform = (FuPLATFORM_WINDOWS and "Windows") or (FuPLATFORM_MAC and "Mac") or (FuPLATFORM_LINUX and "Linux")
- function OpenURL(siteName, path)
- if platform == "Windows" then
- -- Running on Windows
- command = "explorer \"" .. path .. "\""
- elseif platform == "Mac" then
- -- Running on Mac
- command = "open \"" .. path .. "\" &"
- elseif platform == "Linux" then
- -- Running on Linux
- command = "xdg-open \"" .. path .. "\" &"
- else
- print("[Error] There is an invalid Fusion platform detected")
- return
- end
- os.execute(command)
- -- print("[Launch Command] ", command)
- print("[Opening URL] [" .. siteName .. "] " .. path)
- end
- OpenURL("Demo", "https://youtu.be/2XF8BsJ1J5o")
- ]]
- function Create()
- self:BeginControlNest("Color", "Color", true, {})
- InColorR= self:AddInput("Red", "Red", {
- ICS_Name = "Color",
- LINKID_DataType = "Number",
- INPID_InputControl = "ColorControl",
- INP_Default = 1,
- INP_MaxScale = 1.0,
- ICD_Center = 1.0,
- INP_DoNotifyChanged = true,
- CLRC_ShowWheel = False,
- IC_ControlGroup = 1,
- IC_ControlID = 0,
- IC_Visible = true,
- })
- InColorG = self:AddInput("Green", "Green", {
- LINKID_DataType = "Number",
- INPID_InputControl = "ColorControl",
- INP_Default = 1,
- INP_DoNotifyChanged = true,
- IC_ControlGroup = 1,
- IC_ControlID = 1,
- })
- InColorB = self:AddInput("Blue", "Blue", {
- LINKID_DataType = "Number",
- INPID_InputControl = "ColorControl",
- INP_Default = 1,
- INP_DoNotifyChanged = true,
- IC_ControlGroup = 1,
- IC_ControlID = 2,
- })
- InInvert = self:AddInput("Invert", "Invert", {
- LINKID_DataType = "Number",
- INPID_InputControl = "CheckboxControl",
- INP_Integer = true,
- ICD_Width = 0.5,
- INP_Default = 0,
- })
- self:EndControlNest()
- self:BeginControlNest("Intensity", "Intensity", true, {})
- InVibranceMethod = self:AddInput("Vibrance Method", "VibranceMethod", {
- LINKID_DataType = "Number",
- INPID_InputControl = "ComboControl",
- INP_MinScale = 0,
- INP_MaxAllowed = 3,
- INP_MinAllowed = 0,
- INP_MaxAllowed = 3,
- INP_Integer = true,
- ICD_Width = 1.0,
- CC_LabelPosition = "Horizontal",
- INP_DoNotifyChanged = true,
- { CCS_AddString = "Vibrant", },
- { CCS_AddString = "Simple", },
- { CCS_AddString = "Complex", },
- { CCS_AddString = "Power", },
- })
- InVibrance = self:AddInput("Vibrance", "Vibrance", {
- LINKID_DataType = "Number",
- INPID_InputControl = "SliderControl",
- INP_Default = 0,
- INP_MinScale = 0,
- INP_MaxScale = 3,
- INP_Default = 1,
- })
- InSaturation = self:AddInput("Saturation", "Saturation", {
- LINKID_DataType = "Number",
- INPID_InputControl = "SliderControl",
- INP_MinScale = 0,
- INP_MaxScale = 2,
- INP_Default = 1,
- })
- InGain = self:AddInput("Gain", "Gain", {
- LINKID_DataType = "Number",
- INPID_InputControl = "SliderControl",
- INP_MinScale = 0,
- INP_MaxScale = 5,
- INP_Default = 1,
- })
- InGamma = self:AddInput("Gamma", "Gamma", {
- LINKID_DataType = "Number",
- INPID_InputControl = "SliderControl",
- INP_MinScale = 0,
- INP_MaxScale = 2,
- INP_Default = 1,
- })
- self:EndControlNest()
- self:BeginControlNest("Alpha", "Alpha", true, {})
- InUnMultiply = self:AddInput("UnMultiply", "UnMultiply", {
- LINKID_DataType = "Number",
- INPID_InputControl = "CheckboxControl",
- INP_Integer = true,
- INP_Default = 0,
- })
- InAlphaGain = self:AddInput("Alpha Gain", "Alpha Gain", {
- LINKID_DataType = "Number",
- INPID_InputControl = "SliderControl",
- INP_MinScale = 0,
- INP_MaxScale = 1,
- INP_Default = 0,
- })
- self:EndControlNest()
- self:BeginControlNest("Advanced", "Advanced", false, {})
- InDepth = self:AddInput("Depth", "Depth", {
- LINKID_DataType = "Number",
- INPID_InputControl = "ComboControl",
- INP_Default = 2.0,
- INP_Integer = true,
- ICD_Width = 1.0,
- CC_LabelPosition = "Vertical",
- INP_DoNotifyChanged = true,
- { CCS_AddString = "int8", },
- { CCS_AddString = "int16", },
- { CCS_AddString = "float 16", },
- { CCS_AddString = "float 32", },
- })
- InClip = self:AddInput("Clip Negative Pixels", "Clip Negative Pixels", {
- LINKID_DataType = "Number",
- INPID_InputControl = "CheckboxControl",
- INP_Integer = true,
- ICD_Width = 1.0,
- INP_Default = 1,
- })
- DemoButton = self:AddInput("Demo", "Demo", {
- LINKS_Name = "Demo",
- LINKID_DataType = "Number",
- INPID_InputControl = "ButtonControl",
- BTNCS_Execute = OpenDemoURL,
- })
- self:EndControlNest()
- InImage = self:AddInput("Image", "Image", {
- LINKID_DataType = "Image",
- LINK_Main = 1,
- INP_AcceptsGPUImages = true,
- })
- OutImage = self:AddOutput("Output", "Output", {
- LINKID_DataType = "Image",
- LINK_Main = 1,
- })
- end
- function Process(req)
- local src = InImage:GetValue(req)
- local depth = InDepth:GetValue(req).Value
- local dst = Image{ IMG_Like = src, IMG_Depth = depth + 1, IMG_DeferAlloc = true }
- local node = DVIPComputeNode(req, "VibranceKernel", VibranceKernel, "VibranceParams", VibranceParams)
- local params = node:GetParamBlock(VibranceParams)
- params.red = InColorR:GetValue(req).Value
- params.green = InColorG:GetValue(req).Value
- params.blue = InColorB:GetValue(req).Value
- params.method = InVibranceMethod:GetValue(req).Value
- params.vibrance = InVibrance:GetValue(req).Value
- params.saturation = InSaturation:GetValue(req).Value
- params.gain = InGain:GetValue(req).Value
- params.gamma = InGamma:GetValue(req).Value
- params.unmult = InUnMultiply:GetValue(req).Value
- params.again = InAlphaGain:GetValue(req).Value
- params.invert = InInvert:GetValue(req).Value
- params.clip = InClip:GetValue(req).Value
- params.srcCompOrder = src:IsMask() and 1 or 15
- node:SetParamBlock(params)
- node:AddInput("src", src)
- node:AddOutput("dst", dst)
- local ok = node:RunSession(req)
- if not ok then
- dst = nil
- end
- OutImage:Set(req, dst)
- end