Fix input TF bug & large refactoring; Add box filter AA (#613)

* Initial refactoring, pixel AA xform and bf xform still show the problem

* Heavy refactoring and debugging; bf xform first working version

* Clean up code

* Fix pixel aa xform

* Fix average fill; Slight cleanup

* Remove special FinalViewportScaling handling in input_xf; Fix blur fill

* Bump version
This commit is contained in:
fishcu 2024-07-12 00:02:31 +02:00 committed by GitHub
parent 5e702cfdc6
commit 05a41341be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 283 additions and 139 deletions

View File

@ -1,8 +1,8 @@
#version 450 #version 450
/* /*
Average fill v1.8 by fishku Average fill v1.9 by fishku
Copyright (C) 2023 Copyright (C) 2023-2024
Public domain license (CC0) Public domain license (CC0)
This shader preset allows cropping the image on any side, and filling the This shader preset allows cropping the image on any side, and filling the
@ -27,6 +27,7 @@
3 = Smooth angle-based blending 3 = Smooth angle-based blending
Changelog: Changelog:
v1.9: Update input transform library.
v1.8: Add shift option from input transform library. v1.8: Add shift option from input transform library.
v1.7: Add overscale option from crop and scale library. v1.7: Add overscale option from crop and scale library.
v1.6: Refactor for new scaling library. Add rotation support. v1.6: Refactor for new scaling library. Add rotation support.
@ -85,32 +86,33 @@ global;
#pragma stage vertex #pragma stage vertex
layout(location = 0) in vec4 Position; layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord; layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord; layout(location = 0) out vec4 input_corners;
layout(location = 1) out vec2 scale_o2i; layout(location = 1) out vec2 vTexCoord;
layout(location = 2) out vec4 crop; layout(location = 2) out vec2 scale_i2o;
layout(location = 3) out vec2 shift; layout(location = 3) out vec2 input_center;
layout(location = 4) out vec2 tx_coord; layout(location = 4) out vec2 tx_coord;
layout(location = 5) out vec2 tx_per_px; layout(location = 5) out vec2 tx_per_px;
layout(location = 6) out vec2 tx_to_uv; layout(location = 6) out vec2 tx_to_uv;
layout(location = 7) out vec4 input_corners; layout(location = 7) out vec2 cropped_input_size;
layout(location = 8) out vec2 cropped_input_size;
void main() { void main() {
gl_Position = global.MVP * Position; gl_Position = global.MVP * Position;
vTexCoord = TexCoord; vTexCoord = TexCoord;
crop = vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT, param.OS_CROP_BOTTOM, const vec4 crop = vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT,
param.OS_CROP_RIGHT); param.OS_CROP_BOTTOM, param.OS_CROP_RIGHT);
scale_o2i = get_scale_o2i( scale_i2o = get_scale_i2o(
param.InputSize.xy, param.OutputSize.xy, crop, param.Rotation, param.InputSize.xy, param.OutputSize.xy, crop, param.Rotation,
param.CENTER_AFTER_CROPPING, param.FORCE_ASPECT_RATIO, param.CENTER_AFTER_CROPPING, param.FORCE_ASPECT_RATIO,
vec2(param.ASPECT_H, param.ASPECT_V), vec2(param.ASPECT_H, param.ASPECT_V),
vec2(param.FORCE_INTEGER_SCALING_H, param.FORCE_INTEGER_SCALING_V), vec2(param.FORCE_INTEGER_SCALING_H, param.FORCE_INTEGER_SCALING_V),
param.OVERSCALE, param.OVERSCALE);
/* output_size_is_final_viewport_size = */ false); const vec2 shift = vec2(param.SHIFT_H, param.SHIFT_V);
shift = vec2(param.SHIFT_H, param.SHIFT_V); input_center = get_input_center(param.InputSize.xy, param.OutputSize.xy,
tx_coord = o2i(vTexCoord, param.InputSize.xy, crop, shift, param.Rotation, scale_i2o, crop, shift, param.Rotation,
param.CENTER_AFTER_CROPPING, scale_o2i); param.CENTER_AFTER_CROPPING);
tx_per_px = scale_o2i * param.OutputSize.zw; tx_coord = transform(TexCoord, vec2(0.5), param.OutputSize.xy / scale_i2o,
input_center);
tx_per_px = 1.0 / scale_i2o;
tx_to_uv = param.InputSize.zw; tx_to_uv = param.InputSize.zw;
input_corners = get_input_corners(param.InputSize.xy, crop, param.Rotation); input_corners = get_input_corners(param.InputSize.xy, crop, param.Rotation);
@ -121,15 +123,14 @@ void main() {
} }
#pragma stage fragment #pragma stage fragment
layout(location = 0) in vec2 vTexCoord; layout(location = 0) in vec4 input_corners;
layout(location = 1) in vec2 scale_o2i; layout(location = 1) in vec2 vTexCoord;
layout(location = 2) in vec4 crop; layout(location = 2) in vec2 scale_i2o;
layout(location = 3) in vec2 shift; layout(location = 3) in vec2 input_center;
layout(location = 4) in vec2 tx_coord; layout(location = 4) in vec2 tx_coord;
layout(location = 5) in vec2 tx_per_px; layout(location = 5) in vec2 tx_per_px;
layout(location = 6) in vec2 tx_to_uv; layout(location = 6) in vec2 tx_to_uv;
layout(location = 7) in vec4 input_corners; layout(location = 7) in vec2 cropped_input_size;
layout(location = 8) in vec2 cropped_input_size;
layout(location = 0) out vec4 FragColor; layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Input; layout(set = 0, binding = 2) uniform sampler2D Input;
layout(set = 0, binding = 3) uniform sampler2D Top; layout(set = 0, binding = 3) uniform sampler2D Top;
@ -192,8 +193,8 @@ void main() {
// Top left corner // Top left corner
const vec3 top = textureLod(Top, vec2(0.5), BIG_NUMBER).rgb; const vec3 top = textureLod(Top, vec2(0.5), BIG_NUMBER).rgb;
const vec2 content_corner = const vec2 content_corner =
i2o(input_corners.xy, param.InputSize.xy, crop, shift, transform(input_corners.xy, input_center,
param.Rotation, param.CENTER_AFTER_CROPPING, scale_o2i); scale_i2o / param.OutputSize.xy, vec2(0.5));
const vec2 viewport_corner = vec2(0.0, 0.0); const vec2 viewport_corner = vec2(0.0, 0.0);
FragColor = vec4( FragColor = vec4(
blend_corner(left, top, cropped_input_size.y, blend_corner(left, top, cropped_input_size.y,
@ -212,8 +213,8 @@ void main() {
// Bottom left corner // Bottom left corner
const vec3 bottom = textureLod(Bottom, vec2(0.5), BIG_NUMBER).rgb; const vec3 bottom = textureLod(Bottom, vec2(0.5), BIG_NUMBER).rgb;
const vec2 content_corner = const vec2 content_corner =
i2o(input_corners.xw, param.InputSize.xy, crop, shift, transform(input_corners.xw, input_center,
param.Rotation, param.CENTER_AFTER_CROPPING, scale_o2i); scale_i2o / param.OutputSize.xy, vec2(0.5));
const vec2 viewport_corner = vec2(0.0, 1.0); const vec2 viewport_corner = vec2(0.0, 1.0);
FragColor = vec4( FragColor = vec4(
blend_corner(left, bottom, cropped_input_size.y, blend_corner(left, bottom, cropped_input_size.y,
@ -273,8 +274,8 @@ void main() {
// Top right corner // Top right corner
const vec3 top = textureLod(Top, vec2(0.5), BIG_NUMBER).rgb; const vec3 top = textureLod(Top, vec2(0.5), BIG_NUMBER).rgb;
const vec2 content_corner = const vec2 content_corner =
i2o(input_corners.zy, param.InputSize.xy, crop, shift, transform(input_corners.zy, input_center,
param.Rotation, param.CENTER_AFTER_CROPPING, scale_o2i); scale_i2o / param.OutputSize.xy, vec2(0.5));
const vec2 viewport_corner = vec2(1.0, 0.0); const vec2 viewport_corner = vec2(1.0, 0.0);
FragColor = vec4( FragColor = vec4(
blend_corner(right, top, cropped_input_size.y, blend_corner(right, top, cropped_input_size.y,
@ -293,8 +294,8 @@ void main() {
// Bottom right corner // Bottom right corner
const vec3 bottom = textureLod(Bottom, vec2(0.5), BIG_NUMBER).rgb; const vec3 bottom = textureLod(Bottom, vec2(0.5), BIG_NUMBER).rgb;
const vec2 content_corner = const vec2 content_corner =
i2o(input_corners.zw, param.InputSize.xy, crop, shift, transform(input_corners.zw, input_center,
param.Rotation, param.CENTER_AFTER_CROPPING, scale_o2i); scale_i2o / param.OutputSize.xy, vec2(0.5));
const vec2 viewport_corner = vec2(1.0, 1.0); const vec2 viewport_corner = vec2(1.0, 1.0);
FragColor = vec4( FragColor = vec4(
blend_corner(right, bottom, cropped_input_size.y, blend_corner(right, bottom, cropped_input_size.y,

View File

@ -1,7 +1,7 @@
// See compose.slang for copyright and other information. // See compose.slang for copyright and other information.
// clang-format off // clang-format off
#pragma parameter AVERAGE_FILL_SETTINGS "=== Average fill v1.8 settings ===" 0.0 0.0 1.0 1.0 #pragma parameter AVERAGE_FILL_SETTINGS "=== Average fill v1.9 settings ===" 0.0 0.0 1.0 1.0
#include "../../../misc/shaders/input_transform/parameters.inc" #include "../../../misc/shaders/input_transform/parameters.inc"

View File

@ -2,7 +2,7 @@
/* /*
Blur fill v1.9 by fishku Blur fill v1.9 by fishku
Copyright (C) 2023 Copyright (C) 2023-2024
Public domain license (CC0) Public domain license (CC0)
This shader preset allows cropping the image on any side, and filling the This shader preset allows cropping the image on any side, and filling the
@ -27,6 +27,7 @@
strength of the blur. strength of the blur.
Changelog: Changelog:
v1.10: Update input transform library.
v1.9: Add shift option from input transform library. v1.9: Add shift option from input transform library.
v1.8: Add overscale option from crop and scale library. v1.8: Add overscale option from crop and scale library.
v1.7: Refactor for new scaling library. Add rotation support. v1.7: Refactor for new scaling library. Add rotation support.
@ -98,17 +99,19 @@ void main() {
vTexCoord = TexCoord; vTexCoord = TexCoord;
const vec4 crop = vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT, const vec4 crop = vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT,
param.OS_CROP_BOTTOM, param.OS_CROP_RIGHT); param.OS_CROP_BOTTOM, param.OS_CROP_RIGHT);
const vec2 scale_o2i = get_scale_o2i( const vec2 scale_i2o = get_scale_i2o(
param.InputSize.xy, param.OutputSize.xy, crop, param.Rotation, param.InputSize.xy, param.OutputSize.xy, crop, param.Rotation,
param.CENTER_AFTER_CROPPING, param.FORCE_ASPECT_RATIO, param.CENTER_AFTER_CROPPING, param.FORCE_ASPECT_RATIO,
vec2(param.ASPECT_H, param.ASPECT_V), vec2(param.ASPECT_H, param.ASPECT_V),
vec2(param.FORCE_INTEGER_SCALING_H, param.FORCE_INTEGER_SCALING_V), vec2(param.FORCE_INTEGER_SCALING_H, param.FORCE_INTEGER_SCALING_V),
param.OVERSCALE, param.OVERSCALE);
/* output_size_is_final_viewport_size = */ false);
const vec2 shift = vec2(param.SHIFT_H, param.SHIFT_V); const vec2 shift = vec2(param.SHIFT_H, param.SHIFT_V);
tx_coord = o2i(vTexCoord, param.InputSize.xy, crop, shift, param.Rotation, const vec2 input_center = get_input_center(
param.CENTER_AFTER_CROPPING, scale_o2i); param.InputSize.xy, param.OutputSize.xy, scale_i2o, crop, shift,
tx_per_px = scale_o2i * param.OutputSize.zw; param.Rotation, param.CENTER_AFTER_CROPPING);
tx_coord = transform(TexCoord, vec2(0.5), param.OutputSize.xy / scale_i2o,
input_center);
tx_per_px = 1.0 / scale_i2o;
tx_to_uv = param.InputSize.zw; tx_to_uv = param.InputSize.zw;
input_corners = get_input_corners(param.InputSize.xy, crop, param.Rotation); input_corners = get_input_corners(param.InputSize.xy, crop, param.Rotation);
} }

View File

@ -1,7 +1,7 @@
// See compose.slang for copyright and other information. // See compose.slang for copyright and other information.
// clang-format off // clang-format off
#pragma parameter BLUR_FILL_SETTINGS "=== Blur fill v1.9 settings ===" 0.0 0.0 1.0 1.0 #pragma parameter BLUR_FILL_SETTINGS "=== Blur fill v1.10 settings ===" 0.0 0.0 1.0 1.0
#include "../../../misc/shaders/input_transform/parameters.inc" #include "../../../misc/shaders/input_transform/parameters.inc"

View File

@ -48,15 +48,24 @@ void main() {
vTexCoord = TexCoord; vTexCoord = TexCoord;
const vec4 crop = vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT, const vec4 crop = vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT,
param.OS_CROP_BOTTOM, param.OS_CROP_RIGHT); param.OS_CROP_BOTTOM, param.OS_CROP_RIGHT);
const vec2 scale_o2i = get_scale_o2i( // Because RA "rotates" the final viewport size on rotated cores, we need to
param.InputSize.xy, param.FinalViewportSize.xy, crop, param.Rotation, // undo that rotation so that the math checks out. The 0 and 180 degree
// rotations and the 90 and 270 rotations are symmetric for 2D sizes, which
// is why we can use param.Rotation directly here.
const vec2 final_viewport_size_rotated =
get_rotated_size(param.FinalViewportSize.xy, param.Rotation);
const vec2 scale_i2o = get_scale_i2o(
param.InputSize.xy, final_viewport_size_rotated, crop, param.Rotation,
param.CENTER_AFTER_CROPPING, param.FORCE_ASPECT_RATIO, param.CENTER_AFTER_CROPPING, param.FORCE_ASPECT_RATIO,
vec2(param.ASPECT_H, param.ASPECT_V), vec2(param.ASPECT_H, param.ASPECT_V),
vec2(param.FORCE_INTEGER_SCALING_H, param.FORCE_INTEGER_SCALING_V), param.OVERSCALE, vec2(param.FORCE_INTEGER_SCALING_H, param.FORCE_INTEGER_SCALING_V),
/* output_size_is_final_viewport_size = */ true); param.OVERSCALE);
const vec2 shift = vec2(param.SHIFT_H, param.SHIFT_V); const vec2 shift = vec2(param.SHIFT_H, param.SHIFT_V);
tx_coord = o2i(vTexCoord, param.InputSize.xy, crop, shift, param.Rotation, const vec2 input_center = get_input_center(
param.CENTER_AFTER_CROPPING, scale_o2i); param.InputSize.xy, final_viewport_size_rotated, scale_i2o, crop, shift,
param.Rotation, param.CENTER_AFTER_CROPPING);
tx_coord = transform(TexCoord, vec2(0.5),
final_viewport_size_rotated / scale_i2o, input_center);
input_corners = get_input_corners(param.InputSize.xy, crop, param.Rotation); input_corners = get_input_corners(param.InputSize.xy, crop, param.Rotation);
} }

View File

@ -1,16 +1,11 @@
// See the main shader file for copyright and other information. // See the main shader file for copyright and other information.
#include "../../../misc/shaders/coverage/coverage.inc"
// As determined by counting pixels on a photo. // As determined by counting pixels on a photo.
const vec2 subpx_ratio = vec2(0.296, 0.910); const vec2 subpx_ratio = vec2(0.296, 0.910);
const vec2 notch_ratio = vec2(0.115, 0.166); const vec2 notch_ratio = vec2(0.115, 0.166);
float rect_coverage(vec4 px_rect, vec4 rect) {
const vec2 bl = max(rect.xy, px_rect.xy);
const vec2 tr = min(rect.zw, px_rect.zw);
const vec2 coverage = max(tr - bl, 0.0);
return coverage.x * coverage.y;
}
float subpx_coverage(vec4 px_rect, vec2 subpx_orig, vec2 subpx_size, float subpx_coverage(vec4 px_rect, vec2 subpx_orig, vec2 subpx_size,
vec2 notch_size) { vec2 notch_size) {
return rect_coverage(px_rect, vec4(subpx_orig, subpx_orig + subpx_size)) - return rect_coverage(px_rect, vec4(subpx_orig, subpx_orig + subpx_size)) -

View File

@ -0,0 +1,10 @@
// Computes intersection area between pixels and geometric shapes for perfect
// (analytical) anti-aliasing.
// Assumes that the pixel rectangle has area 1 to avoid normalization.
float rect_coverage(vec4 px_rect, vec4 rect) {
const vec2 bl = max(rect.xy, px_rect.xy);
const vec2 tr = min(rect.zw, px_rect.zw);
const vec2 coverage = max(tr - bl, 0.0);
return coverage.x * coverage.y;
}

View File

@ -1,6 +1,6 @@
/* /*
Input transformation library v1.2 by fishku Input transformation library v1.3 by fishku
Copyright (C) 2023 Copyright (C) 2023-2024
Public domain license (CC0) Public domain license (CC0)
Apply cropping, scaling, and transformation operations to input viewport and Apply cropping, scaling, and transformation operations to input viewport and
@ -25,6 +25,7 @@
Refactored from the version that used to be in the blur_fill shader. Refactored from the version that used to be in the blur_fill shader.
Changelog: Changelog:
v1.3: Bug fixes and cleanup.
v1.2: Rename to "input transform". Add translation option. v1.2: Rename to "input transform". Add translation option.
v1.1: Add overscaling option. Unify parameters. v1.1: Add overscaling option. Unify parameters.
v1.0: Initial conversion from blur_fill release. Add rotation support. v1.0: Initial conversion from blur_fill release. Add rotation support.
@ -40,31 +41,30 @@ vec4 get_input_corners(vec2 input_size, vec4 crop, uint rotation) {
return vec4(crop.y, crop.x, input_size.x - crop.w, input_size.y - crop.z); return vec4(crop.y, crop.x, input_size.x - crop.w, input_size.y - crop.z);
} }
// Get adjusted center in input pixel coordinate system. // Get adjusted center in input pixel (texel) coordinate system.
vec2 get_input_center(vec2 input_size, vec4 crop, vec2 shift, uint rotation, // Crop is in input pixels (texels).
// Shift is in output pixels.
vec2 get_input_center(vec2 input_size, vec2 output_size, vec2 scale_i2o,
vec4 crop, vec2 shift, uint rotation,
float center_after_cropping) { float center_after_cropping) {
crop = get_rotated_crop(crop, rotation); crop = get_rotated_crop(crop, rotation);
shift = get_rotated_vector(shift, rotation); shift = get_rotated_vector(shift, rotation);
return (center_after_cropping > 0.5 // If input and output sizes have different parity, shift by 1/2 of an
? 0.5 * vec2(crop.y + input_size.x - crop.w, // output pixel to avoid having input pixel (texel) edges on output pixel
crop.x + input_size.y - crop.z) // centers, which leads to all sorts of issues.
: vec2(0.49999) * input_size) - return 0.5 * (input_size + center_after_cropping *
shift; vec2(crop.y - crop.w, crop.x - crop.z)) +
(0.5 * mod(input_size + output_size, 2.0) - shift) / scale_i2o;
} }
// Scaling from unit output to pixel input space. // Scaling from input to output space.
vec2 get_scale_o2i(vec2 input_size, vec2 output_size, vec4 crop, uint rotation, vec2 get_scale_i2o(vec2 input_size, vec2 output_size, vec4 crop, uint rotation,
float center_after_cropping, float force_aspect_ratio, float center_after_cropping, float force_aspect_ratio,
vec2 aspect, vec2 force_integer_scaling, float overscale, vec2 aspect, vec2 force_integer_scaling, float overscale) {
bool output_size_is_final_viewport_size) {
crop = get_rotated_crop(crop, rotation); 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 = get_rotated_size(aspect, rotation);
// Aspect ratio before cropping. // Aspect ratio before cropping.
// lambda_1 * input_pixels.x, lambda_2 * input_pixels.y, // Corrected for forced aspect ratio.
// possibly corrected for forced aspect ratio
aspect = (force_aspect_ratio < 0.5 aspect = (force_aspect_ratio < 0.5
? output_size * input_size.yx ? output_size * input_size.yx
: (aspect.x < 0.5 || aspect.y < 0.5 : (aspect.x < 0.5 || aspect.y < 0.5
@ -77,78 +77,60 @@ vec2 get_scale_o2i(vec2 input_size, vec2 output_size, vec4 crop, uint rotation,
: 2.0 * vec2(min(crop.y, crop.w), min(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); force_integer_scaling = get_rotated_size(force_integer_scaling, rotation);
float scale_x, scale_y; vec2 scale;
if (output_size.x / (input_size.x * aspect.x) < if (output_size.x / (input_size.x * aspect.x) <
output_size.y / (input_size.y * aspect.y)) { output_size.y / (input_size.y * aspect.y)) {
// Scale will be limited by width. Calc x scale, then derive y scale // Scale will be limited by width. Calc x scale, then derive y scale
// using aspect ratio. // using aspect ratio.
scale_x = mix(output_size.x / input_size.x, scale.x = mix(output_size.x / input_size.x,
output_size.y * aspect.x / (input_size.y * aspect.y), output_size.y * aspect.x / (input_size.y * aspect.y),
overscale); overscale);
if (force_integer_scaling.x > 0.5 && scale_x > 1.0) { if (force_integer_scaling.x > 0.5 && scale.x > 1.0) {
scale_x = floor(scale_x); scale.x = floor(scale.x);
} }
scale_y = scale_x * aspect.y / aspect.x; scale.y = scale.x * aspect.y / aspect.x;
if (force_integer_scaling.y > 0.5 && scale_y > 1.0) { if (force_integer_scaling.y > 0.5 && scale.y > 1.0) {
scale_y = floor(scale_y); scale.y = floor(scale.y);
} }
} else { } else {
// Scale will be limited by height. // Scale will be limited by height.
scale_y = mix(output_size.y / input_size.y, scale.y = mix(output_size.y / input_size.y,
output_size.x * aspect.y / (input_size.x * aspect.x), output_size.x * aspect.y / (input_size.x * aspect.x),
overscale); overscale);
if (force_integer_scaling.y > 0.5 && scale_y > 1.0) { if (force_integer_scaling.y > 0.5 && scale.y > 1.0) {
scale_y = floor(scale_y); scale.y = floor(scale.y);
} }
scale_x = scale_y * aspect.x / aspect.y; scale.x = scale.y * aspect.x / aspect.y;
if (force_integer_scaling.x > 0.5 && scale_x > 1.0) { if (force_integer_scaling.x > 0.5 && scale.x > 1.0) {
scale_x = floor(scale_x); scale.x = floor(scale.x);
} }
} }
return output_size / vec2(scale_x, scale_y); return scale;
} }
// From unit output to pixel input space. vec2 transform(vec2 x, vec2 input_center, vec2 scale, vec2 output_center) {
// coord_in_input_space = o2i(coord_in_output_space) return (x - input_center) * scale + output_center;
// 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, vec2 shift, uint rotation,
float center_after_cropping, vec2 scale_o2i) {
return (x - 0.49999) * scale_o2i + get_input_center(input_size, crop, shift,
rotation,
center_after_cropping);
} }
// Version that computes scale.
vec2 o2i(vec2 x, vec2 input_size, vec2 output_size, vec4 crop, vec2 shift, vec2 o2i(vec2 x, vec2 input_size, vec2 output_size, vec4 crop, vec2 shift,
uint rotation, float center_after_cropping, float force_aspect_ratio, uint rotation, float center_after_cropping, float force_aspect_ratio,
vec2 aspect, vec2 force_integer_scaling, float overscale, vec2 aspect, vec2 force_integer_scaling, float overscale) {
bool output_size_is_final_viewport_size) { const vec2 scale_i2o = get_scale_i2o(
return o2i(x, input_size, crop, shift, rotation, center_after_cropping, input_size, output_size, crop, rotation, center_after_cropping,
get_scale_o2i(input_size, output_size, crop, rotation, force_aspect_ratio, aspect, force_integer_scaling, overscale);
center_after_cropping, force_aspect_ratio, aspect, return transform(x, vec2(0.5), output_size / scale_i2o,
force_integer_scaling, overscale, get_input_center(input_size, output_size, scale_i2o, crop,
output_size_is_final_viewport_size)); shift, rotation, center_after_cropping));
} }
// From pixel input to unit output space.
// Version where scale is passed in.
vec2 i2o(vec2 x, vec2 input_size, vec4 crop, vec2 shift, uint rotation,
float center_after_cropping, vec2 scale_o2i) {
return (x - get_input_center(input_size, crop, shift, rotation,
center_after_cropping)) /
scale_o2i +
0.49999;
}
// Version that computes scale.
vec2 i2o(vec2 x, vec2 input_size, vec2 output_size, vec4 crop, vec2 shift, vec2 i2o(vec2 x, vec2 input_size, vec2 output_size, vec4 crop, vec2 shift,
uint rotation, float center_after_cropping, float force_aspect_ratio, uint rotation, float center_after_cropping, float force_aspect_ratio,
vec2 aspect, vec2 force_integer_scaling, float overscale, vec2 aspect, vec2 force_integer_scaling, float overscale) {
bool output_size_is_final_viewport_size) { const vec2 scale_i2o = get_scale_i2o(
return i2o(x, input_size, crop, shift, rotation, center_after_cropping, input_size, output_size, crop, rotation, center_after_cropping,
get_scale_o2i(input_size, output_size, crop, rotation, force_aspect_ratio, aspect, force_integer_scaling, overscale);
center_after_cropping, force_aspect_ratio, aspect, return transform(x,
force_integer_scaling, overscale, get_input_center(input_size, output_size, scale_i2o, crop,
output_size_is_final_viewport_size)); shift, rotation, center_after_cropping),
scale_i2o / output_size, vec2(0.5));
} }

View File

@ -15,7 +15,7 @@
#pragma parameter OS_CROP_RIGHT "Overscan crop right" 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 MOVING_SETTINGS "= Moving parameters =" 0.0 0.0 1.0 1.0 #pragma parameter MOVING_SETTINGS "= Moving parameters =" 0.0 0.0 1.0 1.0
#pragma parameter SHIFT_H "Horizontal shift" 0.0 -1024.0 1024.0 0.5 #pragma parameter SHIFT_H "Horizontal shift" 0.0 -2048.0 2048.0 1.0
#pragma parameter SHIFT_V "Vertical shift" 0.0 -1024.0 1024.0 0.5 #pragma parameter SHIFT_V "Vertical shift" 0.0 -2048.0 2048.0 1.0
#pragma parameter CENTER_AFTER_CROPPING "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
// clang-format on // clang-format on

View File

@ -0,0 +1,5 @@
shaders = 1
shader0 = shaders/box_filter_aa/box_filter_aa_xform.slang
filter_linear0 = false
scale_type0 = viewport

View File

@ -0,0 +1,136 @@
#version 450
/*
Box Filter AA v1.0 by fishku
Copyright (C) 2024
Public domain license (CC0)
Branching-free anti-aliasing using pixel coverage.
Changelog:
v1.0: Initial release.
*/
// clang-format off
#include "../../../misc/shaders/input_transform/parameters.inc"
#include "../../../misc/shaders/coverage/coverage.inc"
#include "../../../misc/shaders/input_transform/input_transform.inc"
// clang-format on
layout(push_constant) uniform Push {
vec4 SourceSize;
vec4 OutputSize;
uint Rotation;
// From input transform library, scaling section
float FORCE_ASPECT_RATIO;
float ASPECT_H;
float ASPECT_V;
float FORCE_INTEGER_SCALING_H;
float FORCE_INTEGER_SCALING_V;
float OVERSCALE;
// From input transform library, cropping section
float OS_CROP_TOP;
float OS_CROP_BOTTOM;
float OS_CROP_LEFT;
float OS_CROP_RIGHT;
// From input transform library, moving section
float SHIFT_H;
float SHIFT_V;
float CENTER_AFTER_CROPPING;
}
param;
layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; }
global;
#pragma stage vertex
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec4 px_rect;
layout(location = 1) out vec4 input_corners;
layout(location = 2) out vec2 scale_i2o;
layout(location = 3) out vec2 input_center;
layout(location = 4) out vec2 output_center;
layout(location = 5) out vec2 tx_coord;
void main() {
gl_Position = global.MVP * Position;
px_rect = vec4(TexCoord * param.OutputSize.xy - 0.5,
TexCoord * param.OutputSize.xy + 0.5);
const vec4 crop = vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT,
param.OS_CROP_BOTTOM, param.OS_CROP_RIGHT);
scale_i2o = get_scale_i2o(
param.SourceSize.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),
param.OVERSCALE);
const vec2 shift = vec2(param.SHIFT_H, param.SHIFT_V);
input_center = get_input_center(param.SourceSize.xy, param.OutputSize.xy,
scale_i2o, crop, shift, param.Rotation,
param.CENTER_AFTER_CROPPING);
tx_coord = transform(TexCoord, vec2(0.5), param.OutputSize.xy / scale_i2o,
input_center);
output_center = 0.5 * param.OutputSize.xy;
input_corners =
get_input_corners(param.SourceSize.xy, crop, param.Rotation);
}
#pragma stage fragment
layout(location = 0) in vec4 px_rect;
layout(location = 1) in vec4 input_corners;
layout(location = 2) in vec2 scale_i2o;
layout(location = 3) in vec2 input_center;
layout(location = 4) in vec2 output_center;
layout(location = 5) in vec2 tx_coord;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source;
void main() {
// Figure out 4 nearest texels in source texture.
// Clamp tx_coord for proper cropping.
vec2 tx_coord_i;
const vec2 tx_coord_f =
modf(clamp(tx_coord, input_corners.xy, input_corners.zw), tx_coord_i);
const vec2 tx_coord_off = step(vec2(0.5), tx_coord_f) * 2.0 - 1.0;
vec2 tx_origins[] = {tx_coord_i, tx_coord_i + vec2(tx_coord_off.x, 0.0),
tx_coord_i + vec2(0.0, tx_coord_off.y),
tx_coord_i + tx_coord_off};
// Sample.
// Apply square for fast "gamma correction".
vec3 samples[] = {
texture(Source, (tx_origins[0] + 0.5) * param.SourceSize.zw).rgb,
texture(Source, (tx_origins[1] + 0.5) * param.SourceSize.zw).rgb,
texture(Source, (tx_origins[2] + 0.5) * param.SourceSize.zw).rgb,
texture(Source, (tx_origins[3] + 0.5) * param.SourceSize.zw).rgb};
samples[0] *= samples[0];
samples[1] *= samples[1];
samples[2] *= samples[2];
samples[3] *= samples[3];
// Apply shader.
// Transform tx_origins into pixel output space.
tx_origins[0] =
transform(tx_origins[0], input_center, scale_i2o, output_center);
tx_origins[1] =
transform(tx_origins[1], input_center, scale_i2o, output_center);
tx_origins[2] =
transform(tx_origins[2], input_center, scale_i2o, output_center);
tx_origins[3] =
transform(tx_origins[3], input_center, scale_i2o, output_center);
const vec3 res =
samples[0] * rect_coverage(px_rect, vec4(tx_origins[0],
tx_origins[0] + scale_i2o)) +
samples[1] * rect_coverage(px_rect, vec4(tx_origins[1],
tx_origins[1] + scale_i2o)) +
samples[2] * rect_coverage(px_rect, vec4(tx_origins[2],
tx_origins[2] + scale_i2o)) +
samples[3] * rect_coverage(px_rect, vec4(tx_origins[3],
tx_origins[3] + scale_i2o));
// Apply sqrt for fast "gamma correction".
FragColor = vec4(sqrt(res), 1.0);
}

View File

@ -1,7 +1,7 @@
// See pixel_aa.slang for copyright and other information. // See pixel_aa.slang for copyright and other information.
// clang-format off // clang-format off
#pragma parameter PIX_AA_SETTINGS "=== Pixel AA v1.5 settings ===" 0.0 0.0 1.0 1.0 #pragma parameter PIX_AA_SETTINGS "=== Pixel AA v1.6 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_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_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 "Enable subpixel AA" 0.0 0.0 1.0 1.0

View File

@ -1,8 +1,8 @@
#version 450 #version 450
/* /*
Pixel AA v1.5 by fishku Pixel AA v1.6 by fishku
Copyright (C) 2023 Copyright (C) 2023-2024
Public domain license (CC0) Public domain license (CC0)
Features: Features:
@ -24,6 +24,7 @@
subpixel anti-aliasing, results are identical to the "pixellate" shader. subpixel anti-aliasing, results are identical to the "pixellate" shader.
Changelog: Changelog:
v1.6: Update input transform library.
v1.5: Upstream optimizations from GLSL port. Add free transform preset. v1.5: Upstream optimizations from GLSL port. Add free transform preset.
v1.4: Enable subpixel sampling for all four pixel layout orientations, v1.4: Enable subpixel sampling for all four pixel layout orientations,
including rotated screens. including rotated screens.

View File

@ -44,37 +44,39 @@ global;
#pragma stage vertex #pragma stage vertex
layout(location = 0) in vec4 Position; layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord; layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 tx_coord; layout(location = 0) out vec4 input_corners;
layout(location = 1) out vec2 tx_per_px; layout(location = 1) out vec2 tx_coord;
layout(location = 2) out vec2 tx_to_uv; layout(location = 2) out vec2 tx_per_px;
layout(location = 3) out vec4 input_corners; layout(location = 3) out vec2 tx_to_uv;
void main() { void main() {
gl_Position = global.MVP * Position; gl_Position = global.MVP * Position;
const vec4 crop = vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT, const vec4 crop = vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT,
param.OS_CROP_BOTTOM, param.OS_CROP_RIGHT); param.OS_CROP_BOTTOM, param.OS_CROP_RIGHT);
const vec2 scale_o2i = get_scale_o2i( const vec2 scale_i2o = get_scale_i2o(
param.SourceSize.xy, param.OutputSize.xy, crop, param.Rotation, param.SourceSize.xy, param.OutputSize.xy, crop, param.Rotation,
param.CENTER_AFTER_CROPPING, param.FORCE_ASPECT_RATIO, param.CENTER_AFTER_CROPPING, param.FORCE_ASPECT_RATIO,
vec2(param.ASPECT_H, param.ASPECT_V), vec2(param.ASPECT_H, param.ASPECT_V),
vec2(param.FORCE_INTEGER_SCALING_H, param.FORCE_INTEGER_SCALING_V), vec2(param.FORCE_INTEGER_SCALING_H, param.FORCE_INTEGER_SCALING_V),
param.OVERSCALE, param.OVERSCALE);
/* output_size_is_final_viewport_size = */ false);
const vec2 shift = vec2(param.SHIFT_H, param.SHIFT_V); const vec2 shift = vec2(param.SHIFT_H, param.SHIFT_V);
tx_coord = o2i(TexCoord, param.SourceSize.xy, crop, shift, param.Rotation, const vec2 input_center = get_input_center(
param.CENTER_AFTER_CROPPING, scale_o2i); param.SourceSize.xy, param.OutputSize.xy, scale_i2o, crop, shift,
tx_per_px = scale_o2i * param.OutputSize.zw; param.Rotation, param.CENTER_AFTER_CROPPING);
tx_coord = transform(TexCoord, vec2(0.5), param.OutputSize.xy / scale_i2o,
input_center);
tx_per_px = 1.0 / scale_i2o;
tx_to_uv = param.SourceSize.zw; tx_to_uv = param.SourceSize.zw;
input_corners = input_corners =
get_input_corners(param.SourceSize.xy, crop, param.Rotation); get_input_corners(param.SourceSize.xy, crop, param.Rotation);
} }
#pragma stage fragment #pragma stage fragment
layout(location = 0) in vec2 tx_coord; layout(location = 0) in vec4 input_corners;
layout(location = 1) in vec2 tx_per_px; layout(location = 1) in vec2 tx_coord;
layout(location = 2) in vec2 tx_to_uv; layout(location = 2) in vec2 tx_per_px;
layout(location = 3) in vec4 input_corners; layout(location = 3) in vec2 tx_to_uv;
layout(location = 0) out vec4 FragColor; layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source; layout(set = 0, binding = 2) uniform sampler2D Source;