diff --git a/border/shaders/average_fill/compose.slang b/border/shaders/average_fill/compose.slang index 6060cdb..d535091 100644 --- a/border/shaders/average_fill/compose.slang +++ b/border/shaders/average_fill/compose.slang @@ -1,7 +1,7 @@ #version 450 /* - Average fill v1.5 by fishku + Average fill v1.6 by fishku Copyright (C) 2023 Public domain license (CC0) @@ -27,6 +27,7 @@ 3 = Smooth angle-based blending Changelog: + v1.6: Refactor for new scaling library. Add rotation support. v1.5: Optimize. Update to new Pixel AA version. v1.4: Add anti-aliased interpolation for non-integer scaling. v1.3: Fix scaling bugs. @@ -37,17 +38,21 @@ // clang-format off #include "parameters.slang" -#include "../../../pixel-art-scaling/shaders/pixel_aa/shared.slang" +#include "../../../pixel-art-scaling/shaders/pixel_aa/parameters.slang" // clang-format on +#include "../../../misc/shaders/scaling.slang" +#include "../../../pixel-art-scaling/shaders/pixel_aa/shared.slang" + layout(push_constant) uniform Push { vec4 InputSize; - vec4 FinalViewportSize; + vec4 OutputSize; + uint Rotation; float OS_CROP_TOP; float OS_CROP_BOTTOM; float OS_CROP_LEFT; float OS_CROP_RIGHT; - float CENTER_CROP; + float CENTER_AFTER_CROPPING; float SAMPLE_SIZE; float EXTEND_H; float EXTEND_V; @@ -55,7 +60,8 @@ layout(push_constant) uniform Push { float FORCE_ASPECT_RATIO; float ASPECT_H; float ASPECT_V; - float FORCE_INTEGER_SCALING; + float FORCE_INTEGER_SCALING_H; + float FORCE_INTEGER_SCALING_V; float FILL_GAMMA; // From pixel AA float PIX_AA_SHARP; @@ -64,8 +70,6 @@ layout(push_constant) uniform Push { } param; -#include "../blur_fill/scaling.slang" - layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; } global; @@ -73,24 +77,46 @@ global; layout(location = 0) in vec4 Position; layout(location = 1) in vec2 TexCoord; layout(location = 0) out vec2 vTexCoord; -layout(location = 1) out vec2 tx_coord; -layout(location = 2) out vec2 tx_per_px; -layout(location = 3) out vec2 tx_to_uv; +layout(location = 1) out vec2 scale_o2i; +layout(location = 2) out vec4 crop; +layout(location = 3) out vec2 tx_coord; +layout(location = 4) out vec2 tx_per_px; +layout(location = 5) out vec2 tx_to_uv; +layout(location = 6) out vec4 input_corners; +layout(location = 7) out vec2 cropped_input_size; void main() { gl_Position = global.MVP * Position; vTexCoord = TexCoord; - const vec2 scale_o2i = scale_o2i(); - tx_coord = (vTexCoord - 0.49999) * scale_o2i + get_input_center(); - tx_per_px = scale_o2i * param.FinalViewportSize.zw; + crop = vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT, param.OS_CROP_BOTTOM, + param.OS_CROP_RIGHT); + scale_o2i = get_scale_o2i( + param.InputSize.xy, param.OutputSize.xy, crop, param.Rotation, + param.CENTER_AFTER_CROPPING, param.FORCE_ASPECT_RATIO, + vec2(param.ASPECT_H, param.ASPECT_V), + vec2(param.FORCE_INTEGER_SCALING_H, param.FORCE_INTEGER_SCALING_V), + /* output_size_is_final_viewport_size = */ false); + tx_coord = o2i(vTexCoord, param.InputSize.xy, crop, param.Rotation, + param.CENTER_AFTER_CROPPING, scale_o2i); + tx_per_px = scale_o2i * param.OutputSize.zw; tx_to_uv = param.InputSize.zw; + + input_corners = get_input_corners(param.InputSize.xy, crop, param.Rotation); + const vec4 rotated_crop = get_rotated_crop(crop, param.Rotation); + cropped_input_size = + param.InputSize.xy - + vec2(rotated_crop.y + rotated_crop.w, rotated_crop.x + rotated_crop.z); } #pragma stage fragment layout(location = 0) in vec2 vTexCoord; -layout(location = 1) in vec2 tx_coord; -layout(location = 2) in vec2 tx_per_px; -layout(location = 3) in vec2 tx_to_uv; +layout(location = 1) in vec2 scale_o2i; +layout(location = 2) in vec4 crop; +layout(location = 3) in vec2 tx_coord; +layout(location = 4) in vec2 tx_per_px; +layout(location = 5) in vec2 tx_to_uv; +layout(location = 6) in vec4 input_corners; +layout(location = 7) in vec2 cropped_input_size; layout(location = 0) out vec4 FragColor; layout(set = 0, binding = 2) uniform sampler2D Input; layout(set = 0, binding = 3) uniform sampler2D Top; @@ -137,68 +163,65 @@ vec3 blend_corner(vec3 a, // The first color to blend } void main() { - if (tx_coord.x < param.OS_CROP_LEFT) { - if (param.EXTEND_H < 0.5) { + const vec2 extend_fill = + get_rotated_size(vec2(param.EXTEND_H, param.EXTEND_V), param.Rotation); + if (tx_coord.x < input_corners.x) { + if (extend_fill.x < 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 (tx_coord.y < param.OS_CROP_TOP) { - if (param.EXTEND_V < 0.5) { + if (tx_coord.y < input_corners.y) { + if (extend_fill.y < 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)); + i2o(input_corners.xy, param.InputSize.xy, crop, param.Rotation, + param.CENTER_AFTER_CROPPING, scale_o2i); 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, - vTexCoord, content_corner, - viewport_corner - content_corner), - 1.0); + FragColor = vec4( + blend_corner(left, top, cropped_input_size.y, + cropped_input_size.x, vTexCoord, content_corner, + viewport_corner - content_corner), + 1.0); FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); - } else if (tx_coord.y < param.InputSize.y - param.OS_CROP_BOTTOM) { + } else if (tx_coord.y < input_corners.w) { // Left bar FragColor = vec4(pow(left, vec3(param.FILL_GAMMA)), 1.0); } else { - if (param.EXTEND_V < 0.5) { + if (extend_fill.y < 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; - const vec2 content_corner = i2o(vec2( - param.OS_CROP_LEFT, param.InputSize.y - param.OS_CROP_BOTTOM)); + const vec2 content_corner = + i2o(input_corners.xw, param.InputSize.xy, crop, param.Rotation, + param.CENTER_AFTER_CROPPING, scale_o2i); 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 = vec4( + blend_corner(left, bottom, cropped_input_size.y, + cropped_input_size.x, vTexCoord, content_corner, + viewport_corner - content_corner), + 1.0); FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); } - } else if (tx_coord.x < param.InputSize.x - param.OS_CROP_RIGHT) { - if (tx_coord.y < param.OS_CROP_TOP) { - if (param.EXTEND_V < 0.5) { + } else if (tx_coord.x < input_corners.z) { + if (tx_coord.y < input_corners.y) { + if (extend_fill.y < 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 (tx_coord.y < param.InputSize.y - param.OS_CROP_BOTTOM) { + } else if (tx_coord.y < input_corners.w) { // Uncropped - if (param.FORCE_INTEGER_SCALING > 0.5) { + if (param.FORCE_INTEGER_SCALING_H > 0.5 && + param.FORCE_INTEGER_SCALING_V > 0.5) { // Do a perfectly sharp (nearest neighbor) sampling. FragColor = vec4( texture(Input, (floor(tx_coord) + 0.5) * param.InputSize.zw) @@ -211,10 +234,10 @@ void main() { FragColor = pixel_aa( Input, tx_per_px, tx_to_uv, tx_coord, param.PIX_AA_SHARP, /* gamma_correct = */ false, param.PIX_AA_SUBPX > 0.5, - param.PIX_AA_SUBPX_BGR > 0.5); + param.PIX_AA_SUBPX_BGR > 0.5, param.Rotation); } } else { - if (param.EXTEND_V < 0.5) { + if (extend_fill.y < 0.5) { FragColor = vec4(0.0, 0.0, 0.0, 1.0); return; } @@ -224,54 +247,47 @@ void main() { FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); } } else { - if (param.EXTEND_H < 0.5) { + if (extend_fill.x < 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 (tx_coord.y < param.OS_CROP_TOP) { - if (param.EXTEND_V < 0.5) { + if (tx_coord.y < input_corners.y) { + if (extend_fill.y < 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)); + const vec2 content_corner = + i2o(input_corners.zy, param.InputSize.xy, crop, param.Rotation, + param.CENTER_AFTER_CROPPING, scale_o2i); 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, - vTexCoord, content_corner, - viewport_corner - content_corner), - 1.0); + FragColor = vec4( + blend_corner(right, top, cropped_input_size.y, + cropped_input_size.x, vTexCoord, content_corner, + viewport_corner - content_corner), + 1.0); FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); - } else if (tx_coord.y < param.InputSize.y - param.OS_CROP_BOTTOM) { + } else if (tx_coord.y < input_corners.w) { // Right bar FragColor = vec4(pow(right, vec3(param.FILL_GAMMA)), 1.0); } else { - if (param.EXTEND_V < 0.5) { + if (extend_fill.y < 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; const vec2 content_corner = - i2o(vec2(param.InputSize.x - param.OS_CROP_RIGHT, - param.InputSize.y - param.OS_CROP_BOTTOM)); + i2o(input_corners.zw, param.InputSize.xy, crop, param.Rotation, + param.CENTER_AFTER_CROPPING, scale_o2i); 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 = vec4( + blend_corner(right, bottom, cropped_input_size.y, + cropped_input_size.x, 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/crop_and_sample_bottom.slang b/border/shaders/average_fill/crop_and_sample_bottom.slang index dac4930..caab0a4 100644 --- a/border/shaders/average_fill/crop_and_sample_bottom.slang +++ b/border/shaders/average_fill/crop_and_sample_bottom.slang @@ -2,13 +2,12 @@ // See compose.slang for copyright and other information. -#include "parameters.slang" - -// clang-format off -#define EFF_CROP_TOP (param.InputSize.y - param.OS_CROP_BOTTOM - param.SAMPLE_SIZE) -#define EFF_CROP_BOTTOM (param.OS_CROP_BOTTOM) -#define EFF_CROP_LEFT (param.OS_CROP_LEFT) -#define EFF_CROP_RIGHT (param.OS_CROP_RIGHT) -// clang-format on +// Crop is rotated already. +vec4 get_effective_corners(vec4 crop, vec4 input_size, float sample_size) { + return input_size.zwzw * vec4(crop.y, // + input_size.y - crop.z - sample_size, // + input_size.x - crop.w, // + input_size.y - crop.z); +} #include "crop_and_sample_common.slang" diff --git a/border/shaders/average_fill/crop_and_sample_common.slang b/border/shaders/average_fill/crop_and_sample_common.slang index 59fc154..3548e9b 100644 --- a/border/shaders/average_fill/crop_and_sample_common.slang +++ b/border/shaders/average_fill/crop_and_sample_common.slang @@ -1,10 +1,11 @@ // See compose.slang for copyright and other information. +#include "../../../misc/shaders/scaling.slang" +#include "parameters.slang" + layout(push_constant) uniform Push { vec4 InputSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; + uint Rotation; float OS_CROP_TOP; float OS_CROP_BOTTOM; float OS_CROP_LEFT; @@ -23,10 +24,13 @@ layout(location = 0) out vec2 vTexCoord; void main() { gl_Position = global.MVP * Position; - vTexCoord = - mix(vec2(EFF_CROP_LEFT, EFF_CROP_TOP) * param.InputSize.zw, - 1.0 - vec2(EFF_CROP_RIGHT, EFF_CROP_BOTTOM) * param.InputSize.zw, - TexCoord); + const vec4 rotated_crop = + get_rotated_crop(vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT, + param.OS_CROP_BOTTOM, param.OS_CROP_RIGHT), + param.Rotation); + const vec4 effective_corners = + get_effective_corners(rotated_crop, param.InputSize, param.SAMPLE_SIZE); + vTexCoord = mix(effective_corners.xy, effective_corners.zw, TexCoord); } #pragma stage fragment diff --git a/border/shaders/average_fill/crop_and_sample_left.slang b/border/shaders/average_fill/crop_and_sample_left.slang index f1edbea..151013c 100644 --- a/border/shaders/average_fill/crop_and_sample_left.slang +++ b/border/shaders/average_fill/crop_and_sample_left.slang @@ -2,13 +2,11 @@ // See compose.slang for copyright and other information. -#include "parameters.slang" - -// clang-format off -#define EFF_CROP_TOP (param.OS_CROP_TOP) -#define EFF_CROP_BOTTOM (param.OS_CROP_BOTTOM) -#define EFF_CROP_LEFT (param.OS_CROP_LEFT) -#define EFF_CROP_RIGHT (param.InputSize.x - param.OS_CROP_LEFT - param.SAMPLE_SIZE) -// clang-format on +vec4 get_effective_corners(vec4 crop, vec4 input_size, float sample_size) { + return input_size.zwzw * vec4(crop.y, // + crop.x, // + crop.y + sample_size, // + input_size.y - crop.z); +} #include "crop_and_sample_common.slang" diff --git a/border/shaders/average_fill/crop_and_sample_right.slang b/border/shaders/average_fill/crop_and_sample_right.slang index caa4443..ef7792c 100644 --- a/border/shaders/average_fill/crop_and_sample_right.slang +++ b/border/shaders/average_fill/crop_and_sample_right.slang @@ -2,13 +2,11 @@ // See compose.slang for copyright and other information. -#include "parameters.slang" - -// clang-format off -#define EFF_CROP_TOP (param.OS_CROP_TOP) -#define EFF_CROP_BOTTOM (param.OS_CROP_BOTTOM) -#define EFF_CROP_LEFT (param.InputSize.x - param.OS_CROP_RIGHT - param.SAMPLE_SIZE) -#define EFF_CROP_RIGHT (param.OS_CROP_RIGHT) -// clang-format on +vec4 get_effective_corners(vec4 crop, vec4 input_size, float sample_size) { + return input_size.zwzw * vec4(input_size.x - crop.w - sample_size, // + crop.x, // + input_size.x - crop.w, // + input_size.y - crop.z); +} #include "crop_and_sample_common.slang" diff --git a/border/shaders/average_fill/crop_and_sample_top.slang b/border/shaders/average_fill/crop_and_sample_top.slang index 2ce3391..c393d44 100644 --- a/border/shaders/average_fill/crop_and_sample_top.slang +++ b/border/shaders/average_fill/crop_and_sample_top.slang @@ -2,13 +2,11 @@ // See compose.slang for copyright and other information. -#include "parameters.slang" - -// clang-format off -#define EFF_CROP_TOP (param.OS_CROP_TOP) -#define EFF_CROP_BOTTOM (param.InputSize.y - param.OS_CROP_TOP - param.SAMPLE_SIZE) -#define EFF_CROP_LEFT (param.OS_CROP_LEFT) -#define EFF_CROP_RIGHT (param.OS_CROP_RIGHT) -// clang-format on +vec4 get_effective_corners(vec4 crop, vec4 input_size, float sample_size) { + return input_size.zwzw * vec4(crop.y, // + crop.x, // + input_size.x - crop.w, // + crop.x + sample_size); +} #include "crop_and_sample_common.slang" diff --git a/border/shaders/average_fill/parameters.slang b/border/shaders/average_fill/parameters.slang index d43b458..6c43ee3 100644 --- a/border/shaders/average_fill/parameters.slang +++ b/border/shaders/average_fill/parameters.slang @@ -1,25 +1,30 @@ // See compose.slang for copyright and other information. // clang-format off -#pragma parameter AVERAGE_FILL_SETTINGS "=== Average fill v1.5 settings ===" 0.0 0.0 1.0 1.0 +#pragma parameter AVERAGE_FILL_SETTINGS "=== Average fill v1.6 settings ===" 0.0 0.0 1.0 1.0 + +#pragma parameter SCALING_SETTINGS "= Scaling parameters =" 0.0 0.0 1.0 1.0 +#pragma parameter FORCE_ASPECT_RATIO "Force aspect ratio" 1.0 0.0 1.0 1.0 +#pragma parameter ASPECT_H "Horizontal aspect ratio before crop (0 = unchanged)" 0.0 0.0 256.0 1.0 +#pragma parameter ASPECT_V "Vertical aspect ratio before crop (0 = unchanged)" 0.0 0.0 256.0 1.0 +#pragma parameter FORCE_INTEGER_SCALING_H "Force integer scaling horizontally" 0.0 0.0 1.0 1.0 +#pragma parameter FORCE_INTEGER_SCALING_V "Force integer scaling vertically" 1.0 0.0 1.0 1.0 + +#pragma parameter CROPPING_SETTINGS "= Cropping parameters =" 0.0 0.0 1.0 1.0 #pragma parameter OS_CROP_TOP "Overscan crop top" 0.0 0.0 1024.0 1.0 #pragma parameter OS_CROP_BOTTOM "Overscan crop bottom" 0.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 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 CENTER_AFTER_CROPPING "Center cropped area" 1.0 0.0 1.0 1.0 +#pragma parameter OTHER_SETTINGS "= Other parameters =" 0.0 0.0 1.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" 1.0 0.0 1.0 1.0 -#pragma parameter ASPECT_H "Horizontal aspect ratio before crop (0 = original)" 0.0 0.0 256.0 1.0 -#pragma parameter ASPECT_V "Vertical aspect ratio before crop (0 = original)" 0.0 0.0 256.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 + +#pragma parameter SAMPLE_SIZE "No. of lines for calculating the average" 8.0 1.0 64.0 1.0 // clang-format on diff --git a/border/shaders/blur_fill/compose.slang b/border/shaders/blur_fill/compose.slang index 75d9a12..8d2ff3b 100644 --- a/border/shaders/blur_fill/compose.slang +++ b/border/shaders/blur_fill/compose.slang @@ -1,7 +1,7 @@ #version 450 /* - Blur fill v1.6 by fishku + Blur fill v1.7 by fishku Copyright (C) 2023 Public domain license (CC0) @@ -27,6 +27,7 @@ strength of the blur. Changelog: + v1.7: Refactor for new scaling library. Add rotation support. v1.6: Optimize. Update to new Pixel AA version. Tune default blur strength. v1.5: Add anti-aliased interpolation for non-integer scaling. v1.4: Fix scaling bugs. @@ -39,23 +40,28 @@ // clang-format off #include "parameters.slang" #include "../../../blurs/shaders/dual_filter/parameters.slang" -#include "../../../pixel-art-scaling/shaders/pixel_aa/shared.slang" +#include "../../../pixel-art-scaling/shaders/pixel_aa/parameters.slang" // clang-format on +#include "../../../misc/shaders/scaling.slang" +#include "../../../pixel-art-scaling/shaders/pixel_aa/shared.slang" + layout(push_constant) uniform Push { vec4 InputSize; vec4 TiledSize; - vec4 FinalViewportSize; + vec4 OutputSize; + uint Rotation; float OS_CROP_TOP; float OS_CROP_BOTTOM; float OS_CROP_LEFT; float OS_CROP_RIGHT; - float CENTER_CROP; + float CENTER_AFTER_CROPPING; float SAMPLE_SIZE; float FORCE_ASPECT_RATIO; float ASPECT_H; float ASPECT_V; - float FORCE_INTEGER_SCALING; + float FORCE_INTEGER_SCALING_H; + float FORCE_INTEGER_SCALING_V; float FILL_GAMMA; // From dual filter blur float BLUR_RADIUS; @@ -66,8 +72,6 @@ layout(push_constant) uniform Push { } param; -#include "scaling.slang" - layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; } global; @@ -78,18 +82,24 @@ layout(location = 0) out vec2 vTexCoord; layout(location = 1) out vec2 tx_coord; layout(location = 2) out vec2 tx_per_px; layout(location = 3) out vec2 tx_to_uv; -layout(location = 4) out vec4 input_extrema; +layout(location = 4) out vec4 input_corners; void main() { gl_Position = global.MVP * Position; vTexCoord = TexCoord; - const vec2 scale_o2i = scale_o2i(); - tx_coord = (vTexCoord - 0.49999) * scale_o2i + get_input_center(); - tx_per_px = scale_o2i * param.FinalViewportSize.zw; + const vec4 crop = vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT, + param.OS_CROP_BOTTOM, param.OS_CROP_RIGHT); + const vec2 scale_o2i = get_scale_o2i( + param.InputSize.xy, param.OutputSize.xy, crop, param.Rotation, + param.CENTER_AFTER_CROPPING, param.FORCE_ASPECT_RATIO, + vec2(param.ASPECT_H, param.ASPECT_V), + vec2(param.FORCE_INTEGER_SCALING_H, param.FORCE_INTEGER_SCALING_V), + /* output_size_is_final_viewport_size = */ false); + tx_coord = o2i(vTexCoord, param.InputSize.xy, crop, param.Rotation, + param.CENTER_AFTER_CROPPING, scale_o2i); + tx_per_px = scale_o2i * param.OutputSize.zw; tx_to_uv = param.InputSize.zw; - input_extrema = vec4(param.OS_CROP_LEFT, param.OS_CROP_TOP, - param.InputSize.x - param.OS_CROP_RIGHT, - param.InputSize.y - param.OS_CROP_BOTTOM); + input_corners = get_input_corners(param.InputSize.xy, crop, param.Rotation); } #pragma stage fragment @@ -97,15 +107,15 @@ layout(location = 0) in vec2 vTexCoord; layout(location = 1) in vec2 tx_coord; layout(location = 2) in vec2 tx_per_px; layout(location = 3) in vec2 tx_to_uv; -layout(location = 4) in vec4 input_extrema; +layout(location = 4) in vec4 input_corners; layout(location = 0) out vec4 FragColor; layout(set = 0, binding = 2) uniform sampler2D Input; layout(set = 0, binding = 3) uniform sampler2D Tiled; layout(set = 0, binding = 4) uniform sampler2D Blurred; void main() { - if (any(lessThan(tx_coord, input_extrema.xy)) || - any(greaterThanEqual(tx_coord, input_extrema.zw))) { + if (any(lessThan(tx_coord, input_corners.xy)) || + any(greaterThanEqual(tx_coord, input_corners.zw))) { if (param.BLUR_RADIUS > 0.0) { // Sample blur. FragColor = vec4( @@ -113,7 +123,7 @@ void main() { 1.0); } else { // Sample tiled pattern. - // Do a sharp (nearest neighbor) resampling. + // Do a perfectly sharp (nearest neighbor) resampling. FragColor = vec4(pow(texture(Tiled, (floor(vTexCoord * param.TiledSize.xy) + 0.5) * @@ -124,7 +134,8 @@ void main() { } } else { // Sample original. - if (param.FORCE_INTEGER_SCALING > 0.5) { + if (param.FORCE_INTEGER_SCALING_H > 0.5 && + param.FORCE_INTEGER_SCALING_V > 0.5) { // Do a perfectly sharp (nearest neighbor) sampling. FragColor = vec4( texture(Input, (floor(tx_coord) + 0.5) * param.InputSize.zw) @@ -137,7 +148,7 @@ void main() { FragColor = pixel_aa( Input, tx_per_px, tx_to_uv, tx_coord, param.PIX_AA_SHARP, /* gamma_correct = */ false, param.PIX_AA_SUBPX > 0.5, - param.PIX_AA_SUBPX_BGR > 0.5); + param.PIX_AA_SUBPX_BGR > 0.5, param.Rotation); } } } diff --git a/border/shaders/blur_fill/parameters.slang b/border/shaders/blur_fill/parameters.slang index 8467850..cc3d59a 100644 --- a/border/shaders/blur_fill/parameters.slang +++ b/border/shaders/blur_fill/parameters.slang @@ -1,24 +1,30 @@ // See compose.slang for copyright and other information. // clang-format off -#pragma parameter BLUR_FILL_SETTINGS "=== Blur fill v1.6 settings ===" 0.0 0.0 1.0 1.0 +#pragma parameter BLUR_FILL_SETTINGS "=== Blur fill v1.7 settings ===" 0.0 0.0 1.0 1.0 + +#pragma parameter SCALING_SETTINGS "= Scaling parameters =" 0.0 0.0 1.0 1.0 +#pragma parameter FORCE_ASPECT_RATIO "Force aspect ratio" 1.0 0.0 1.0 1.0 +#pragma parameter ASPECT_H "Horizontal aspect ratio before crop (0 = unchanged)" 0.0 0.0 256.0 1.0 +#pragma parameter ASPECT_V "Vertical aspect ratio before crop (0 = unchanged)" 0.0 0.0 256.0 1.0 +#pragma parameter FORCE_INTEGER_SCALING_H "Force integer scaling horizontally" 0.0 0.0 1.0 1.0 +#pragma parameter FORCE_INTEGER_SCALING_V "Force integer scaling vertically" 1.0 0.0 1.0 1.0 + +#pragma parameter CROPPING_SETTINGS "= Cropping parameters =" 0.0 0.0 1.0 1.0 #pragma parameter OS_CROP_TOP "Overscan crop top" 0.0 0.0 1024.0 1.0 #pragma parameter OS_CROP_BOTTOM "Overscan crop bottom" 0.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 CENTER_CROP "Center cropped area" 1.0 0.0 1.0 1.0 +#pragma parameter CENTER_AFTER_CROPPING "Center cropped area" 1.0 0.0 1.0 1.0 -#pragma parameter SAMPLE_SIZE "No. of lines for rendering the blur" 16.0 1.0 1024.0 1.0 +#pragma parameter OTHER_SETTINGS "= Other parameters =" 0.0 0.0 1.0 1.0 +#pragma parameter EXTEND_H "Extend the fill horizontally" 0.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 BLUR_EXTEND_H "Extend the blur horizontally" 1.0 0.0 1.0 1.0 -#pragma parameter BLUR_EXTEND_V "Extend the blur vertically" 1.0 0.0 1.0 1.0 #pragma parameter MIRROR_BLUR "Mirror the blur" 0.0 0.0 1.0 1.0 -#pragma parameter FORCE_ASPECT_RATIO "Force aspect ratio" 1.0 0.0 1.0 1.0 -#pragma parameter ASPECT_H "Horizontal aspect ratio before crop (0 = original)" 0.0 0.0 256.0 1.0 -#pragma parameter ASPECT_V "Vertical aspect ratio before crop (0 = original)" 0.0 0.0 256.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.4 0.5 2.0 0.1 + +#pragma parameter SAMPLE_SIZE "No. of lines for rendering the blur" 16.0 1.0 1024.0 1.0 // clang-format on diff --git a/border/shaders/blur_fill/render_sampling_areas.slang b/border/shaders/blur_fill/render_sampling_areas.slang index 0c351eb..bfd87d9 100644 --- a/border/shaders/blur_fill/render_sampling_areas.slang +++ b/border/shaders/blur_fill/render_sampling_areas.slang @@ -2,29 +2,30 @@ // See compose.slang for copyright and other information. +#include "../../../misc/shaders/scaling.slang" #include "parameters.slang" layout(push_constant) uniform Push { vec4 InputSize; vec4 FinalViewportSize; + uint Rotation; float OS_CROP_TOP; float OS_CROP_BOTTOM; float OS_CROP_LEFT; float OS_CROP_RIGHT; - float CENTER_CROP; + float CENTER_AFTER_CROPPING; float SAMPLE_SIZE; - float BLUR_EXTEND_H; - float BLUR_EXTEND_V; + float EXTEND_H; + float EXTEND_V; float MIRROR_BLUR; float FORCE_ASPECT_RATIO; float ASPECT_H; float ASPECT_V; - float FORCE_INTEGER_SCALING; + float FORCE_INTEGER_SCALING_H; + float FORCE_INTEGER_SCALING_V; } param; -#include "scaling.slang" - layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; } global; @@ -32,14 +33,29 @@ global; layout(location = 0) in vec4 Position; layout(location = 1) in vec2 TexCoord; layout(location = 0) out vec2 vTexCoord; +layout(location = 1) out vec2 tx_coord; +layout(location = 2) out vec4 input_corners; void main() { gl_Position = global.MVP * Position; vTexCoord = TexCoord; + const vec4 crop = vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT, + param.OS_CROP_BOTTOM, param.OS_CROP_RIGHT); + const vec2 scale_o2i = get_scale_o2i( + param.InputSize.xy, param.FinalViewportSize.xy, crop, param.Rotation, + param.CENTER_AFTER_CROPPING, param.FORCE_ASPECT_RATIO, + vec2(param.ASPECT_H, param.ASPECT_V), + vec2(param.FORCE_INTEGER_SCALING_H, param.FORCE_INTEGER_SCALING_V), + /* output_size_is_final_viewport_size = */ true); + tx_coord = o2i(vTexCoord, param.InputSize.xy, crop, param.Rotation, + param.CENTER_AFTER_CROPPING, scale_o2i); + input_corners = get_input_corners(param.InputSize.xy, crop, param.Rotation); } #pragma stage fragment layout(location = 0) in vec2 vTexCoord; +layout(location = 1) in vec2 tx_coord; +layout(location = 2) in vec4 input_corners; layout(location = 0) out vec4 FragColor; layout(set = 0, binding = 2) uniform sampler2D Source; @@ -62,24 +78,24 @@ float mirrored_repeat(float w, float x) { return period % 2 == 1 != x > 0.0 ? phase : w - phase; } -float extend_left(vec2 coord, vec4 input_extrema) { - return input_extrema.x + - mirrored_repeat(param.SAMPLE_SIZE, coord.x - input_extrema.x); +float extend_left(vec2 coord, vec4 input_corners) { + return input_corners.x + + mirrored_repeat(param.SAMPLE_SIZE, coord.x - input_corners.x); } -float extend_right(vec2 coord, vec4 input_extrema) { - return input_extrema.z - - mirrored_repeat(param.SAMPLE_SIZE, input_extrema.z - coord.x); +float extend_right(vec2 coord, vec4 input_corners) { + return input_corners.z - + mirrored_repeat(param.SAMPLE_SIZE, input_corners.z - coord.x); } -float extend_top(vec2 coord, vec4 input_extrema) { - return input_extrema.y + - mirrored_repeat(param.SAMPLE_SIZE, coord.y - input_extrema.y); +float extend_top(vec2 coord, vec4 input_corners) { + return input_corners.y + + mirrored_repeat(param.SAMPLE_SIZE, coord.y - input_corners.y); } -float extend_bottom(vec2 coord, vec4 input_extrema) { - return input_extrema.w - - mirrored_repeat(param.SAMPLE_SIZE, input_extrema.w - coord.y); +float extend_bottom(vec2 coord, vec4 input_corners) { + return input_corners.w - + mirrored_repeat(param.SAMPLE_SIZE, input_corners.w - coord.y); } // This function samples in a very specific way which is the foundation for @@ -92,123 +108,128 @@ float extend_bottom(vec2 coord, vec4 input_extrema) { // - If the coordinate is further inside than the frame band, a mirrored // repeating sample is returned. The side of the frame that is sampled is given // by the one that is closest to the sampled point. -vec3 sample_mirrored_frame(sampler2D tex, vec2 coord, vec4 input_extrema) { - if (coord.x < input_extrema.x) { - if (param.BLUR_EXTEND_H < 0.5) { +vec3 sample_mirrored_frame(sampler2D tex, vec2 tx_coord, vec4 input_corners) { + const vec2 extend_fill = + get_rotated_size(vec2(param.EXTEND_H, param.EXTEND_V), param.Rotation); + if (tx_coord.x < input_corners.x) { + if (extend_fill.x < 0.5) { return vec3(0.0); } - if (coord.y < input_extrema.y) { + if (tx_coord.y < input_corners.y) { // Top left corner extension - if (param.BLUR_EXTEND_V < 0.5) { + if (extend_fill.y < 0.5) { return vec3(0.0); } - return texture(tex, vec2(extend_left(coord, input_extrema), - extend_top(coord, input_extrema)) * + return texture(tex, vec2(extend_left(tx_coord, input_corners), + extend_top(tx_coord, input_corners)) * param.InputSize.zw) .rgb; - } else if (coord.y < input_extrema.w) { + } else if (tx_coord.y < input_corners.w) { // Left extension - return texture(tex, - vec2(extend_left(coord, input_extrema), coord.y) * - param.InputSize.zw) + return texture(tex, vec2(extend_left(tx_coord, input_corners), + tx_coord.y) * + param.InputSize.zw) .rgb; } else { // Bottom left corner extension - if (param.BLUR_EXTEND_V < 0.5) { + if (extend_fill.y < 0.5) { return vec3(0.0); } - return texture(tex, vec2(extend_left(coord, input_extrema), - extend_bottom(coord, input_extrema)) * + return texture(tex, vec2(extend_left(tx_coord, input_corners), + extend_bottom(tx_coord, input_corners)) * param.InputSize.zw) .rgb; } - } else if (coord.x < input_extrema.z) { - if (coord.y < input_extrema.y) { - if (param.BLUR_EXTEND_V < 0.5) { + } else if (tx_coord.x < input_corners.z) { + if (tx_coord.y < input_corners.y) { + if (extend_fill.y < 0.5) { return vec3(0.0); } // Top extension - return texture(tex, - vec2(coord.x, extend_top(coord, input_extrema)) * - param.InputSize.zw) + return texture(tex, vec2(tx_coord.x, + extend_top(tx_coord, input_corners)) * + param.InputSize.zw) .rgb; - } else if (coord.y < input_extrema.w) { - const vec4 inner_extrema = - input_extrema + vec4(param.SAMPLE_SIZE, param.SAMPLE_SIZE, + } else if (tx_coord.y < input_corners.w) { + const vec4 inner_corners = + input_corners + vec4(param.SAMPLE_SIZE, param.SAMPLE_SIZE, -param.SAMPLE_SIZE, -param.SAMPLE_SIZE); - if (any(lessThan(coord, inner_extrema.xy)) || - any(greaterThanEqual(coord, inner_extrema.zw))) { + if (any(lessThan(tx_coord, inner_corners.xy)) || + any(greaterThanEqual(tx_coord, inner_corners.zw))) { // In frame band - return texture(tex, coord * param.InputSize.zw).rgb; + return texture(tex, tx_coord * param.InputSize.zw).rgb; } // Innermost -- mirrored repeat sampling from nearest side - const vec4 distances = - vec4(coord.x - inner_extrema.x, inner_extrema.z - coord.x, - coord.y - inner_extrema.y, inner_extrema.w - coord.y); + const vec4 distances = vec4( + tx_coord.x - inner_corners.x, inner_corners.z - tx_coord.x, + tx_coord.y - inner_corners.y, inner_corners.w - tx_coord.y); switch (argmin(distances)) { case 0: // left - return texture(tex, vec2(extend_left(coord, input_extrema), - coord.y) * - param.InputSize.zw) + return texture(tex, + vec2(extend_left(tx_coord, input_corners), + tx_coord.y) * + param.InputSize.zw) .rgb; case 1: // right - return texture(tex, vec2(extend_right(coord, input_extrema), - coord.y) * - param.InputSize.zw) + return texture(tex, + vec2(extend_right(tx_coord, input_corners), + tx_coord.y) * + param.InputSize.zw) .rgb; case 2: // top - return texture(tex, vec2(coord.x, - extend_top(coord, input_extrema)) * - param.InputSize.zw) + return texture(tex, + vec2(tx_coord.x, + extend_top(tx_coord, input_corners)) * + param.InputSize.zw) .rgb; case 3: default: // bottom - return texture(tex, - vec2(coord.x, - extend_bottom(coord, input_extrema)) * - param.InputSize.zw) + return texture(tex, vec2(tx_coord.x, + extend_bottom(tx_coord, + input_corners)) * + param.InputSize.zw) .rgb; } } else { - if (param.BLUR_EXTEND_V < 0.5) { + if (extend_fill.y < 0.5) { return vec3(0.0); } // Bottom extension - return texture(tex, - vec2(coord.x, extend_bottom(coord, input_extrema)) * - param.InputSize.zw) + return texture(tex, vec2(tx_coord.x, + extend_bottom(tx_coord, input_corners)) * + param.InputSize.zw) .rgb; } } else { - if (param.BLUR_EXTEND_H < 0.5) { + if (extend_fill.x < 0.5) { return vec3(0.0); } - if (coord.y < input_extrema.y) { + if (tx_coord.y < input_corners.y) { // Top right corner extension - if (param.BLUR_EXTEND_V < 0.5) { + if (extend_fill.y < 0.5) { return vec3(0.0); } - return texture(tex, vec2(extend_right(coord, input_extrema), - extend_top(coord, input_extrema)) * + return texture(tex, vec2(extend_right(tx_coord, input_corners), + extend_top(tx_coord, input_corners)) * param.InputSize.zw) .rgb; - } else if (coord.y < input_extrema.w) { + } else if (tx_coord.y < input_corners.w) { // Right extension - return texture(tex, - vec2(extend_right(coord, input_extrema), coord.y) * - param.InputSize.zw) + return texture(tex, vec2(extend_right(tx_coord, input_corners), + tx_coord.y) * + param.InputSize.zw) .rgb; } else { // Bottom right corner extension - if (param.BLUR_EXTEND_V < 0.5) { + if (extend_fill.y < 0.5) { return vec3(0.0); } - return texture(tex, vec2(extend_right(coord, input_extrema), - extend_bottom(coord, input_extrema)) * + return texture(tex, vec2(extend_right(tx_coord, input_corners), + extend_bottom(tx_coord, input_corners)) * param.InputSize.zw) .rgb; } @@ -216,11 +237,6 @@ vec3 sample_mirrored_frame(sampler2D tex, vec2 coord, vec4 input_extrema) { } void main() { - const vec2 pixel_coord = o2i(vTexCoord); - FragColor = vec4( - sample_mirrored_frame(Source, pixel_coord, - vec4(param.OS_CROP_LEFT, param.OS_CROP_TOP, - param.InputSize.x - param.OS_CROP_RIGHT, - param.InputSize.y - param.OS_CROP_BOTTOM)), - 1.0); + FragColor = + vec4(sample_mirrored_frame(Source, tx_coord, input_corners), 1.0); } diff --git a/border/shaders/blur_fill/scaling.slang b/border/shaders/blur_fill/scaling.slang deleted file mode 100644 index de0c9c1..0000000 --- a/border/shaders/blur_fill/scaling.slang +++ /dev/null @@ -1,64 +0,0 @@ -// See compose.slang for copyright and other information. - -void apply_integer_scaling(inout float x) { - if (param.FORCE_INTEGER_SCALING > 0.5 && x > 1.0) { - x = floor(x); - } -} - -// Scaling from unit output to pixel input space. -vec2 scale_o2i() { - // Pixels in input coord. space, after cropping. - const vec2 eff_input_res = - param.InputSize.xy - - (param.CENTER_CROP > 0.5 - ? vec2(param.OS_CROP_LEFT + param.OS_CROP_RIGHT, - param.OS_CROP_TOP + param.OS_CROP_BOTTOM) - : 2 * vec2(min(param.OS_CROP_LEFT, param.OS_CROP_RIGHT), - min(param.OS_CROP_TOP, param.OS_CROP_BOTTOM))); - // Aspect ratio before cropping. - // lambda_1 * input_pixels.x, lambda_2 * input_pixels.y, - // possibly corrected for forced aspect ratio - const vec2 eff_aspect = - (param.FORCE_ASPECT_RATIO < 0.5 - ? param.FinalViewportSize.xy * param.InputSize.yx - : (param.ASPECT_H < 0.5 || param.ASPECT_V < 0.5 - ? vec2(1.0) - : vec2(param.ASPECT_H, param.ASPECT_V) * - param.InputSize.yx)); - float scale_x, scale_y; - if (param.FinalViewportSize.x / (eff_input_res.x * eff_aspect.x) < - param.FinalViewportSize.y / (eff_input_res.y * eff_aspect.y)) { - // Scale will be limited by width. Calc x scale, then derive y scale - // using aspect ratio. - scale_x = param.FinalViewportSize.x / eff_input_res.x; - apply_integer_scaling(scale_x); - scale_y = scale_x * eff_aspect.y / eff_aspect.x; - apply_integer_scaling(scale_y); - } else { - // Scale will be limited by height. - scale_y = param.FinalViewportSize.y / eff_input_res.y; - apply_integer_scaling(scale_y); - scale_x = scale_y * eff_aspect.x / eff_aspect.y; - apply_integer_scaling(scale_x); - } - return param.FinalViewportSize.xy / vec2(scale_x, scale_y); -} - -// Get adjusted center in input pixel coordinate system. -vec2 get_input_center() { - return param.CENTER_CROP > 0.5 - ? 0.5 * 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) * param.InputSize.xy; -} - -// From unit output to pixel input space. -// 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) { return (x - 0.49999) * scale_o2i() + get_input_center(); } - -// From pixel input to unit output space. -vec2 i2o(vec2 x) { return (x - get_input_center()) / scale_o2i() + 0.49999; } diff --git a/misc/shaders/scaling.slang b/misc/shaders/scaling.slang new file mode 100644 index 0000000..e4146e3 --- /dev/null +++ b/misc/shaders/scaling.slang @@ -0,0 +1,163 @@ +/* + Scaling library v1.0 by fishku + Copyright (C) 2023 + Public domain license (CC0) + + This file acts like a library and should be included in another shader to be + used. For example usages, see the border/blur_fill shaders. + + It's recommended to use these functions in the vertex shader pass, and pass + the data to the fragment pass. + + Features: + - Cropping on each side + - Centering of image after crop has been applied + - Forcing of a certain aspect ratio + - Forcing of either vert. or horiz. integer scaling, or both + - Rotation support (0, 90, 180, 270 degrees). + + Refactored from the version that used to be in the blur_fill shader. + + Changelog: + v1.0: Initial conversion from blur_fill release. Add rotation support. +*/ + +vec4 get_rotated_crop(vec4 crop, uint rotation) { + switch (rotation) { + case 0: + default: + return crop; + case 1: + return crop.yzwx; + case 2: + return crop.zwxy; + case 3: + return crop.wxyz; + } +} + +vec2 get_rotated_size(vec2 x, uint rotation) { + switch (rotation) { + case 0: + case 2: + default: + return x; + case 1: + case 3: + return x.yx; + } +} + +// Get 2 corners of input in texel space, spanning the input image. +// corners.x and .y define the top-left corner, corners.z and .w define the +// bottom-right corner. +vec4 get_input_corners(vec2 input_size, vec4 crop, uint rotation) { + crop = get_rotated_crop(crop, rotation); + return vec4(crop.y, crop.x, input_size.x - crop.w, input_size.y - crop.z); +} + +// Get adjusted center in input pixel coordinate system. +vec2 get_input_center(vec2 input_size, vec4 crop, uint rotation, + float center_after_cropping) { + crop = get_rotated_crop(crop, rotation); + return center_after_cropping > 0.5 + ? 0.5 * vec2(crop.y + input_size.x - crop.w, + crop.x + input_size.y - crop.z) + : vec2(0.49999) * input_size; +} + +// Scaling from unit output to pixel input space. +vec2 get_scale_o2i(vec2 input_size, vec2 output_size, vec4 crop, uint rotation, + float center_after_cropping, float force_aspect_ratio, + vec2 aspect, vec2 force_integer_scaling, + bool output_size_is_final_viewport_size) { + crop = get_rotated_crop(crop, rotation); + if (output_size_is_final_viewport_size) { + output_size = get_rotated_size(output_size, rotation); + } + aspect = get_rotated_size(aspect, rotation); + // Aspect ratio before cropping. + // lambda_1 * input_pixels.x, lambda_2 * input_pixels.y, + // possibly corrected for forced aspect ratio + aspect = (force_aspect_ratio < 0.5 + ? output_size * input_size.yx + : (aspect.x < 0.5 || aspect.y < 0.5 + ? vec2(1.0) + : vec2(aspect.x, aspect.y) * input_size.yx)); + // Pixels in input coord. space, after cropping. + input_size = input_size - + (center_after_cropping > 0.5 + ? vec2(crop.y + crop.w, crop.x + crop.z) + : 2.0 * vec2(min(crop.y, crop.w), min(crop.x, crop.z))); + + force_integer_scaling = get_rotated_size(force_integer_scaling, rotation); + float scale_x, scale_y; + if (output_size.x / (input_size.x * aspect.x) < + output_size.y / (input_size.y * aspect.y)) { + // Scale will be limited by width. Calc x scale, then derive y scale + // using aspect ratio. + scale_x = output_size.x / input_size.x; + if (force_integer_scaling.x > 0.5 && scale_x > 1.0) { + scale_x = floor(scale_x); + } + scale_y = scale_x * aspect.y / aspect.x; + if (force_integer_scaling.y > 0.5 && scale_y > 1.0) { + scale_y = floor(scale_y); + } + } else { + // Scale will be limited by height. + scale_y = output_size.y / input_size.y; + if (force_integer_scaling.y > 0.5 && scale_y > 1.0) { + scale_y = floor(scale_y); + } + scale_x = scale_y * aspect.x / aspect.y; + if (force_integer_scaling.x > 0.5 && scale_x > 1.0) { + scale_x = floor(scale_x); + } + } + return output_size.xy / vec2(scale_x, scale_y); +} + +// From unit output to pixel input space. +// coord_in_input_space = o2i(coord_in_output_space) +// This is used to sample from the input texture in the output pass. +// Version where scale is passed in. +vec2 o2i(vec2 x, vec2 input_size, vec4 crop, uint rotation, + float center_after_cropping, vec2 scale_o2i) { + return (x - 0.49999) * scale_o2i + + get_input_center(input_size, crop, rotation, center_after_cropping); +} + +// Version that computes scale. +vec2 o2i(vec2 x, vec2 input_size, vec2 output_size, vec4 crop, uint rotation, + float center_after_cropping, float force_aspect_ratio, vec2 aspect, + vec2 force_integer_scaling, bool output_size_is_final_viewport_size) { + return o2i(x, input_size, crop, rotation, center_after_cropping, + get_scale_o2i(input_size, output_size, crop, rotation, + center_after_cropping, force_aspect_ratio, aspect, + force_integer_scaling, + output_size_is_final_viewport_size)); +} + +// From pixel input to unit output space. +// Version where scale is passed in. +vec2 i2o(vec2 x, vec2 input_size, vec4 crop, uint rotation, + float center_after_cropping, vec2 scale_o2i) { + return (x - get_input_center(input_size, crop, rotation, + center_after_cropping)) / + scale_o2i + + 0.49999; +} + +// Version that computes scale. +vec2 i2o(vec2 x, vec2 input_size, vec2 output_size, vec4 crop, uint rotation, + float center_after_cropping, float force_aspect_ratio, vec2 aspect, + vec2 force_integer_scaling, bool output_size_is_final_viewport_size) { + return (x - get_input_center(input_size, crop, rotation, + center_after_cropping)) / + get_scale_o2i(input_size, output_size, crop, rotation, + center_after_cropping, force_aspect_ratio, aspect, + force_integer_scaling, + output_size_is_final_viewport_size) + + 0.49999; +} diff --git a/pixel-art-scaling/shaders/pixel_aa/parameters.slang b/pixel-art-scaling/shaders/pixel_aa/parameters.slang new file mode 100644 index 0000000..7862c68 --- /dev/null +++ b/pixel-art-scaling/shaders/pixel_aa/parameters.slang @@ -0,0 +1,9 @@ +// See pixel_aa.slang for copyright and other information. + +// clang-format off +#pragma parameter PIX_AA_SETTINGS "=== Pixel AA v1.3 settings ===" 0.0 0.0 1.0 1.0 +#pragma parameter PIX_AA_SHARP "Pixel AA sharpening amount" 1.5 0.0 2.0 0.05 +#pragma parameter PIX_AA_GAMMA "Enable gamma-correct blending" 1.0 0.0 1.0 1.0 +#pragma parameter PIX_AA_SUBPX "Enable subpixel AA" 0.0 0.0 1.0 1.0 +#pragma parameter PIX_AA_SUBPX_BGR "Use BGR subpx. instead of RGB" 0.0 0.0 1.0 1.0 +// clang-format on diff --git a/pixel-art-scaling/shaders/pixel_aa/pixel_aa.slang b/pixel-art-scaling/shaders/pixel_aa/pixel_aa.slang index 14545dd..f9c141f 100644 --- a/pixel-art-scaling/shaders/pixel_aa/pixel_aa.slang +++ b/pixel-art-scaling/shaders/pixel_aa/pixel_aa.slang @@ -1,7 +1,7 @@ #version 450 /* - Pixel AA v1.2 by fishku + Pixel AA v1.3 by fishku Copyright (C) 2023 Public domain license (CC0) @@ -24,15 +24,20 @@ subpixel anti-aliasing, results are identical to the "pixellate" shader. Changelog: + v1.3: Account for screen rotation in subpixel sampling. v1.2: Optimize and simplify algorithm. Enable sharpness < 1.0. Fix subpixel sampling bug. v1.1: Better subpixel sampling. v1.0: Initial release. */ +#include "parameters.slang" +#include "shared.slang" + layout(push_constant) uniform Push { vec4 SourceSize; vec4 OutputSize; + uint Rotation; float PIX_AA_SHARP; float PIX_AA_GAMMA; float PIX_AA_SUBPX; @@ -64,11 +69,9 @@ layout(location = 2) in vec2 tx_to_uv; layout(location = 0) out vec4 FragColor; layout(set = 0, binding = 2) uniform sampler2D Source; -#include "shared.slang" - void main() { FragColor = pixel_aa(Source, tx_per_px, tx_to_uv, tx_coord, param.PIX_AA_SHARP, param.PIX_AA_GAMMA > 0.5, param.PIX_AA_SUBPX > 0.5, - param.PIX_AA_SUBPX_BGR > 0.5); + param.PIX_AA_SUBPX_BGR > 0.5, param.Rotation); } diff --git a/pixel-art-scaling/shaders/pixel_aa/shared.slang b/pixel-art-scaling/shaders/pixel_aa/shared.slang index ae09acd..7b2ed26 100644 --- a/pixel-art-scaling/shaders/pixel_aa/shared.slang +++ b/pixel-art-scaling/shaders/pixel_aa/shared.slang @@ -1,13 +1,5 @@ // See pixel_aa.slang for copyright and other information. -// clang-format off -#pragma parameter PIX_AA_SETTINGS "=== Pixel AA v1.2 settings ===" 0.0 0.0 1.0 1.0 -#pragma parameter PIX_AA_SHARP "Pixel AA sharpening amount" 1.5 0.0 2.0 0.05 -#pragma parameter PIX_AA_GAMMA "Enable gamma-correct blending" 1.0 0.0 1.0 1.0 -#pragma parameter PIX_AA_SUBPX "Enable subpixel AA" 0.0 0.0 1.0 1.0 -#pragma parameter PIX_AA_SUBPX_BGR "Use BGR subpx. instead of RGB" 0.0 0.0 1.0 1.0 -// clang-format on - // Similar to smoothstep, but has a configurable slope at x = 0.5. // Original smoothstep has a slope of 1.5 at x = 0.5 #define INSTANTIATE_SLOPESTEP(T) \ @@ -66,13 +58,17 @@ vec3 sample_aa(sampler2D tex, vec2 tx_per_px, vec2 tx_to_uv, vec2 tx_coord, // interpolation. vec4 pixel_aa(sampler2D tex, vec2 tx_per_px, vec2 tx_to_uv, vec2 tx_coord, float sharpness, bool gamma_correct, bool sample_subpx, - bool subpx_bgr) { + bool subpx_bgr, uint rotation) { if (sample_subpx) { // Subpixel sampling: Shift the sampling by 1/3rd of an output pixel for // each subpixel, assuming that the output size is at monitor // resolution. + // Compensate for possible rotation of the screen in certain cores. + const vec2 rotation_correction[] = {vec2(1.0, 0.0), vec2(0.0, 1.0), + vec2(-1.0, 0.0), vec2(0.0, -1.0)}; const vec2 sub_tx_offset = - vec2(tx_per_px.x / 3.0 * (subpx_bgr ? -1.0 : 1.0), 0.0); + tx_per_px / 3.0 * + rotation_correction[(rotation + int(subpx_bgr) * 2) % 4]; vec3 res; for (int i = -1; i < 2; ++i) { res[i + 1] = sample_aa(tex, tx_per_px, tx_to_uv,