diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index a27117d..9bbe26f 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -3,9 +3,18 @@ AdvancedMasks.Effect="Mask Effect" AdvancedMasks.Effects.Alpha="Alpha Mask" AdvancedMasks.Effects.Adjustment="Adjustment Mask" AdvancedMasks.Adjustments.Label="Adjustments" -AdvancedMasks.Adjustments.Brightness="Brightness" -AdvancedMasks.Adjustments.MinBrightness="Minimum Brightness" -AdvancedMasks.Adjustments.MaxBrightness="Maximum Brightness" +AdvancedMasks.Adjustments.Brightness="Adjust Brightness?" +AdvancedMasks.Adjustments.MinBrightness="Brightness @ min" +AdvancedMasks.Adjustments.MaxBrightness="Brightness @ max" +AdvancedMasks.Adjustments.Contrast="Adjust Contrast?" +AdvancedMasks.Adjustments.MinContrast="Contrast @ min" +AdvancedMasks.Adjustments.MaxContrast="Contrast @ max" +AdvancedMasks.Adjustments.Saturation="Adjust Saturation?" +AdvancedMasks.Adjustments.MinSaturation="Saturation @ min" +AdvancedMasks.Adjustments.MaxSaturation="Saturation @ max" +AdvancedMasks.Adjustments.HueShift="Adjust Hue Shift?" +AdvancedMasks.Adjustments.MinHueShift="Hue Shift @ min" +AdvancedMasks.Adjustments.MaxHueShift="Hue Shift @ max" AdvancedMasks.Type="Mask Type" AdvancedMasks.Shape="Shape" AdvancedMasks.Source="Source" @@ -16,6 +25,7 @@ AdvancedMasks.GradientMask.Width="Width" AdvancedMasks.GradientMask.Position="Position" AdvancedMasks.GradientMask.Rotation="Rotation" AdvancedMasks.GradientMask.DebugLines="Debug Lines" +AdvancedMasks.GradientMask.Invert="Invert?" AdvancedMasks.Shape.Rectangle="Rectangle" AdvancedMasks.Shape.Circle="Circle" AdvancedMasks.Shape.Ellipse="Ellipse" @@ -26,6 +36,7 @@ AdvancedMasks.Shape.ScalePosition="Scale/Position Properties" AdvancedMasks.Shape.Rectangle.Width="Width" AdvancedMasks.Shape.Rectangle.Height="Height" AdvancedMasks.Shape.Rectangle.SourceGroup="Source Mask Properties" +AdvancedMasks.Shape.Rectangle.GeometryGroup="Adjustment Mask Geometry" AdvancedMasks.Shape.Rectangle.CornerRadius="Corner Radius" AdvancedMasks.Shape.Rectangle.CornerRadius.CustomGroup="Corner Radius Properties" AdvancedMasks.Shape.Rectangle.CornerRadius.TopLeft="Top Left" diff --git a/data/shaders/common.effect b/data/shaders/common.effect index 6e5bec1..bd02398 100644 --- a/data/shaders/common.effect +++ b/data/shaders/common.effect @@ -1,3 +1,10 @@ + +#define PI180 0.0174533f +#define SQRT3 0.5773503f +#define LUM_R 0.299f +#define LUM_G 0.587f +#define LUM_B 0.114f + // Convert pre-multiplied rgba values // to linear rgba. float4 pmrgba_to_rgba(float4 color) @@ -9,3 +16,37 @@ float4 adjust_brightness(float4 color, float brightness) { return saturate(float4(color.rgb + brightness, color.a)); } + +float4 adjustments(float4 color, float b, float c, float s, float hue_shift) { + // Calculate Contrast Value + float ca = c < 0.0f ? 1.0f / (-c + 1.0f) : (c + 1.0f); + + // Calculate Saturation Values + float s_r = (1.0f - s) * LUM_R; + float s_g = (1.0f - s) * LUM_G; + float s_b = (1.0f - s) * LUM_B; + + float3 col = float3( + ca * (s_r + s) * color.r + ca * s_g * color.g + ca * s_b * color.b + b, + ca * s_r * color.r + ca * (s_g + s) * color.g + ca * s_b * color.b + b, + ca * s_r * color.r + ca * s_g * color.g + ca * (s_b + s) * color.b + b + ); + + // Calculate Hue shift values + float half_angle = 0.5f * hue_shift * PI180; + float rq = SQRT3 * sin(half_angle); + float cross = rq * rq; + float sq = 2.0f * cross; + float d = 2.0f * (0.5f - sq); + float w_imag = rq * cos(half_angle); + float a_l = 2.0f * (cross + w_imag); + float b_l = 2.0f * (cross - w_imag); + + + return saturate(float4( + col.r * d + col.g * a_l + col.b * b_l, + col.r * b_l + col.g * d + col.b * a_l, + col.r * a_l + col.g * b_l + col.b * d, + color.a + )); +} diff --git a/data/shaders/gradient-mask.effect b/data/shaders/gradient-mask.effect index 5b9c6fd..618ec47 100644 --- a/data/shaders/gradient-mask.effect +++ b/data/shaders/gradient-mask.effect @@ -7,9 +7,15 @@ uniform float2 uv_size; // source dimensions in px uniform float width; // width in pixels uniform float position; // Pixel position uniform float rotation; // rotation in radians -uniform bool adj_brightness; +uniform bool invert; uniform float min_brightness; uniform float max_brightness; +uniform float min_contrast; +uniform float max_contrast; +uniform float min_saturation; +uniform float max_saturation; +uniform float min_hue_shift; +uniform float max_hue_shift; sampler_state textureSampler{ Filter = Linear; @@ -46,7 +52,7 @@ float4 mainAlphaImage(VertData v_in) : TARGET float alpha = saturate((coord_p.x - position + width / 2.0f) / width); float4 col = pmrgba_to_rgba(image.Sample(textureSampler, v_in.uv)); - return float4(col.rgb, col.a * alpha); + return float4(col.rgb, col.a * (invert ? 1.0f - alpha : alpha)); } float4 mainAdjustmentsImage(VertData v_in) : TARGET @@ -63,13 +69,17 @@ float4 mainAdjustmentsImage(VertData v_in) : TARGET ); float scale = saturate((coord_p.x - position + width / 2.0f) / width); - float4 col = pmrgba_to_rgba(image.Sample(textureSampler, v_in.uv)); - if (adj_brightness) - { - col = adjust_brightness(col, lerp(min_brightness, max_brightness, scale)); - } + scale = invert ? 1.0f - scale : scale; + float4 color = pmrgba_to_rgba(image.Sample(textureSampler, v_in.uv)); - return float4(col.rgb, col.a); + color = adjustments( + color, + lerp(min_brightness, max_brightness, scale), + lerp(min_contrast, max_contrast, scale), + lerp(min_saturation, max_saturation, scale), + lerp(min_hue_shift, max_hue_shift, scale) + ); + return color; } float4 debugAlphaImage(VertData v_in) : TARGET @@ -88,24 +98,64 @@ float4 debugAlphaImage(VertData v_in) : TARGET float alpha = saturate((coord_p.x - position + width / 2.0f) / width); - float4 col = pmrgba_to_rgba(image.Sample(textureSampler, v_in.uv)); - if (coord_p.x - position > -(width / 2.0 + 2.0) && coord_p.x - position < -(width / 2.0)) + float4 color = pmrgba_to_rgba(image.Sample(textureSampler, v_in.uv)); + if (coord_p.x - position > -(width / 2.0f + 2.0f) && coord_p.x - position < -(width / 2.0f)) { - return float4(0.0, 1.0, 0.0, 1.0); + return float4(0.0f, 1.0f, 0.0f, 1.0f); } - if (coord_p.x - position < (width / 2.0 + 2.0) && coord_p.x - position > width / 2.0) + if (coord_p.x - position < (width / 2.0f + 2.0f) && coord_p.x - position > width / 2.0f) { - return float4(0.0, 1.0, 0.0, 1.0); + return float4(0.0f, 1.0f, 0.0f, 1.0f); } - if (coord_p.x - position > -1.0 && coord_p.x - position < 1.0) + if (coord_p.x - position > -1.0f && coord_p.x - position < 1.0f) { - return float4(1.0, 0.0, 0.0, 1.0); + return float4(1.0f, 0.0f, 0.0f, 1.0f); } - return float4(col.rgb, col.a * alpha); + + return float4(color.rgb, color.a * (invert ? 1.0f - alpha : alpha)); } -technique DrawAlpha +float4 debugAdjustmentsImage(VertData v_in) : TARGET +{ + float2 coord = v_in.uv * uv_size; + float h = uv_size.x / 2.0f; + float k = uv_size.y / 2.0f; + float sina = sin(rotation); + float cosa = cos(rotation); + + float2 coord_p = float2( + cosa * coord.x + sina * coord.y - h * cosa - k * sina, + -sina * coord.x + cosa * coord.y + h * sina - k * cosa + ); + + float scale = saturate((coord_p.x - position + width / 2.0f) / width); + scale = invert ? 1.0f - scale : scale; + float4 color = pmrgba_to_rgba(image.Sample(textureSampler, v_in.uv)); + if (coord_p.x - position > -(width / 2.0f + 2.0f) && coord_p.x - position < -(width / 2.0f)) + { + return float4(0.0f, 1.0f, 0.0f, 1.0f); + } + if (coord_p.x - position < (width / 2.0f + 2.0f) && coord_p.x - position > width / 2.0f) + { + return float4(0.0f, 1.0f, 0.0f, 1.0f); + } + if (coord_p.x - position > -1.0f && coord_p.x - position < 1.0f) + { + return float4(1.0f, 0.0f, 0.0f, 1.0f); + } + + color = adjustments( + color, + lerp(min_brightness, max_brightness, scale), + lerp(min_contrast, max_contrast, scale), + lerp(min_saturation, max_saturation, scale), + lerp(min_hue_shift, max_hue_shift, scale) + ); + return color; +} + +technique Alpha { pass { @@ -114,7 +164,7 @@ technique DrawAlpha } } -technique DrawAdjustments +technique Adjustments { pass { @@ -132,3 +182,13 @@ technique DebugAlpha } } + +technique DebugAdjustments +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = debugAdjustmentsImage(v_in); + + } +} diff --git a/data/shaders/rectangular-mask.effect b/data/shaders/rectangular-mask.effect index 361b138..4e22092 100644 --- a/data/shaders/rectangular-mask.effect +++ b/data/shaders/rectangular-mask.effect @@ -3,6 +3,7 @@ uniform float4x4 ViewProj; uniform texture2d image; +uniform float2 uv_size; uniform float2 mask_position; uniform float width; @@ -14,6 +15,16 @@ uniform float2 global_position; uniform float global_scale; uniform float max_corner_radius; uniform float4 corner_radius; +uniform bool invert; + +uniform float min_brightness; +uniform float max_brightness; +uniform float min_contrast; +uniform float max_contrast; +uniform float min_saturation; +uniform float max_saturation; +uniform float min_hue_shift; +uniform float max_hue_shift; uniform float aa_scale; @@ -37,55 +48,122 @@ float circleAlpha(float2 uv, float r) { return smoothstep(r + wd, r - wd, d); } -float4 applyRoundedCorners(float2 uv, float2 min_vals, float2 max_vals) +float4 applyRoundedCorners(float2 coord, float2 min_vals, float2 max_vals) { //float2 scaled_uv = uv * aspect_ratio; - float2 min_vals_max = min_vals + max_corner_radius / aspect_ratio; - float2 max_vals_max = max_vals - max_corner_radius / aspect_ratio; - bool safe_zone = !(uv.x < min_vals_max.x || uv.y < min_vals_max.y || uv.x > max_vals_max.x || uv.y > max_vals_max.y); - float4 color = pmrgba_to_rgba(image.Sample(textureSampler, uv)); + float2 min_vals_max = min_vals + max_corner_radius; + float2 max_vals_max = max_vals - max_corner_radius; + bool safe_zone = !(coord.x < min_vals_max.x || coord.y < min_vals_max.y || coord.x > max_vals_max.x || coord.y > max_vals_max.y); + float4 color = pmrgba_to_rgba(image.Sample(textureSampler, coord / uv_size)); float alpha = color.a; - float2 local = float2(0.0f, 0.0f); + if (safe_zone) { return color; } - float2 tl = min_vals + corner_radius[0] / aspect_ratio; - float2 tr = float2(max_vals.x, min_vals.y) + float2(-corner_radius[1], corner_radius[1]) / aspect_ratio; - float2 bl = float2(min_vals.x, max_vals.y) + float2(corner_radius[2], -corner_radius[2]) / aspect_ratio; - float2 br = max_vals - corner_radius[3] / aspect_ratio; + float2 local = float2(0.0f, 0.0f); + float2 tl = min_vals + corner_radius[0]; + float2 tr = float2(max_vals.x, min_vals.y) + float2(-corner_radius[1], corner_radius[1]); + float2 bl = float2(min_vals.x, max_vals.y) + float2(corner_radius[2], -corner_radius[2]); + float2 br = max_vals - corner_radius[3]; - if (uv.x < tl.x && uv.y < tl.y) { - local = (uv - tl) * aspect_ratio; + if (coord.x < tl.x && coord.y < tl.y) { + local = (coord - tl); alpha *= circleAlpha(local, corner_radius[0]); - } else if (uv.x > tr.x && uv.y < tr.y) { - local = (uv - tr) * aspect_ratio; + } else if (coord.x > tr.x && coord.y < tr.y) { + local = (coord - tr); alpha *= circleAlpha(local, corner_radius[1]); - } else if (uv.x < bl.x && uv.y > bl.y) { - local = (uv - bl) * aspect_ratio; + } else if (coord.x < bl.x && coord.y > bl.y) { + local = (coord - bl); alpha *= circleAlpha(local, corner_radius[2]); - } else if (uv.x > br.x && uv.y > br.y) { - local = (uv - br) * aspect_ratio; + } else if (coord.x > br.x && coord.y > br.y) { + local = (coord - br); alpha *= circleAlpha(local, corner_radius[3]); } return float4(color.rgb, alpha); } -float4 applyMask(float2 uv) +float applyAdjustmentCorners(float2 coord, float2 min_vals, float2 max_vals) +{ + //float2 scaled_uv = uv * aspect_ratio; + float2 min_vals_max = min_vals + max_corner_radius; + float2 max_vals_max = max_vals - max_corner_radius; + bool safe_zone = !(coord.x < min_vals_max.x || coord.y < min_vals_max.y || coord.x > max_vals_max.x || coord.y > max_vals_max.y); + if (safe_zone) + { + return 1.0; + } + float alpha = 1.0; + float2 local = float2(0.0f, 0.0f); + float2 tl = min_vals + corner_radius[0]; + float2 tr = float2(max_vals.x, min_vals.y) + float2(-corner_radius[1], corner_radius[1]); + float2 bl = float2(min_vals.x, max_vals.y) + float2(corner_radius[2], -corner_radius[2]); + float2 br = max_vals - corner_radius[3]; + + if (coord.x < tl.x && coord.y < tl.y) + { + local = (coord - tl); + alpha *= circleAlpha(local, corner_radius[0]); + } + else if (coord.x > tr.x && coord.y < tr.y) + { + local = (coord - tr); + alpha *= circleAlpha(local, corner_radius[1]); + } + else if (coord.x < bl.x && coord.y > bl.y) + { + local = (coord - bl); + alpha *= circleAlpha(local, corner_radius[2]); + } + else if (coord.x > br.x && coord.y > br.y) + { + local = (coord - br); + alpha *= circleAlpha(local, corner_radius[3]); + } + + return alpha; +} + +float4 applyMask(float2 coord) { - float2 dist = uv - mask_position; - uv = mask_position + (dist / global_scale / zoom); - float2 position_uv = mask_position; - float2 crop_uv = float2(width, height) / 2.0 / zoom; + float2 dist = coord - mask_position; + coord = mask_position + (dist / global_scale / zoom); + float2 crop = float2(width, height) / 2.0 / zoom; - float2 min_vals = (position_uv - crop_uv); - float2 max_vals = (position_uv + crop_uv); + float2 min_vals = (mask_position - crop); + float2 max_vals = (mask_position + crop); // Must expand out for OpenGL compatibility, rather than using the // much more convenient `any` - bool dont_draw = uv.x < min_vals.x || uv.y < min_vals.y || uv.x > max_vals.x || uv.y > max_vals.y; + bool dont_draw = coord.x < min_vals.x || coord.y < min_vals.y || coord.x > max_vals.x || coord.y > max_vals.y; - return dont_draw ? float4(0.0, 0.0, 0.0, 0.0) : applyRoundedCorners(uv, min_vals, max_vals); + return dont_draw ? float4(0.0, 0.0, 0.0, 0.0) : applyRoundedCorners(coord, min_vals, max_vals); +} + +float4 applyAdjustments(float2 coord) +{ + float2 crop = float2(width, height) / 2.0 / zoom; + float2 min_vals = (mask_position - crop); + float2 max_vals = (mask_position + crop); + + bool outside = coord.x < min_vals.x || coord.y < min_vals.y || coord.x > max_vals.x || coord.y > max_vals.y; + float scale = outside ? 0.0 : applyAdjustmentCorners(coord, min_vals, max_vals); + float4 color = pmrgba_to_rgba(image.Sample(textureSampler, coord / uv_size)); + float4 color_adj_min = adjustments( + color, + min_brightness, + min_contrast, + min_saturation, + min_hue_shift + ); + float4 color_adj_max = adjustments( + color, + max_brightness, + max_contrast, + max_saturation, + max_hue_shift + ); + return (color_adj_min * (1.0 - scale) + color_adj_max * scale); } VertData mainTransform(VertData v_in) @@ -94,34 +172,34 @@ VertData mainTransform(VertData v_in) return v_in; } -float4 sharpCornersImage(VertData v_in) : TARGET +float4 alphaImage(VertData v_in) : TARGET { + float2 coord = v_in.uv * uv_size; float2 shift = global_position - mask_position; - float2 uv = v_in.uv - shift; - return applyMask(uv); + float2 coord_p = coord - shift; + return applyMask(coord_p); } -float4 roundedCornersImage(VertData v_in) : TARGET +float4 adjustmentsImage(VertData v_in) : TARGET { - // 1. Sample incoming pixel - float4 c1 = pmrgba_to_rgba(image.Sample(textureSampler, v_in.uv)); - return c1; + float2 coord = v_in.uv * uv_size; + return applyAdjustments(coord); } -technique SharpCorners +technique Alpha { pass { vertex_shader = mainTransform(v_in); - pixel_shader = sharpCornersImage(v_in); + pixel_shader = alphaImage(v_in); } } -technique RoundedCorners +technique Adjustments { pass { vertex_shader = mainTransform(v_in); - pixel_shader = roundedCornersImage(v_in); + pixel_shader = adjustmentsImage(v_in); } } diff --git a/data/shaders/source-mask.effect b/data/shaders/source-mask.effect index d433180..588e1fa 100644 --- a/data/shaders/source-mask.effect +++ b/data/shaders/source-mask.effect @@ -11,9 +11,14 @@ uniform float threshold_value; uniform float range_min; uniform float range_max; -uniform bool adj_brightness; uniform float min_brightness; uniform float max_brightness; +uniform float min_contrast; +uniform float max_contrast; +uniform float min_saturation; +uniform float max_saturation; +uniform float min_hue_shift; +uniform float max_hue_shift; sampler_state textureSampler{ Filter = Linear; @@ -36,7 +41,7 @@ VertData mainTransform(VertData v_in) return v_in; } -float4 mainImage(VertData v_in) : TARGET +float4 alphaImage(VertData v_in) : TARGET { float4 mask = pmrgba_to_rgba(source_image.Sample(textureSampler, v_in.uv)); float alpha = multiplier * (mask.r * channel_multipliers.r + mask.g * channel_multipliers.g + mask.b * channel_multipliers.b + mask.a * channel_multipliers.a); @@ -44,21 +49,23 @@ float4 mainImage(VertData v_in) : TARGET return float4(color.rgb, color.a * (invert ? 1.0 - alpha : alpha)); } -float4 mainAdjustmentsImage(VertData v_in) : TARGET +float4 adjustmentsImage(VertData v_in) : TARGET { float4 mask = pmrgba_to_rgba(source_image.Sample(textureSampler, v_in.uv)); float scale = multiplier * (mask.r * channel_multipliers.r + mask.g * channel_multipliers.g + mask.b * channel_multipliers.b + mask.a * channel_multipliers.a); - float4 col = pmrgba_to_rgba(image.Sample(textureSampler, v_in.uv)); - - if (adj_brightness) - { - col = adjust_brightness(col, lerp(min_brightness, max_brightness, scale)); - } - - return float4(col.rgb, col.a); + scale = invert ? 1.0 - scale : scale; + float4 color = pmrgba_to_rgba(image.Sample(textureSampler, v_in.uv)); + color = adjustments( + color, + lerp(min_brightness, max_brightness, scale), + lerp(min_contrast, max_contrast, scale), + lerp(min_saturation, max_saturation, scale), + lerp(min_hue_shift, max_hue_shift, scale) + ); + return color; } -float4 thresholdImage(VertData v_in) : TARGET +float4 alphaThresholdImage(VertData v_in) : TARGET { float4 mask = pmrgba_to_rgba(source_image.Sample(textureSampler, v_in.uv)); float alpha = multiplier * (mask.r * channel_multipliers.r + mask.g * channel_multipliers.g + mask.b * channel_multipliers.b + mask.a * channel_multipliers.a); @@ -67,7 +74,24 @@ float4 thresholdImage(VertData v_in) : TARGET return float4(color.rgb, color.a * (invert ? 1.0 - alpha : alpha)); } -float4 rangeImage(VertData v_in) : TARGET +float4 adjustmentsThresholdImage(VertData v_in) : TARGET +{ + float4 mask = pmrgba_to_rgba(source_image.Sample(textureSampler, v_in.uv)); + float scale = multiplier * (mask.r * channel_multipliers.r + mask.g * channel_multipliers.g + mask.b * channel_multipliers.b + mask.a * channel_multipliers.a); + scale = step(threshold_value, scale); + scale = invert ? 1.0 - scale : scale; + float4 color = pmrgba_to_rgba(image.Sample(textureSampler, v_in.uv)); + color = adjustments( + color, + lerp(min_brightness, max_brightness, scale), + lerp(min_contrast, max_contrast, scale), + lerp(min_saturation, max_saturation, scale), + lerp(min_hue_shift, max_hue_shift, scale) + ); + return color; +} + +float4 alphaRangeImage(VertData v_in) : TARGET { float4 mask = pmrgba_to_rgba(source_image.Sample(textureSampler, v_in.uv)); float alpha = multiplier * (mask.r * channel_multipliers.r + mask.g * channel_multipliers.g + mask.b * channel_multipliers.b + mask.a * channel_multipliers.a); @@ -76,39 +100,73 @@ float4 rangeImage(VertData v_in) : TARGET return float4(color.rgb, color.a * (invert ? 1.0 - alpha : alpha)); } +float4 adjustmentsRangeImage(VertData v_in) : TARGET +{ + float4 mask = pmrgba_to_rgba(source_image.Sample(textureSampler, v_in.uv)); + float scale = multiplier * (mask.r * channel_multipliers.r + mask.g * channel_multipliers.g + mask.b * channel_multipliers.b + mask.a * channel_multipliers.a); + scale = smoothstep(range_min, range_max, scale); + scale = invert ? 1.0 - scale : scale; + float4 color = pmrgba_to_rgba(image.Sample(textureSampler, v_in.uv)); + color = adjustments( + color, + lerp(min_brightness, max_brightness, scale), + lerp(min_contrast, max_contrast, scale), + lerp(min_saturation, max_saturation, scale), + lerp(min_hue_shift, max_hue_shift, scale) + ); + return color; +} + +technique Alpha +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = alphaImage(v_in); + } +} + +technique Adjustments +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = adjustmentsImage(v_in); + } +} -technique Draw +technique AlphaThreshold { pass { vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); + pixel_shader = alphaThresholdImage(v_in); } } -technique DrawAdjustments +technique AlphaRange { pass { vertex_shader = mainTransform(v_in); - pixel_shader = mainAdjustmentsImage(v_in); + pixel_shader = alphaRangeImage(v_in); } } -technique Threshold +technique AdjustmentsThreshold { pass { vertex_shader = mainTransform(v_in); - pixel_shader = thresholdImage(v_in); + pixel_shader = adjustmentsThresholdImage(v_in); } } -technique Range +technique AdjustmentsRange { pass { vertex_shader = mainTransform(v_in); - pixel_shader = rangeImage(v_in); + pixel_shader = adjustmentsRangeImage(v_in); } } diff --git a/src/advanced-masks-filter.c b/src/advanced-masks-filter.c index 80d74d6..f079307 100644 --- a/src/advanced-masks-filter.c +++ b/src/advanced-masks-filter.c @@ -44,6 +44,7 @@ static void *advanced_masks_create(obs_data_t *settings, obs_source_t *source) filter->rendering = false; filter->param_rectangle_image = NULL; + filter->param_rectangle_uv_size = NULL; filter->param_rectangle_mask_position = NULL; filter->param_rectangle_width = NULL; filter->param_rectangle_height = NULL; @@ -54,6 +55,15 @@ static void *advanced_masks_create(obs_data_t *settings, obs_source_t *source) filter->param_rectangle_aa_scale = NULL; filter->param_max_corner_radius = NULL; filter->param_rectangle_zoom = NULL; + filter->param_rectangle_min_brightness = NULL; + filter->param_rectangle_max_brightness = NULL; + filter->param_rectangle_min_contrast = NULL; + filter->param_rectangle_max_contrast = NULL; + filter->param_rectangle_min_saturation = NULL; + filter->param_rectangle_max_saturation = NULL; + filter->param_rectangle_min_hue_shift = NULL; + filter->param_rectangle_max_hue_shift = NULL; + filter->param_circle_global_position = NULL; filter->param_circle_mask_position = NULL; @@ -71,18 +81,29 @@ static void *advanced_masks_create(obs_data_t *settings, obs_source_t *source) filter->param_source_threshold_value = NULL; filter->param_source_range_min = NULL; filter->param_source_range_max = NULL; - filter->param_source_adj_brightness = NULL; filter->param_source_min_brightness = NULL; filter->param_source_max_brightness = NULL; + filter->param_source_min_contrast = NULL; + filter->param_source_max_contrast = NULL; + filter->param_source_min_saturation = NULL; + filter->param_source_max_saturation = NULL; + filter->param_source_min_hue_shift = NULL; + filter->param_source_max_hue_shift = NULL; filter->param_gradient_image = NULL; filter->param_gradient_width = NULL; filter->param_gradient_position = NULL; filter->param_gradient_rotation = NULL; filter->param_gradient_uv_size = NULL; - filter->param_gradient_adj_brightness = NULL; + filter->param_gradient_invert = NULL; filter->param_gradient_min_brightness = NULL; filter->param_gradient_max_brightness = NULL; + filter->param_gradient_min_contrast = NULL; + filter->param_gradient_max_contrast = NULL; + filter->param_gradient_min_saturation = NULL; + filter->param_gradient_max_saturation = NULL; + filter->param_gradient_min_hue_shift = NULL; + filter->param_gradient_max_hue_shift = NULL; load_effect_files(filter); obs_source_update(source, settings); @@ -147,13 +168,29 @@ static void advanced_masks_update(void *data, obs_data_t *settings) (uint32_t)obs_data_get_int(settings, "mask_effect"); filter->adj_brightness = obs_data_get_bool(settings, "brightness"); - filter->brightness = - (float)obs_data_get_double(settings, "brightness_value"); filter->min_brightness = (float)obs_data_get_double(settings, "min_brightness_value"); filter->max_brightness = (float)obs_data_get_double(settings, "max_brightness_value"); + filter->adj_contrast = obs_data_get_bool(settings, "contrast"); + filter->min_contrast = + (float)obs_data_get_double(settings, "min_contrast_value"); + filter->max_contrast = + (float)obs_data_get_double(settings, "max_contrast_value"); + + filter->adj_saturation = obs_data_get_bool(settings, "saturation"); + filter->min_saturation = + (float)obs_data_get_double(settings, "min_saturation_value"); + filter->max_saturation = + (float)obs_data_get_double(settings, "max_saturation_value"); + + filter->adj_hue_shift = obs_data_get_bool(settings, "hue_shift"); + filter->min_hue_shift = + (float)obs_data_get_double(settings, "min_hue_shift_value"); + filter->max_hue_shift = + (float)obs_data_get_double(settings, "max_hue_shift_value"); + filter->mask_type = (uint32_t)obs_data_get_int(settings, "mask_type"); filter->mask_shape_type = (uint32_t)obs_data_get_int(settings, "shape_type"); @@ -167,13 +204,15 @@ static void advanced_masks_update(void *data, obs_data_t *settings) filter->global_position.y = (float)obs_data_get_double(settings, "position_y"); filter->global_scale = - (float)obs_data_get_double(settings, "position_scale"); + filter->mask_effect == MASK_EFFECT_ALPHA ? (float)obs_data_get_double(settings, "position_scale") : 100.0f; filter->rectangle_width = (float)obs_data_get_double(settings, "rectangle_width"); filter->rectangle_height = (float)obs_data_get_double(settings, "rectangle_height"); - filter->zoom = (float)obs_data_get_double(settings, "source_zoom"); + filter->zoom = filter->mask_effect == MASK_EFFECT_ALPHA + ? (float)obs_data_get_double(settings, "source_zoom") : 100.0f; + filter->corner_radius_type = (uint32_t)obs_data_get_int(settings, "rectangle_corner_type"); if (filter->corner_radius_type == MASK_CORNER_UNIFORM) { @@ -268,6 +307,8 @@ static void advanced_masks_update(void *data, obs_data_t *settings) (float)obs_data_get_double(settings, "mask_gradient_rotation"); filter->gradient_debug = obs_data_get_bool(settings, "mask_gradient_debug"); + filter->gradient_invert = + obs_data_get_bool(settings, "gradient_invert"); } static void advanced_masks_video_render(void *data, gs_effect_t *effect) @@ -369,16 +410,44 @@ static obs_properties_t *advanced_masks_properties(void *data) obs_property_set_modified_callback(mask_type_list, setting_mask_type_modified); + obs_property_t *mask_source = obs_properties_add_list( + props, "mask_source", + obs_module_text("AdvancedMasks.SourceMask.Source"), + OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string( + mask_source, obs_module_text("AdvancedMasks.Common.None"), ""); + obs_enum_sources(add_source_to_list, mask_source); + obs_enum_scenes(add_source_to_list, mask_source); + + obs_property_t *shape_type_list = obs_properties_add_list( + props, "shape_type", obs_module_text("AdvancedMasks.Shape"), + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + + obs_property_list_add_int(shape_type_list, + obs_module_text(SHAPE_RECTANGLE_LABEL), + SHAPE_RECTANGLE); + obs_property_list_add_int(shape_type_list, + obs_module_text(SHAPE_CIRCLE_LABEL), + SHAPE_CIRCLE); + //obs_property_list_add_int(shape_type_list, + // obs_module_text(SHAPE_ELLIPSE_LABEL), + // SHAPE_ELLIPSE); + //obs_property_list_add_int(shape_type_list, + // obs_module_text(SHAPE_HEXAGON_LABEL), + // SHAPE_HEXAGON); + + obs_property_set_modified_callback(shape_type_list, + setting_shape_type_modified); + + obs_properties_t *mask_adjustments_group = obs_properties_create(); p = obs_properties_add_bool( mask_adjustments_group, "brightness", obs_module_text("AdvancedMasks.Adjustments.Brightness")); - p = obs_properties_add_float_slider( - mask_adjustments_group, "brightness_value", - obs_module_text("AdvancedMasks.Adjustments.Brightness"), -1.0, - 1.0, 0.01); + obs_property_set_modified_callback(p, + setting_mask_adjustment_modified); p = obs_properties_add_float_slider( mask_adjustments_group, "min_brightness_value", @@ -390,6 +459,54 @@ static obs_properties_t *advanced_masks_properties(void *data) obs_module_text("AdvancedMasks.Adjustments.MaxBrightness"), -1.0, 1.0, 0.01); + p = obs_properties_add_bool( + mask_adjustments_group, "contrast", + obs_module_text("AdvancedMasks.Adjustments.Contrast")); + + obs_property_set_modified_callback(p, setting_mask_adjustment_modified); + + p = obs_properties_add_float_slider( + mask_adjustments_group, "min_contrast_value", + obs_module_text("AdvancedMasks.Adjustments.MinContrast"), + -4.0, 4.0, 0.01); + + p = obs_properties_add_float_slider( + mask_adjustments_group, "max_contrast_value", + obs_module_text("AdvancedMasks.Adjustments.MaxContrast"), + -4.0, 4.0, 0.01); + + p = obs_properties_add_bool( + mask_adjustments_group, "saturation", + obs_module_text("AdvancedMasks.Adjustments.Saturation")); + + obs_property_set_modified_callback(p, setting_mask_adjustment_modified); + + p = obs_properties_add_float_slider( + mask_adjustments_group, "min_saturation_value", + obs_module_text("AdvancedMasks.Adjustments.MinSaturation"), + 0.0, 5.0, 0.01); + + p = obs_properties_add_float_slider( + mask_adjustments_group, "max_saturation_value", + obs_module_text("AdvancedMasks.Adjustments.MaxSaturation"), + 0.0, 5.0, 0.01); + + p = obs_properties_add_bool( + mask_adjustments_group, "hue_shift", + obs_module_text("AdvancedMasks.Adjustments.HueShift")); + + obs_property_set_modified_callback(p, setting_mask_adjustment_modified); + + p = obs_properties_add_float_slider( + mask_adjustments_group, "min_hue_shift_value", + obs_module_text("AdvancedMasks.Adjustments.MinHueShift"), -360.0, + 360.0, 0.1); + + p = obs_properties_add_float_slider( + mask_adjustments_group, "max_hue_shift_value", + obs_module_text("AdvancedMasks.Adjustments.MaxHueShift"), -360.0, + 360.0, 0.1); + obs_properties_add_group( props, "mask_adjustments_group", obs_module_text("AdvancedMasks.Adjustments.Label"), @@ -415,6 +532,10 @@ static obs_properties_t *advanced_masks_properties(void *data) 360.0, 0.1); obs_property_float_set_suffix(p, "deg"); + obs_properties_add_bool( + mask_gradient_group, "gradient_invert", + obs_module_text("AdvancedMasks.GradientMask.Invert")); + obs_properties_add_bool( mask_gradient_group, "mask_gradient_debug", obs_module_text("AdvancedMasks.GradientMask.DebugLines")); @@ -425,15 +546,6 @@ static obs_properties_t *advanced_masks_properties(void *data) OBS_GROUP_NORMAL, mask_gradient_group); // START OF SOURCE STUFF - obs_property_t *mask_source = obs_properties_add_list( - props, "mask_source", - obs_module_text("AdvancedMasks.SourceMask.Source"), - OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); - obs_property_list_add_string( - mask_source, obs_module_text("AdvancedMasks.Common.None"), ""); - obs_enum_sources(add_source_to_list, mask_source); - obs_enum_scenes(add_source_to_list, mask_source); - obs_properties_t *mask_source_group = obs_properties_create(); obs_property_t *mask_source_filter_list = obs_properties_add_list( @@ -542,35 +654,8 @@ static obs_properties_t *advanced_masks_properties(void *data) obs_module_text("AdvancedMasks.SourceMaskCompress"), OBS_GROUP_NORMAL, mask_source_compression_group); - //obs_properties_t *source_mask_group = obs_properties_create(); - - //obs_properties_add_group( - // props, "source_mask_group", - // obs_module_text("AdvancedMasks.SourceMask"), - // OBS_GROUP_NORMAL, source_mask_group); - - // Add a source to this. - // START OF SHAPE STUFF - obs_property_t *shape_type_list = obs_properties_add_list( - props, "shape_type", obs_module_text("AdvancedMasks.Shape"), - OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - - obs_property_list_add_int(shape_type_list, - obs_module_text(SHAPE_RECTANGLE_LABEL), - SHAPE_RECTANGLE); - obs_property_list_add_int(shape_type_list, - obs_module_text(SHAPE_CIRCLE_LABEL), - SHAPE_CIRCLE); - //obs_property_list_add_int(shape_type_list, - // obs_module_text(SHAPE_ELLIPSE_LABEL), - // SHAPE_ELLIPSE); - //obs_property_list_add_int(shape_type_list, - // obs_module_text(SHAPE_HEXAGON_LABEL), - // SHAPE_HEXAGON); - obs_property_set_modified_callback(shape_type_list, - setting_shape_type_modified); // START OF SHAPE - RECTANGLE @@ -735,6 +820,7 @@ static bool setting_shape_type_modified(obs_properties_t *props, { UNUSED_PARAMETER(p); int shape_type = (int)obs_data_get_int(settings, "shape_type"); + int effect_type = (int)obs_data_get_int(settings, "mask_effect"); switch (shape_type) { case SHAPE_RECTANGLE: setting_visibility("rectangle_width", true, props); @@ -750,7 +836,13 @@ static bool setting_shape_type_modified(obs_properties_t *props, setting_visibility("rectangle_rounded_corners_group", false, props); } - + setting_visibility("source_zoom", effect_type == MASK_EFFECT_ALPHA, props); + obs_property_t *group = obs_properties_get(props, "rectangle_source_group"); + const char *group_name = + effect_type == MASK_EFFECT_ALPHA + ? obs_module_text("AdvancedMasks.Shape.Rectangle.SourceGroup") + : obs_module_text("AdvancedMasks.Shape.Rectangle.GeometryGroup"); + obs_property_set_description(group, group_name); return true; } @@ -809,6 +901,26 @@ static bool setting_mask_source_filter_modified(obs_properties_t *props, return true; } +static bool setting_mask_adjustment_modified(obs_properties_t *props, + obs_property_t *p, + obs_data_t *settings) +{ + UNUSED_PARAMETER(p); + bool brightness = obs_data_get_bool(settings, "brightness"); + bool contrast = obs_data_get_bool(settings, "contrast"); + bool saturation = obs_data_get_bool(settings, "saturation"); + bool hue_shift = obs_data_get_bool(settings, "hue_shift"); + setting_visibility("min_brightness_value", brightness, props); + setting_visibility("max_brightness_value", brightness, props); + setting_visibility("min_contrast_value", contrast, props); + setting_visibility("max_contrast_value", contrast, props); + setting_visibility("min_saturation_value", saturation, props); + setting_visibility("max_saturation_value", saturation, props); + setting_visibility("min_hue_shift_value", hue_shift, props); + setting_visibility("max_hue_shift_value", hue_shift, props); + return true; +} + static bool setting_mask_effect_modified(obs_properties_t *props, obs_property_t *p, obs_data_t *settings) @@ -818,12 +930,14 @@ static bool setting_mask_effect_modified(obs_properties_t *props, switch (mask_effect) { case MASK_EFFECT_ADJUSTMENT: setting_visibility("mask_adjustments_group", true, props); - return true; + break; case MASK_EFFECT_ALPHA: setting_visibility("mask_adjustments_group", false, props); - return true; + break; } - return false; + setting_mask_type_modified(props, p, settings); + setting_shape_type_modified(props, p, settings); + return true; } static bool setting_mask_type_modified(obs_properties_t *props, @@ -831,6 +945,7 @@ static bool setting_mask_type_modified(obs_properties_t *props, { UNUSED_PARAMETER(p); int mask_type = (int)obs_data_get_int(settings, "mask_type"); + int effect_type = (int)obs_data_get_int(settings, "mask_effect"); switch (mask_type) { case MASK_TYPE_SHAPE: setting_visibility("mask_source", false, props); @@ -841,7 +956,8 @@ static bool setting_mask_type_modified(obs_properties_t *props, setting_visibility("rectangle_source_group", true, props); setting_visibility("rectangle_rounded_corners_group", true, props); - setting_visibility("scale_position_group", true, props); + setting_visibility("scale_position_group", + effect_type == MASK_EFFECT_ALPHA, props); setting_visibility("mask_gradient_group", false, props); return true; case MASK_TYPE_SOURCE: @@ -958,9 +1074,18 @@ static void advanced_masks_video_tick(void *data, float seconds) static void advanced_masks_defaults(obs_data_t *settings) { obs_data_set_default_int(settings, "mask_effect", MASK_EFFECT_ALPHA); - obs_data_set_default_double(settings, "brightness_value", 0.0); - obs_data_set_default_double(settings, "min_brightness_value", 0.0); + obs_data_set_default_bool(settings, "brightness", false); + obs_data_set_default_double(settings, "min_brightness_value", -1.0); obs_data_set_default_double(settings, "max_brightness_value", 1.0); + obs_data_set_default_bool(settings, "contrast", false); + obs_data_set_default_double(settings, "min_contrast_value", 0.0); + obs_data_set_default_double(settings, "max_contrast_value", 1.0); + obs_data_set_default_bool(settings, "saturation", false); + obs_data_set_default_double(settings, "min_saturation_value", 0.0); + obs_data_set_default_double(settings, "max_saturation_value", 4.0); + obs_data_set_default_bool(settings, "hue_shift", false); + obs_data_set_default_double(settings, "min_hue_shift_value", -180.0); + obs_data_set_default_double(settings, "max_hue_shift_value", 180.0); obs_data_set_default_int(settings, "mask_type", MASK_TYPE_SHAPE); obs_data_set_default_int(settings, "shape_type", SHAPE_RECTANGLE); @@ -974,6 +1099,8 @@ static void advanced_masks_defaults(obs_data_t *settings) obs_data_set_default_double(settings, "mask_source_filter_multiplier", 1.0); obs_data_set_default_double(settings, "source_zoom", 100.0); + + obs_data_set_default_bool(settings, "gradient_invert", false); } static void get_input_source(advanced_masks_data_t *filter) @@ -1044,6 +1171,8 @@ static void shape_load_rectangle_effect(advanced_masks_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "image") == 0) { filter->param_rectangle_image = param; + } else if (strcmp(info.name, "uv_size") == 0) { + filter->param_rectangle_uv_size = param; } else if (strcmp(info.name, "mask_position") == 0) { filter->param_rectangle_mask_position = param; } else if (strcmp(info.name, "width") == 0) { @@ -1056,8 +1185,7 @@ static void shape_load_rectangle_effect(advanced_masks_data_t *filter) filter->param_global_scale = param; } else if (strcmp(info.name, "corner_radius") == 0) { filter->param_corner_radius = param; - } else if (strcmp(info.name, "max_corner_radius") == - 0) { + } else if (strcmp(info.name, "max_corner_radius") == 0) { filter->param_max_corner_radius = param; } else if (strcmp(info.name, "aspect_ratio") == 0) { filter->param_rect_aspect_ratio = param; @@ -1065,6 +1193,22 @@ static void shape_load_rectangle_effect(advanced_masks_data_t *filter) filter->param_rectangle_aa_scale = param; } else if (strcmp(info.name, "zoom") == 0) { filter->param_rectangle_zoom = param; + } else if (strcmp(info.name, "min_brightness") == 0) { + filter->param_rectangle_min_brightness = param; + } else if (strcmp(info.name, "max_brightness") == 0) { + filter->param_rectangle_max_brightness = param; + } else if (strcmp(info.name, "min_contrast") == 0) { + filter->param_rectangle_min_contrast = param; + } else if (strcmp(info.name, "max_contrast") == 0) { + filter->param_rectangle_max_contrast = param; + } else if (strcmp(info.name, "min_saturation") == 0) { + filter->param_rectangle_min_saturation = param; + } else if (strcmp(info.name, "max_saturation") == 0) { + filter->param_rectangle_max_saturation = param; + } else if (strcmp(info.name, "min_hue_shift") == 0) { + filter->param_rectangle_min_hue_shift = param; + } else if (strcmp(info.name, "max_hue_shift") == 0) { + filter->param_rectangle_max_hue_shift = param; } } } @@ -1097,31 +1241,24 @@ static void render_rect_mask(advanced_masks_data_t *data) data->zoom / 100.0f); } if (data->param_rectangle_mask_position) { - struct vec2 mask_center; - mask_center.x = data->mask_center.x / (float)data->width; - mask_center.y = data->mask_center.y / (float)data->height; gs_effect_set_vec2(data->param_rectangle_mask_position, - &mask_center); + &data->mask_center); } if (data->param_rectangle_width) { - float width = data->rectangle_width / (float)data->width; - gs_effect_set_float(data->param_rectangle_width, width); + gs_effect_set_float(data->param_rectangle_width, + data->rectangle_width); } if (data->param_rectangle_height) { - float height = data->rectangle_height / (float)data->height; - gs_effect_set_float(data->param_rectangle_height, height); + gs_effect_set_float(data->param_rectangle_height, + data->rectangle_height); } if (data->param_global_position) { - struct vec2 global_position; - global_position.x = - data->global_position.x / (float)data->width; - global_position.y = - data->global_position.y / (float)data->height; gs_effect_set_vec2(data->param_global_position, - &global_position); + &data->global_position); + } if (data->param_global_scale) { @@ -1132,8 +1269,6 @@ static void render_rect_mask(advanced_masks_data_t *data) if (data->param_corner_radius) { struct vec4 corner_radius; vec4_divf(&corner_radius, &data->rectangle_corner_radius, - (float)fmin((double)data->width, - (double)data->height) * scale_factor * (data->zoom / 100.0f)); gs_effect_set_vec4(data->param_corner_radius, &corner_radius); } @@ -1141,8 +1276,7 @@ static void render_rect_mask(advanced_masks_data_t *data) if (data->param_max_corner_radius) { float max_corner_radius = data->rectangle_max_corner_radius / scale_factor * - (data->zoom / 100.0f) / - (float)fmin((double)data->width, (double)data->height); + (data->zoom / 100.0f); gs_effect_set_float(data->param_max_corner_radius, max_corner_radius); } @@ -1164,13 +1298,80 @@ static void render_rect_mask(advanced_masks_data_t *data) gs_effect_set_float(data->param_rectangle_aa_scale, aa_scale); } + if (data->param_rectangle_min_brightness) { + const float min_brightness = + data->adj_brightness ? data->min_brightness : 0.0f; + gs_effect_set_float(data->param_rectangle_min_brightness, + min_brightness); + } + + if (data->param_rectangle_max_brightness) { + const float max_brightness = + data->adj_brightness ? data->max_brightness : 0.0f; + gs_effect_set_float(data->param_rectangle_max_brightness, + max_brightness); + } + + if (data->param_rectangle_min_contrast) { + const float min_contrast = + data->adj_contrast ? data->min_contrast : 0.0f; + gs_effect_set_float(data->param_rectangle_min_contrast, + min_contrast); + } + + if (data->param_rectangle_max_contrast) { + const float max_contrast = + data->adj_contrast ? data->max_contrast : 0.0f; + gs_effect_set_float(data->param_rectangle_max_contrast, + max_contrast); + } + + if (data->param_rectangle_min_saturation) { + const float min_saturation = + data->adj_saturation ? data->min_saturation : 1.0f; + gs_effect_set_float(data->param_rectangle_min_saturation, + min_saturation); + } + + if (data->param_rectangle_max_saturation) { + const float max_saturation = + data->adj_saturation ? data->max_saturation : 1.0f; + gs_effect_set_float(data->param_rectangle_max_saturation, + max_saturation); + } + + if (data->param_rectangle_min_hue_shift) { + const float min_hue_shift = + data->adj_hue_shift ? data->min_hue_shift : 0.0f; + gs_effect_set_float(data->param_rectangle_min_hue_shift, + min_hue_shift); + } + + if (data->param_rectangle_max_hue_shift) { + const float max_hue_shift = + data->adj_hue_shift ? data->max_hue_shift : 1.0f; + gs_effect_set_float(data->param_rectangle_max_hue_shift, + max_hue_shift); + } + + if (data->param_rectangle_uv_size) { + struct vec2 uv_size; + uv_size.x = (float)data->width; + uv_size.y = (float)data->height; + gs_effect_set_vec2(data->param_rectangle_uv_size, &uv_size); + } + set_blending_parameters(); + const char *technique = data->mask_effect == MASK_EFFECT_ALPHA + ? "Alpha" + : "Adjustments"; + if (gs_texrender_begin(data->output_texrender, data->width, data->height)) { gs_ortho(0.0f, (float)data->width, 0.0f, (float)data->height, - -100.0f, 100.0f); - while (gs_effect_loop(effect, "SharpCorners")) + -100.0f, 100.0f); + while (gs_effect_loop(effect, technique)) gs_draw_sprite(texture, 0, data->width, data->height); gs_texrender_end(data->output_texrender); } @@ -1205,31 +1406,23 @@ static void render_circle_mask(advanced_masks_data_t *data) data->zoom / 100.0f); } if (data->param_rectangle_mask_position) { - struct vec2 mask_center; - mask_center.x = data->mask_center.x / (float)data->width; - mask_center.y = data->mask_center.y / (float)data->height; gs_effect_set_vec2(data->param_rectangle_mask_position, - &mask_center); + &data->mask_center); } if (data->param_rectangle_width) { - float width = 2.0f * data->radius / (float)data->width; + float width = 2.0f * data->radius; gs_effect_set_float(data->param_rectangle_width, width); } if (data->param_rectangle_height) { - float height = 2.0f * data->radius / (float)data->height; + float height = 2.0f * data->radius; gs_effect_set_float(data->param_rectangle_height, height); } if (data->param_global_position) { - struct vec2 global_position; - global_position.x = - data->global_position.x / (float)data->width; - global_position.y = - data->global_position.y / (float)data->height; gs_effect_set_vec2(data->param_global_position, - &global_position); + &data->global_position); } if (data->param_global_scale) { @@ -1239,17 +1432,14 @@ static void render_circle_mask(advanced_masks_data_t *data) if (data->param_corner_radius) { struct vec4 corner_radius; - float r = data->radius / ((float)fmin((double)data->width, - (double)data->height) * - (data->zoom / 100.0f)); + float r = data->radius / (data->zoom / 100.0f); vec4_set(&corner_radius, r, r, r, r); gs_effect_set_vec4(data->param_corner_radius, &corner_radius); } if (data->param_max_corner_radius) { float max_corner_radius = - data->radius / (data->zoom / 100.0f) / - (float)fmin((double)data->width, (double)data->height); + data->radius / (data->zoom / 100.0f); gs_effect_set_float(data->param_max_corner_radius, max_corner_radius); } @@ -1271,13 +1461,80 @@ static void render_circle_mask(advanced_masks_data_t *data) gs_effect_set_float(data->param_rectangle_aa_scale, aa_scale); } + if (data->param_rectangle_min_brightness) { + const float min_brightness = + data->adj_brightness ? data->min_brightness : 0.0f; + gs_effect_set_float(data->param_rectangle_min_brightness, + min_brightness); + } + + if (data->param_rectangle_max_brightness) { + const float max_brightness = + data->adj_brightness ? data->max_brightness : 0.0f; + gs_effect_set_float(data->param_rectangle_max_brightness, + max_brightness); + } + + if (data->param_rectangle_min_contrast) { + const float min_contrast = + data->adj_contrast ? data->min_contrast : 0.0f; + gs_effect_set_float(data->param_rectangle_min_contrast, + min_contrast); + } + + if (data->param_rectangle_max_contrast) { + const float max_contrast = + data->adj_contrast ? data->max_contrast : 0.0f; + gs_effect_set_float(data->param_rectangle_max_contrast, + max_contrast); + } + + if (data->param_rectangle_min_saturation) { + const float min_saturation = + data->adj_saturation ? data->min_saturation : 1.0f; + gs_effect_set_float(data->param_rectangle_min_saturation, + min_saturation); + } + + if (data->param_rectangle_max_saturation) { + const float max_saturation = + data->adj_saturation ? data->max_saturation : 1.0f; + gs_effect_set_float(data->param_rectangle_max_saturation, + max_saturation); + } + + if (data->param_rectangle_min_hue_shift) { + const float min_hue_shift = + data->adj_hue_shift ? data->min_hue_shift : 0.0f; + gs_effect_set_float(data->param_rectangle_min_hue_shift, + min_hue_shift); + } + + if (data->param_rectangle_max_hue_shift) { + const float max_hue_shift = + data->adj_hue_shift ? data->max_hue_shift : 1.0f; + gs_effect_set_float(data->param_rectangle_max_hue_shift, + max_hue_shift); + } + + if (data->param_rectangle_uv_size) { + struct vec2 uv_size; + uv_size.x = (float)data->width; + uv_size.y = (float)data->height; + gs_effect_set_vec2(data->param_rectangle_uv_size, &uv_size); + } + set_blending_parameters(); + const char *technique = data->mask_effect == MASK_EFFECT_ALPHA + ? "Alpha" + : "Adjustments"; + if (gs_texrender_begin(data->output_texrender, data->width, data->height)) { gs_ortho(0.0f, (float)data->width, 0.0f, (float)data->height, -100.0f, 100.0f); - while (gs_effect_loop(effect, "SharpCorners")) + while (gs_effect_loop(effect, technique)) gs_draw_sprite(texture, 0, data->width, data->height); gs_texrender_end(data->output_texrender); } @@ -1307,10 +1564,8 @@ static void load_source_mask_effect(advanced_masks_data_t *filter) filter->param_source_mask_source_image = param; } else if (strcmp(info.name, "invert") == 0) { filter->param_source_mask_invert = param; - } else if (strcmp(info.name, "channel_multipliers") == - 0) { - filter->param_source_channel_multipliers = - param; + } else if (strcmp(info.name, "channel_multipliers") == 0) { + filter->param_source_channel_multipliers = param; } else if (strcmp(info.name, "multiplier") == 0) { filter->param_source_multiplier = param; } else if (strcmp(info.name, "threshold_value") == 0) { @@ -1319,12 +1574,22 @@ static void load_source_mask_effect(advanced_masks_data_t *filter) filter->param_source_range_min = param; } else if (strcmp(info.name, "range_max") == 0) { filter->param_source_range_max = param; - } else if (strcmp(info.name, "adj_brightness") == 0) { - filter->param_source_adj_brightness = param; } else if (strcmp(info.name, "min_brightness") == 0) { filter->param_source_min_brightness = param; } else if (strcmp(info.name, "max_brightness") == 0) { filter->param_source_max_brightness = param; + } else if (strcmp(info.name, "min_contrast") == 0) { + filter->param_source_min_contrast = param; + } else if (strcmp(info.name, "max_contrast") == 0) { + filter->param_source_max_contrast = param; + } else if (strcmp(info.name, "min_saturation") == 0) { + filter->param_source_min_saturation = param; + } else if (strcmp(info.name, "max_saturation") == 0) { + filter->param_source_max_saturation = param; + } else if (strcmp(info.name, "min_hue_shift") == 0) { + filter->param_source_min_hue_shift = param; + } else if (strcmp(info.name, "max_hue_shift") == 0) { + filter->param_source_max_hue_shift = param; } } } @@ -1375,19 +1640,60 @@ static void render_source_mask(advanced_masks_data_t *data) data->range_max); } - if (data->param_source_adj_brightness) { - gs_effect_set_bool(data->param_source_adj_brightness, - data->adj_brightness); - } - if (data->param_source_min_brightness) { + const float min_brightness = + data->adj_brightness ? data->min_brightness : 0.0f; gs_effect_set_float(data->param_source_min_brightness, - data->min_brightness); + min_brightness); } if (data->param_source_max_brightness) { + const float max_brightness = + data->adj_brightness ? data->max_brightness : 0.0f; gs_effect_set_float(data->param_source_max_brightness, - data->max_brightness); + max_brightness); + } + + if (data->param_source_min_contrast) { + const float min_contrast = + data->adj_contrast ? data->min_contrast : 0.0f; + gs_effect_set_float(data->param_source_min_contrast, + min_contrast); + } + + if (data->param_source_max_contrast) { + const float max_contrast = data->adj_contrast ? data->max_contrast + : 0.0f; + gs_effect_set_float(data->param_source_max_contrast, + max_contrast); + } + + if (data->param_source_min_saturation) { + const float min_saturation = data->adj_saturation ? data->min_saturation + : 1.0f; + gs_effect_set_float(data->param_source_min_saturation, + min_saturation); + } + + if (data->param_source_max_saturation) { + const float max_saturation = data->adj_saturation ? data->max_saturation + : 1.0f; + gs_effect_set_float(data->param_source_max_saturation, + max_saturation); + } + + if (data->param_source_min_hue_shift) { + const float min_hue_shift = + data->adj_hue_shift ? data->min_hue_shift : 0.0f; + gs_effect_set_float(data->param_source_min_hue_shift, + min_hue_shift); + } + + if (data->param_source_max_hue_shift) { + const float max_hue_shift = + data->adj_hue_shift ? data->max_hue_shift : 1.0f; + gs_effect_set_float(data->param_source_max_hue_shift, + max_hue_shift); } gs_texrender_t *mask_source_render = NULL; @@ -1438,14 +1744,17 @@ static void render_source_mask(advanced_masks_data_t *data) set_blending_parameters(); - const char *technique = + char technique[32]; + strcpy(technique, data->mask_effect == MASK_EFFECT_ADJUSTMENT + ? "Adjustments" + : "Alpha"); + char *techniqueType = data->compression_type == MASK_SOURCE_COMPRESSION_THRESHOLD - ? "Threshold" - : data->compression_type == MASK_SOURCE_COMPRESSION_RANGE - ? "Range" - : data->mask_effect == MASK_EFFECT_ADJUSTMENT - ? "DrawAdjustments" - : "Draw"; + ? "Threshold" + : data->compression_type == MASK_SOURCE_COMPRESSION_RANGE + ? "Range" + : ""; + strcat(technique, techniqueType); if (gs_texrender_begin(data->output_texrender, data->width, data->height)) { @@ -1519,12 +1828,24 @@ static void load_gradient_mask_effect(advanced_masks_data_t *filter) filter->param_gradient_position = param; } else if (strcmp(info.name, "rotation") == 0) { filter->param_gradient_rotation = param; - } else if (strcmp(info.name, "adj_brightness") == 0) { - filter->param_gradient_adj_brightness = param; + } else if (strcmp(info.name, "invert") == 0) { + filter->param_gradient_invert = param; } else if (strcmp(info.name, "min_brightness") == 0) { filter->param_gradient_min_brightness = param; } else if (strcmp(info.name, "max_brightness") == 0) { filter->param_gradient_max_brightness = param; + } else if (strcmp(info.name, "min_contrast") == 0) { + filter->param_gradient_min_contrast = param; + } else if (strcmp(info.name, "max_contrast") == 0) { + filter->param_gradient_max_contrast = param; + } else if (strcmp(info.name, "min_saturation") == 0) { + filter->param_gradient_min_saturation = param; + } else if (strcmp(info.name, "max_saturation") == 0) { + filter->param_gradient_max_saturation = param; + } else if (strcmp(info.name, "min_hue_shift") == 0) { + filter->param_gradient_min_hue_shift = param; + } else if (strcmp(info.name, "max_hue_shift") == 0) { + filter->param_gradient_max_hue_shift = param; } } } @@ -1550,6 +1871,10 @@ static void render_gradient_mask(advanced_masks_data_t *data) data->gradient_width); } + if (data->param_gradient_invert) { + gs_effect_set_bool(data->param_gradient_invert, data->gradient_invert); + } + if (data->param_gradient_position) { const float position = data->gradient_position - (float)data->width / 2.0f; @@ -1561,19 +1886,60 @@ static void render_gradient_mask(advanced_masks_data_t *data) gs_effect_set_float(data->param_gradient_rotation, rotation); } - if (data->param_gradient_adj_brightness) { - gs_effect_set_bool(data->param_gradient_adj_brightness, - data->adj_brightness); - } - if (data->param_gradient_min_brightness) { + const float min_brightness = + data->adj_brightness ? data->min_brightness : 0.0f; gs_effect_set_float(data->param_gradient_min_brightness, - data->min_brightness); + min_brightness); } if (data->param_gradient_max_brightness) { + const float max_brightness = + data->adj_brightness ? data->max_brightness : 0.0f; gs_effect_set_float(data->param_gradient_max_brightness, - data->max_brightness); + max_brightness); + } + + if (data->param_gradient_min_contrast) { + const float min_contrast = + data->adj_contrast ? data->min_contrast : 0.0f; + gs_effect_set_float(data->param_gradient_min_contrast, + min_contrast); + } + + if (data->param_gradient_max_contrast) { + const float max_contrast = + data->adj_contrast ? data->max_contrast : 0.0f; + gs_effect_set_float(data->param_gradient_max_contrast, + max_contrast); + } + + if (data->param_gradient_min_saturation) { + const float min_saturation = + data->adj_saturation ? data->min_saturation : 1.0f; + gs_effect_set_float(data->param_gradient_min_saturation, + min_saturation); + } + + if (data->param_gradient_max_saturation) { + const float max_saturation = + data->adj_saturation ? data->max_saturation : 1.0f; + gs_effect_set_float(data->param_gradient_max_saturation, + max_saturation); + } + + if (data->param_gradient_min_hue_shift) { + const float min_hue_shift = + data->adj_hue_shift ? data->min_hue_shift : 0.0f; + gs_effect_set_float(data->param_gradient_min_hue_shift, + min_hue_shift); + } + + if (data->param_gradient_max_hue_shift) { + const float max_hue_shift = + data->adj_hue_shift ? data->max_hue_shift : 1.0f; + gs_effect_set_float(data->param_gradient_max_hue_shift, + max_hue_shift); } if (data->param_gradient_uv_size) { @@ -1582,11 +1948,15 @@ static void render_gradient_mask(advanced_masks_data_t *data) uv_size.y = (float)data->height; gs_effect_set_vec2(data->param_gradient_uv_size, &uv_size); } + set_render_parameters(); set_blending_parameters(); - const char *technique = data->mask_effect == MASK_EFFECT_ALPHA - ? "DrawAlpha" - : "DrawAdjustments"; + char technique[32]; + strcpy(technique, data->gradient_debug ? "Debug" : ""); + strcat(technique, data->mask_effect == MASK_EFFECT_ALPHA + ? "Alpha" + : "Adjustments"); + if (gs_texrender_begin(data->output_texrender, data->width, data->height)) { gs_ortho(0.0f, (float)data->width, 0.0f, (float)data->height, diff --git a/src/advanced-masks-filter.h b/src/advanced-masks-filter.h index 9c562c9..dedc957 100644 --- a/src/advanced-masks-filter.h +++ b/src/advanced-masks-filter.h @@ -33,6 +33,10 @@ static bool setting_mask_effect_modified(obs_properties_t *props, obs_property_t *p, obs_data_t *settings); +static bool setting_mask_adjustment_modified(obs_properties_t *props, + obs_property_t *p, + obs_data_t *settings); + // Setup Stuff static void load_effect_files(advanced_masks_data_t *filter); diff --git a/src/advanced-masks.h b/src/advanced-masks.h index 7cfa706..40ebd02 100644 --- a/src/advanced-masks.h +++ b/src/advanced-masks.h @@ -95,6 +95,18 @@ struct advanced_masks_data { float min_brightness; float max_brightness; + bool adj_contrast; + float min_contrast; + float max_contrast; + + bool adj_saturation; + float min_saturation; + float max_saturation; + + bool adj_hue_shift; + float min_hue_shift; + float max_hue_shift; + // Parameters for shape masks uint32_t mask_shape_type; struct vec2 mask_center; @@ -130,10 +142,12 @@ struct advanced_masks_data { float gradient_width; float gradient_position; float gradient_rotation; + bool gradient_invert; bool gradient_debug; // Shader Parameters gs_eparam_t *param_rectangle_image; + gs_eparam_t *param_rectangle_uv_size; gs_eparam_t *param_rectangle_mask_position; gs_eparam_t *param_rectangle_width; gs_eparam_t *param_rectangle_height; @@ -144,6 +158,14 @@ struct advanced_masks_data { gs_eparam_t *param_rect_aspect_ratio; gs_eparam_t *param_rectangle_aa_scale; gs_eparam_t *param_rectangle_zoom; + gs_eparam_t *param_rectangle_min_brightness; + gs_eparam_t *param_rectangle_max_brightness; + gs_eparam_t *param_rectangle_min_contrast; + gs_eparam_t *param_rectangle_max_contrast; + gs_eparam_t *param_rectangle_min_saturation; + gs_eparam_t *param_rectangle_max_saturation; + gs_eparam_t *param_rectangle_min_hue_shift; + gs_eparam_t *param_rectangle_max_hue_shift; gs_eparam_t *param_circle_image; gs_eparam_t *param_circle_radius; @@ -161,16 +183,27 @@ struct advanced_masks_data { gs_eparam_t *param_source_threshold_value; gs_eparam_t *param_source_range_min; gs_eparam_t *param_source_range_max; - gs_eparam_t *param_source_adj_brightness; gs_eparam_t *param_source_min_brightness; gs_eparam_t *param_source_max_brightness; + gs_eparam_t *param_source_min_contrast; + gs_eparam_t *param_source_max_contrast; + gs_eparam_t *param_source_min_saturation; + gs_eparam_t *param_source_max_saturation; + gs_eparam_t *param_source_min_hue_shift; + gs_eparam_t *param_source_max_hue_shift; gs_eparam_t *param_gradient_image; gs_eparam_t *param_gradient_width; gs_eparam_t *param_gradient_position; gs_eparam_t *param_gradient_rotation; gs_eparam_t *param_gradient_uv_size; - gs_eparam_t *param_gradient_adj_brightness; + gs_eparam_t *param_gradient_invert; gs_eparam_t *param_gradient_min_brightness; gs_eparam_t *param_gradient_max_brightness; + gs_eparam_t *param_gradient_min_contrast; + gs_eparam_t *param_gradient_max_contrast; + gs_eparam_t *param_gradient_min_saturation; + gs_eparam_t *param_gradient_max_saturation; + gs_eparam_t *param_gradient_min_hue_shift; + gs_eparam_t *param_gradient_max_hue_shift; };