diff --git a/border/shaders/average_fill/compose.slang b/border/shaders/average_fill/compose.slang index 344bca7..be2da33 100644 --- a/border/shaders/average_fill/compose.slang +++ b/border/shaders/average_fill/compose.slang @@ -10,6 +10,13 @@ This is useful for certain games that do not render a full image to maintain the overall aspect ratio and to avoid burn-in. + The preset also allows you to extend the original content to a larger + screen. It's recommended to set the video scaling options as follows: + - Turn integer scaling OFF + - Set aspect ratio to FULL + The shader will then take over and handle the proper scaling and aspect + ratio of the input. + In case the image is cropped on multiple sides, different blend modes for the corner are available. Simply change the parameter for the "corner blend mode". @@ -20,6 +27,7 @@ 3 = Smooth angle-based blending Changelog: + v1.1: Add extension modes from blur fill; Add average gamma adjustment. v1.0: Initial release. */ @@ -27,18 +35,26 @@ layout(push_constant) uniform Push { vec4 InputSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; + vec4 FinalViewportSize; float OS_CROP_TOP; float OS_CROP_BOTTOM; float OS_CROP_LEFT; float OS_CROP_RIGHT; - float CORNER_BLEND_MODE; + float CENTER_CROP; float SAMPLE_SIZE; + float EXTEND_H; + float EXTEND_V; + float CORNER_BLEND_MODE; + float FORCE_ASPECT_RATIO; + float ASPECT_H; + float ASPECT_V; + float FORCE_INTEGER_SCALING; + float FILL_GAMMA; } param; +#include "../blur_fill/scaling.slang" + layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; } global; @@ -66,11 +82,11 @@ layout(set = 0, binding = 6) uniform sampler2D Right; // For mipmap sampling, use a big offset to get the average of a PoT input. #define BIG_NUMBER 9000.1 -vec3 blend_corner(vec3 a, // The first color to blend - vec3 b, // The second color to blend - float wa, // The weight of the first color - float wb, // The weight of the second color - vec2 pixel_coord, // The coordinate to evaluate the blend for +vec3 blend_corner(vec3 a, // The first color to blend + vec3 b, // The second color to blend + float wa, // The weight of the first color + float wb, // The weight of the second color + vec2 coord, // The coordinate to evaluate the blend for vec2 corner_coord, // The coordinate of the corner of the // content after cropping vec2 gap_size // The component-wise distance from the corner @@ -89,7 +105,7 @@ vec3 blend_corner(vec3 a, // The first color to blend case 3: default: // Angle blend - const vec2 delta = (pixel_coord - corner_coord) / gap_size; + const vec2 delta = (coord - corner_coord) / gap_size; // Use absolutes to always operate in 1st quadrant. // This makes the angle work out to be correct in all cases when // carefully choosing argument ordering. @@ -100,90 +116,139 @@ vec3 blend_corner(vec3 a, // The first color to blend } void main() { - const vec2 pixel_coord = vTexCoord * param.InputSize.xy; + const vec2 pixel_coord = o2i(vTexCoord) * param.InputSize.xy; if (pixel_coord.x < param.OS_CROP_LEFT) { + if (param.EXTEND_H < 0.5) { + FragColor = vec4(0.0, 0.0, 0.0, 1.0); + return; + } const vec3 left = textureLod(Left, vec2(0.5), BIG_NUMBER).rgb; if (pixel_coord.y < param.OS_CROP_TOP) { + if (param.EXTEND_V < 0.5) { + FragColor = vec4(0.0, 0.0, 0.0, 1.0); + return; + } // Top left corner const vec3 top = textureLod(Top, vec2(0.5), BIG_NUMBER).rgb; + const vec2 content_corner = + i2o(vec2(param.OS_CROP_LEFT, param.OS_CROP_TOP) * + param.InputSize.zw); + const vec2 viewport_corner = vec2(0.0, 0.0); FragColor = vec4(blend_corner(left, top, param.InputSize.y - param.OS_CROP_TOP - param.OS_CROP_BOTTOM, param.InputSize.x - param.OS_CROP_LEFT - param.OS_CROP_RIGHT, - pixel_coord, - vec2(param.OS_CROP_LEFT, param.OS_CROP_TOP), - vec2(param.OS_CROP_LEFT, param.OS_CROP_TOP)), + vTexCoord, content_corner, + viewport_corner - content_corner), 1.0); + FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); } else if (pixel_coord.y < param.InputSize.y - param.OS_CROP_BOTTOM) { // Left bar - FragColor = vec4(left, 1.0); + FragColor = vec4(pow(left, vec3(param.FILL_GAMMA)), 1.0); } else { + if (param.EXTEND_V < 0.5) { + FragColor = vec4(0.0, 0.0, 0.0, 1.0); + return; + } // Bottom left corner const vec3 bottom = textureLod(Bottom, vec2(0.5), BIG_NUMBER).rgb; - FragColor = vec4( - blend_corner(left, bottom, - param.InputSize.y - param.OS_CROP_TOP - - param.OS_CROP_BOTTOM, - param.InputSize.x - param.OS_CROP_LEFT - - param.OS_CROP_RIGHT, - pixel_coord, - vec2(param.OS_CROP_LEFT, - param.InputSize.y - param.OS_CROP_BOTTOM), - vec2(param.OS_CROP_LEFT, param.OS_CROP_BOTTOM)), - 1.0); + const vec2 content_corner = + i2o(vec2(param.OS_CROP_LEFT, + param.InputSize.y - param.OS_CROP_BOTTOM) * + param.InputSize.zw); + const vec2 viewport_corner = vec2(0.0, 1.0); + FragColor = + vec4(blend_corner(left, bottom, + param.InputSize.y - param.OS_CROP_TOP - + param.OS_CROP_BOTTOM, + param.InputSize.x - param.OS_CROP_LEFT - + param.OS_CROP_RIGHT, + vTexCoord, content_corner, + viewport_corner - content_corner), + 1.0); + FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); } } else if (pixel_coord.x < param.InputSize.x - param.OS_CROP_RIGHT) { if (pixel_coord.y < param.OS_CROP_TOP) { + if (param.EXTEND_V < 0.5) { + FragColor = vec4(0.0, 0.0, 0.0, 1.0); + return; + } // Top bar FragColor = vec4(textureLod(Top, vec2(0.5), BIG_NUMBER).rgb, 1.0); + FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); } else if (pixel_coord.y < param.InputSize.y - param.OS_CROP_BOTTOM) { // Uncropped // Do a sharp (nearest neighbor) resampling. FragColor = vec4( - texture(Input, (floor(vTexCoord * param.InputSize.xy) + 0.5) * - param.InputSize.zw) + texture(Input, (floor(pixel_coord) + 0.5) * param.InputSize.zw) .rgb, 1.0); } else { + if (param.EXTEND_V < 0.5) { + FragColor = vec4(0.0, 0.0, 0.0, 1.0); + return; + } // Bottom bar FragColor = vec4(textureLod(Bottom, vec2(0.5), BIG_NUMBER).rgb, 1.0); + FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); } } else { + if (param.EXTEND_H < 0.5) { + FragColor = vec4(0.0, 0.0, 0.0, 1.0); + return; + } const vec3 right = textureLod(Right, vec2(0.5), BIG_NUMBER).rgb; if (pixel_coord.y < param.OS_CROP_TOP) { + if (param.EXTEND_V < 0.5) { + FragColor = vec4(0.0, 0.0, 0.0, 1.0); + return; + } // Top right corner const vec3 top = textureLod(Top, vec2(0.5), BIG_NUMBER).rgb; + const vec2 content_corner = + i2o(vec2(param.InputSize.x - param.OS_CROP_RIGHT, + param.OS_CROP_TOP) * + param.InputSize.zw); + const vec2 viewport_corner = vec2(1.0, 0.0); FragColor = vec4(blend_corner(right, top, param.InputSize.y - param.OS_CROP_TOP - param.OS_CROP_BOTTOM, param.InputSize.x - param.OS_CROP_LEFT - param.OS_CROP_RIGHT, - pixel_coord, - vec2(param.InputSize.x - param.OS_CROP_RIGHT, - param.OS_CROP_TOP), - vec2(param.OS_CROP_RIGHT, param.OS_CROP_TOP)), + vTexCoord, content_corner, + viewport_corner - content_corner), 1.0); + FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); } else if (pixel_coord.y < param.InputSize.y - param.OS_CROP_BOTTOM) { // Right bar - FragColor = vec4(right, 1.0); + FragColor = vec4(pow(right, vec3(param.FILL_GAMMA)), 1.0); } else { + if (param.EXTEND_V < 0.5) { + FragColor = vec4(0.0, 0.0, 0.0, 1.0); + return; + } // Bottom right corner const vec3 bottom = textureLod(Bottom, vec2(0.5), BIG_NUMBER).rgb; - FragColor = vec4( - blend_corner(right, bottom, - param.InputSize.y - param.OS_CROP_TOP - - param.OS_CROP_BOTTOM, - param.InputSize.x - param.OS_CROP_LEFT - - param.OS_CROP_RIGHT, - pixel_coord, - vec2(param.InputSize.x - param.OS_CROP_RIGHT, - param.InputSize.y - param.OS_CROP_BOTTOM), - vec2(param.OS_CROP_RIGHT, param.OS_CROP_BOTTOM)), - 1.0); + const vec2 content_corner = + i2o(vec2(param.InputSize.x - param.OS_CROP_RIGHT, + param.InputSize.y - param.OS_CROP_BOTTOM) * + param.InputSize.zw); + const vec2 viewport_corner = vec2(1.0, 1.0); + FragColor = + vec4(blend_corner(right, bottom, + param.InputSize.y - param.OS_CROP_TOP - + param.OS_CROP_BOTTOM, + param.InputSize.x - param.OS_CROP_LEFT - + param.OS_CROP_RIGHT, + vTexCoord, content_corner, + viewport_corner - content_corner), + 1.0); + FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); } } } diff --git a/border/shaders/average_fill/parameters.slang b/border/shaders/average_fill/parameters.slang index e3ff754..9dec098 100644 --- a/border/shaders/average_fill/parameters.slang +++ b/border/shaders/average_fill/parameters.slang @@ -1,11 +1,25 @@ // See compose.slang for copyright and other information. // clang-format off -#pragma parameter AVERAGE_FILL_SETTINGS "=== Average fill v1.0 settings ===" 0.0 0.0 1.0 1.0 -#pragma parameter OS_CROP_TOP "Overscan crop top" 16.0 0.0 1024.0 1.0 -#pragma parameter OS_CROP_BOTTOM "Overscan crop bottom" 16.0 0.0 1024.0 1.0 +#pragma parameter AVERAGE_FILL_SETTINGS "=== Average fill v1.1 settings ===" 0.0 0.0 1.0 1.0 +#pragma parameter OS_CROP_TOP "Overscan crop top" 8.0 0.0 1024.0 1.0 +#pragma parameter OS_CROP_BOTTOM "Overscan crop bottom" 8.0 0.0 1024.0 1.0 #pragma parameter OS_CROP_LEFT "Overscan crop left" 0.0 0.0 1024.0 1.0 #pragma parameter OS_CROP_RIGHT "Overscan crop right" 0.0 0.0 1024.0 1.0 -#pragma parameter CORNER_BLEND_MODE "Cropped corner blend mode" 0.0 0.0 3.0 1.0 + +#pragma parameter CENTER_CROP "Center cropped area" 1.0 0.0 1.0 1.0 + #pragma parameter SAMPLE_SIZE "No. of lines for calculating the average" 4.0 1.0 64.0 1.0 + +#pragma parameter EXTEND_H "Extend the fill horizontally" 1.0 0.0 1.0 1.0 +#pragma parameter EXTEND_V "Extend the fill vertically" 1.0 0.0 1.0 1.0 + +#pragma parameter CORNER_BLEND_MODE "Cropped corner blend mode" 0.0 0.0 3.0 1.0 + +#pragma parameter FORCE_ASPECT_RATIO "Force aspect ratio" 0.0 0.0 1.0 1.0 +#pragma parameter ASPECT_H "Horizontal aspect ratio before crop" 4.0 1.0 100.0 1.0 +#pragma parameter ASPECT_V "Vertical aspect ratio before crop" 3.0 1.0 100.0 1.0 +#pragma parameter FORCE_INTEGER_SCALING "Force integer scaling" 1.0 0.0 1.0 1.0 + +#pragma parameter FILL_GAMMA "Background fill gamma adjustment" 1.0 0.5 2.0 0.1 // clang-format on diff --git a/border/shaders/blur_fill/scaling.slang b/border/shaders/blur_fill/scaling.slang index e3cf027..f7d8f51 100644 --- a/border/shaders/blur_fill/scaling.slang +++ b/border/shaders/blur_fill/scaling.slang @@ -1,5 +1,3 @@ -#include "parameters.slang" - // See compose.slang for copyright and other information. // Pixels in input coord. space @@ -48,17 +46,24 @@ vec2 scale_o2i() { } } -// In unit space (output to input) +// Input to output scaling, in unit coordinate systems. +vec2 scale_i2o() { return 1.0 / scale_o2i(); } + +// Get adjusted center in input unit coordinate system. +vec2 get_input_center() { + return param.CENTER_CROP > 0.5 + ? 0.5 * param.InputSize.zw * + vec2(param.OS_CROP_LEFT + param.InputSize.x - + param.OS_CROP_RIGHT, + param.OS_CROP_TOP + param.InputSize.y - + param.OS_CROP_BOTTOM) + : vec2(0.49999); +} + +// In unit space (output to input). // coord_in_input_space = o2i(coord_in_output_space) // This is used to sample from the input texture in the output pass. -vec2 o2i(vec2 x) { - const vec2 center_in = - param.CENTER_CROP > 0.5 - ? 0.5 * param.InputSize.zw * - vec2(param.OS_CROP_LEFT + param.InputSize.x - - param.OS_CROP_RIGHT, - param.OS_CROP_TOP + param.InputSize.y - - param.OS_CROP_BOTTOM) - : vec2(0.49999); - return (x - 0.49999) * scale_o2i() + center_in; -} +vec2 o2i(vec2 x) { return (x - 0.49999) * scale_o2i() + get_input_center(); } + +// In unit coordinate systems. +vec2 i2o(vec2 x) { return (x - get_input_center()) * scale_i2o() + 0.49999; }