Correctly account for screen rotation in several shaders (#478)

* Refactor scaling into include library

Add separate H and V integer scale forcing

Fix blur_fill; average fill still TODO

Clean up and fix blur_fill; Initial docs; Avg fill TODO

Mostly fix avg. fill; sampling TODO; Bump versions, add docs

Fix H/V extension; Tweak default params

Fix avg. fill

Remove defines, create functions

Reorder params

Clean up

Fix pixel_aa subpx sampling with rotation

* Minor docs fix
This commit is contained in:
fishcu 2023-09-10 16:15:41 +02:00 committed by GitHub
parent eca36c3b04
commit e366c7524c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 474 additions and 316 deletions

View file

@ -1,7 +1,7 @@
#version 450 #version 450
/* /*
Average fill v1.5 by fishku Average fill v1.6 by fishku
Copyright (C) 2023 Copyright (C) 2023
Public domain license (CC0) Public domain license (CC0)
@ -27,6 +27,7 @@
3 = Smooth angle-based blending 3 = Smooth angle-based blending
Changelog: Changelog:
v1.6: Refactor for new scaling library. Add rotation support.
v1.5: Optimize. Update to new Pixel AA version. v1.5: Optimize. Update to new Pixel AA version.
v1.4: Add anti-aliased interpolation for non-integer scaling. v1.4: Add anti-aliased interpolation for non-integer scaling.
v1.3: Fix scaling bugs. v1.3: Fix scaling bugs.
@ -37,17 +38,21 @@
// clang-format off // clang-format off
#include "parameters.slang" #include "parameters.slang"
#include "../../../pixel-art-scaling/shaders/pixel_aa/shared.slang" #include "../../../pixel-art-scaling/shaders/pixel_aa/parameters.slang"
// clang-format on // clang-format on
#include "../../../misc/shaders/scaling.slang"
#include "../../../pixel-art-scaling/shaders/pixel_aa/shared.slang"
layout(push_constant) uniform Push { layout(push_constant) uniform Push {
vec4 InputSize; vec4 InputSize;
vec4 FinalViewportSize; vec4 OutputSize;
uint Rotation;
float OS_CROP_TOP; float OS_CROP_TOP;
float OS_CROP_BOTTOM; float OS_CROP_BOTTOM;
float OS_CROP_LEFT; float OS_CROP_LEFT;
float OS_CROP_RIGHT; float OS_CROP_RIGHT;
float CENTER_CROP; float CENTER_AFTER_CROPPING;
float SAMPLE_SIZE; float SAMPLE_SIZE;
float EXTEND_H; float EXTEND_H;
float EXTEND_V; float EXTEND_V;
@ -55,7 +60,8 @@ layout(push_constant) uniform Push {
float FORCE_ASPECT_RATIO; float FORCE_ASPECT_RATIO;
float ASPECT_H; float ASPECT_H;
float ASPECT_V; float ASPECT_V;
float FORCE_INTEGER_SCALING; float FORCE_INTEGER_SCALING_H;
float FORCE_INTEGER_SCALING_V;
float FILL_GAMMA; float FILL_GAMMA;
// From pixel AA // From pixel AA
float PIX_AA_SHARP; float PIX_AA_SHARP;
@ -64,8 +70,6 @@ layout(push_constant) uniform Push {
} }
param; param;
#include "../blur_fill/scaling.slang"
layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; } layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; }
global; global;
@ -73,24 +77,46 @@ global;
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 vec2 vTexCoord;
layout(location = 1) out vec2 tx_coord; layout(location = 1) out vec2 scale_o2i;
layout(location = 2) out vec2 tx_per_px; layout(location = 2) out vec4 crop;
layout(location = 3) out vec2 tx_to_uv; 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() { void main() {
gl_Position = global.MVP * Position; gl_Position = global.MVP * Position;
vTexCoord = TexCoord; vTexCoord = TexCoord;
const vec2 scale_o2i = scale_o2i(); crop = vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT, param.OS_CROP_BOTTOM,
tx_coord = (vTexCoord - 0.49999) * scale_o2i + get_input_center(); param.OS_CROP_RIGHT);
tx_per_px = scale_o2i * param.FinalViewportSize.zw; 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; 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 #pragma stage fragment
layout(location = 0) in vec2 vTexCoord; layout(location = 0) in vec2 vTexCoord;
layout(location = 1) in vec2 tx_coord; layout(location = 1) in vec2 scale_o2i;
layout(location = 2) in vec2 tx_per_px; layout(location = 2) in vec4 crop;
layout(location = 3) in vec2 tx_to_uv; 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(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;
@ -137,68 +163,65 @@ vec3 blend_corner(vec3 a, // The first color to blend
} }
void main() { void main() {
if (tx_coord.x < param.OS_CROP_LEFT) { const vec2 extend_fill =
if (param.EXTEND_H < 0.5) { 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); FragColor = vec4(0.0, 0.0, 0.0, 1.0);
return; return;
} }
const vec3 left = textureLod(Left, vec2(0.5), BIG_NUMBER).rgb; const vec3 left = textureLod(Left, vec2(0.5), BIG_NUMBER).rgb;
if (tx_coord.y < param.OS_CROP_TOP) { if (tx_coord.y < input_corners.y) {
if (param.EXTEND_V < 0.5) { if (extend_fill.y < 0.5) {
FragColor = vec4(0.0, 0.0, 0.0, 1.0); FragColor = vec4(0.0, 0.0, 0.0, 1.0);
return; return;
} }
// 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(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); const vec2 viewport_corner = vec2(0.0, 0.0);
FragColor = FragColor = vec4(
vec4(blend_corner(left, top, blend_corner(left, top, cropped_input_size.y,
param.InputSize.y - param.OS_CROP_TOP - cropped_input_size.x, vTexCoord, content_corner,
param.OS_CROP_BOTTOM, viewport_corner - content_corner),
param.InputSize.x - param.OS_CROP_LEFT - 1.0);
param.OS_CROP_RIGHT,
vTexCoord, content_corner,
viewport_corner - content_corner),
1.0);
FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); 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 // Left bar
FragColor = vec4(pow(left, vec3(param.FILL_GAMMA)), 1.0); FragColor = vec4(pow(left, vec3(param.FILL_GAMMA)), 1.0);
} else { } else {
if (param.EXTEND_V < 0.5) { if (extend_fill.y < 0.5) {
FragColor = vec4(0.0, 0.0, 0.0, 1.0); FragColor = vec4(0.0, 0.0, 0.0, 1.0);
return; return;
} }
// 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 = i2o(vec2( const vec2 content_corner =
param.OS_CROP_LEFT, param.InputSize.y - param.OS_CROP_BOTTOM)); 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); const vec2 viewport_corner = vec2(0.0, 1.0);
FragColor = FragColor = vec4(
vec4(blend_corner(left, bottom, blend_corner(left, bottom, cropped_input_size.y,
param.InputSize.y - param.OS_CROP_TOP - cropped_input_size.x, vTexCoord, content_corner,
param.OS_CROP_BOTTOM, viewport_corner - content_corner),
param.InputSize.x - param.OS_CROP_LEFT - 1.0);
param.OS_CROP_RIGHT,
vTexCoord, content_corner,
viewport_corner - content_corner),
1.0);
FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA));
} }
} else if (tx_coord.x < param.InputSize.x - param.OS_CROP_RIGHT) { } else if (tx_coord.x < input_corners.z) {
if (tx_coord.y < param.OS_CROP_TOP) { if (tx_coord.y < input_corners.y) {
if (param.EXTEND_V < 0.5) { if (extend_fill.y < 0.5) {
FragColor = vec4(0.0, 0.0, 0.0, 1.0); FragColor = vec4(0.0, 0.0, 0.0, 1.0);
return; return;
} }
// Top bar // Top bar
FragColor = vec4(textureLod(Top, vec2(0.5), BIG_NUMBER).rgb, 1.0); FragColor = vec4(textureLod(Top, vec2(0.5), BIG_NUMBER).rgb, 1.0);
FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); 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 // 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. // Do a perfectly sharp (nearest neighbor) sampling.
FragColor = vec4( FragColor = vec4(
texture(Input, (floor(tx_coord) + 0.5) * param.InputSize.zw) texture(Input, (floor(tx_coord) + 0.5) * param.InputSize.zw)
@ -211,10 +234,10 @@ void main() {
FragColor = pixel_aa( FragColor = pixel_aa(
Input, tx_per_px, tx_to_uv, tx_coord, param.PIX_AA_SHARP, Input, tx_per_px, tx_to_uv, tx_coord, param.PIX_AA_SHARP,
/* gamma_correct = */ false, param.PIX_AA_SUBPX > 0.5, /* 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 { } else {
if (param.EXTEND_V < 0.5) { if (extend_fill.y < 0.5) {
FragColor = vec4(0.0, 0.0, 0.0, 1.0); FragColor = vec4(0.0, 0.0, 0.0, 1.0);
return; return;
} }
@ -224,54 +247,47 @@ void main() {
FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA));
} }
} else { } else {
if (param.EXTEND_H < 0.5) { if (extend_fill.x < 0.5) {
FragColor = vec4(0.0, 0.0, 0.0, 1.0); FragColor = vec4(0.0, 0.0, 0.0, 1.0);
return; return;
} }
const vec3 right = textureLod(Right, vec2(0.5), BIG_NUMBER).rgb; const vec3 right = textureLod(Right, vec2(0.5), BIG_NUMBER).rgb;
if (tx_coord.y < param.OS_CROP_TOP) { if (tx_coord.y < input_corners.y) {
if (param.EXTEND_V < 0.5) { if (extend_fill.y < 0.5) {
FragColor = vec4(0.0, 0.0, 0.0, 1.0); FragColor = vec4(0.0, 0.0, 0.0, 1.0);
return; return;
} }
// 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 = i2o(vec2( const vec2 content_corner =
param.InputSize.x - param.OS_CROP_RIGHT, param.OS_CROP_TOP)); 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); const vec2 viewport_corner = vec2(1.0, 0.0);
FragColor = FragColor = vec4(
vec4(blend_corner(right, top, blend_corner(right, top, cropped_input_size.y,
param.InputSize.y - param.OS_CROP_TOP - cropped_input_size.x, vTexCoord, content_corner,
param.OS_CROP_BOTTOM, viewport_corner - content_corner),
param.InputSize.x - param.OS_CROP_LEFT - 1.0);
param.OS_CROP_RIGHT,
vTexCoord, content_corner,
viewport_corner - content_corner),
1.0);
FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); 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 // Right bar
FragColor = vec4(pow(right, vec3(param.FILL_GAMMA)), 1.0); FragColor = vec4(pow(right, vec3(param.FILL_GAMMA)), 1.0);
} else { } else {
if (param.EXTEND_V < 0.5) { if (extend_fill.y < 0.5) {
FragColor = vec4(0.0, 0.0, 0.0, 1.0); FragColor = vec4(0.0, 0.0, 0.0, 1.0);
return; return;
} }
// 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(vec2(param.InputSize.x - param.OS_CROP_RIGHT, i2o(input_corners.zw, param.InputSize.xy, crop, param.Rotation,
param.InputSize.y - param.OS_CROP_BOTTOM)); param.CENTER_AFTER_CROPPING, scale_o2i);
const vec2 viewport_corner = vec2(1.0, 1.0); const vec2 viewport_corner = vec2(1.0, 1.0);
FragColor = FragColor = vec4(
vec4(blend_corner(right, bottom, blend_corner(right, bottom, cropped_input_size.y,
param.InputSize.y - param.OS_CROP_TOP - cropped_input_size.x, vTexCoord, content_corner,
param.OS_CROP_BOTTOM, viewport_corner - content_corner),
param.InputSize.x - param.OS_CROP_LEFT - 1.0);
param.OS_CROP_RIGHT,
vTexCoord, content_corner,
viewport_corner - content_corner),
1.0);
FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA));
} }
} }

View file

@ -2,13 +2,12 @@
// See compose.slang for copyright and other information. // See compose.slang for copyright and other information.
#include "parameters.slang" // Crop is rotated already.
vec4 get_effective_corners(vec4 crop, vec4 input_size, float sample_size) {
// clang-format off return input_size.zwzw * vec4(crop.y, //
#define EFF_CROP_TOP (param.InputSize.y - param.OS_CROP_BOTTOM - param.SAMPLE_SIZE) input_size.y - crop.z - sample_size, //
#define EFF_CROP_BOTTOM (param.OS_CROP_BOTTOM) input_size.x - crop.w, //
#define EFF_CROP_LEFT (param.OS_CROP_LEFT) input_size.y - crop.z);
#define EFF_CROP_RIGHT (param.OS_CROP_RIGHT) }
// clang-format on
#include "crop_and_sample_common.slang" #include "crop_and_sample_common.slang"

View file

@ -1,10 +1,11 @@
// See compose.slang for copyright and other information. // See compose.slang for copyright and other information.
#include "../../../misc/shaders/scaling.slang"
#include "parameters.slang"
layout(push_constant) uniform Push { layout(push_constant) uniform Push {
vec4 InputSize; vec4 InputSize;
vec4 OriginalSize; uint Rotation;
vec4 OutputSize;
uint FrameCount;
float OS_CROP_TOP; float OS_CROP_TOP;
float OS_CROP_BOTTOM; float OS_CROP_BOTTOM;
float OS_CROP_LEFT; float OS_CROP_LEFT;
@ -23,10 +24,13 @@ layout(location = 0) out vec2 vTexCoord;
void main() { void main() {
gl_Position = global.MVP * Position; gl_Position = global.MVP * Position;
vTexCoord = const vec4 rotated_crop =
mix(vec2(EFF_CROP_LEFT, EFF_CROP_TOP) * param.InputSize.zw, get_rotated_crop(vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT,
1.0 - vec2(EFF_CROP_RIGHT, EFF_CROP_BOTTOM) * param.InputSize.zw, param.OS_CROP_BOTTOM, param.OS_CROP_RIGHT),
TexCoord); 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 #pragma stage fragment

View file

@ -2,13 +2,11 @@
// See compose.slang for copyright and other information. // See compose.slang for copyright and other information.
#include "parameters.slang" vec4 get_effective_corners(vec4 crop, vec4 input_size, float sample_size) {
return input_size.zwzw * vec4(crop.y, //
// clang-format off crop.x, //
#define EFF_CROP_TOP (param.OS_CROP_TOP) crop.y + sample_size, //
#define EFF_CROP_BOTTOM (param.OS_CROP_BOTTOM) input_size.y - crop.z);
#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
#include "crop_and_sample_common.slang" #include "crop_and_sample_common.slang"

View file

@ -2,13 +2,11 @@
// See compose.slang for copyright and other information. // See compose.slang for copyright and other information.
#include "parameters.slang" vec4 get_effective_corners(vec4 crop, vec4 input_size, float sample_size) {
return input_size.zwzw * vec4(input_size.x - crop.w - sample_size, //
// clang-format off crop.x, //
#define EFF_CROP_TOP (param.OS_CROP_TOP) input_size.x - crop.w, //
#define EFF_CROP_BOTTOM (param.OS_CROP_BOTTOM) input_size.y - crop.z);
#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
#include "crop_and_sample_common.slang" #include "crop_and_sample_common.slang"

View file

@ -2,13 +2,11 @@
// See compose.slang for copyright and other information. // See compose.slang for copyright and other information.
#include "parameters.slang" vec4 get_effective_corners(vec4 crop, vec4 input_size, float sample_size) {
return input_size.zwzw * vec4(crop.y, //
// clang-format off crop.x, //
#define EFF_CROP_TOP (param.OS_CROP_TOP) input_size.x - crop.w, //
#define EFF_CROP_BOTTOM (param.InputSize.y - param.OS_CROP_TOP - param.SAMPLE_SIZE) crop.x + sample_size);
#define EFF_CROP_LEFT (param.OS_CROP_LEFT) }
#define EFF_CROP_RIGHT (param.OS_CROP_RIGHT)
// clang-format on
#include "crop_and_sample_common.slang" #include "crop_and_sample_common.slang"

View file

@ -1,25 +1,30 @@
// 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.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_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_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_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 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 calculating the average" 4.0 1.0 64.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_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 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 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 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 // clang-format on

View file

@ -1,7 +1,7 @@
#version 450 #version 450
/* /*
Blur fill v1.6 by fishku Blur fill v1.7 by fishku
Copyright (C) 2023 Copyright (C) 2023
Public domain license (CC0) Public domain license (CC0)
@ -27,6 +27,7 @@
strength of the blur. strength of the blur.
Changelog: 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.6: Optimize. Update to new Pixel AA version. Tune default blur strength.
v1.5: Add anti-aliased interpolation for non-integer scaling. v1.5: Add anti-aliased interpolation for non-integer scaling.
v1.4: Fix scaling bugs. v1.4: Fix scaling bugs.
@ -39,23 +40,28 @@
// clang-format off // clang-format off
#include "parameters.slang" #include "parameters.slang"
#include "../../../blurs/shaders/dual_filter/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 // clang-format on
#include "../../../misc/shaders/scaling.slang"
#include "../../../pixel-art-scaling/shaders/pixel_aa/shared.slang"
layout(push_constant) uniform Push { layout(push_constant) uniform Push {
vec4 InputSize; vec4 InputSize;
vec4 TiledSize; vec4 TiledSize;
vec4 FinalViewportSize; vec4 OutputSize;
uint Rotation;
float OS_CROP_TOP; float OS_CROP_TOP;
float OS_CROP_BOTTOM; float OS_CROP_BOTTOM;
float OS_CROP_LEFT; float OS_CROP_LEFT;
float OS_CROP_RIGHT; float OS_CROP_RIGHT;
float CENTER_CROP; float CENTER_AFTER_CROPPING;
float SAMPLE_SIZE; float SAMPLE_SIZE;
float FORCE_ASPECT_RATIO; float FORCE_ASPECT_RATIO;
float ASPECT_H; float ASPECT_H;
float ASPECT_V; float ASPECT_V;
float FORCE_INTEGER_SCALING; float FORCE_INTEGER_SCALING_H;
float FORCE_INTEGER_SCALING_V;
float FILL_GAMMA; float FILL_GAMMA;
// From dual filter blur // From dual filter blur
float BLUR_RADIUS; float BLUR_RADIUS;
@ -66,8 +72,6 @@ layout(push_constant) uniform Push {
} }
param; param;
#include "scaling.slang"
layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; } layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; }
global; global;
@ -78,18 +82,24 @@ layout(location = 0) out vec2 vTexCoord;
layout(location = 1) out vec2 tx_coord; layout(location = 1) out vec2 tx_coord;
layout(location = 2) out vec2 tx_per_px; layout(location = 2) out vec2 tx_per_px;
layout(location = 3) out vec2 tx_to_uv; layout(location = 3) out vec2 tx_to_uv;
layout(location = 4) out vec4 input_extrema; layout(location = 4) out vec4 input_corners;
void main() { void main() {
gl_Position = global.MVP * Position; gl_Position = global.MVP * Position;
vTexCoord = TexCoord; vTexCoord = TexCoord;
const vec2 scale_o2i = scale_o2i(); const vec4 crop = vec4(param.OS_CROP_TOP, param.OS_CROP_LEFT,
tx_coord = (vTexCoord - 0.49999) * scale_o2i + get_input_center(); param.OS_CROP_BOTTOM, param.OS_CROP_RIGHT);
tx_per_px = scale_o2i * param.FinalViewportSize.zw; 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; tx_to_uv = param.InputSize.zw;
input_extrema = vec4(param.OS_CROP_LEFT, param.OS_CROP_TOP, input_corners = get_input_corners(param.InputSize.xy, crop, param.Rotation);
param.InputSize.x - param.OS_CROP_RIGHT,
param.InputSize.y - param.OS_CROP_BOTTOM);
} }
#pragma stage fragment #pragma stage fragment
@ -97,15 +107,15 @@ layout(location = 0) in vec2 vTexCoord;
layout(location = 1) in vec2 tx_coord; layout(location = 1) in vec2 tx_coord;
layout(location = 2) in vec2 tx_per_px; layout(location = 2) in vec2 tx_per_px;
layout(location = 3) in vec2 tx_to_uv; 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(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 Tiled; layout(set = 0, binding = 3) uniform sampler2D Tiled;
layout(set = 0, binding = 4) uniform sampler2D Blurred; layout(set = 0, binding = 4) uniform sampler2D Blurred;
void main() { void main() {
if (any(lessThan(tx_coord, input_extrema.xy)) || if (any(lessThan(tx_coord, input_corners.xy)) ||
any(greaterThanEqual(tx_coord, input_extrema.zw))) { any(greaterThanEqual(tx_coord, input_corners.zw))) {
if (param.BLUR_RADIUS > 0.0) { if (param.BLUR_RADIUS > 0.0) {
// Sample blur. // Sample blur.
FragColor = vec4( FragColor = vec4(
@ -113,7 +123,7 @@ void main() {
1.0); 1.0);
} else { } else {
// Sample tiled pattern. // Sample tiled pattern.
// Do a sharp (nearest neighbor) resampling. // Do a perfectly sharp (nearest neighbor) resampling.
FragColor = FragColor =
vec4(pow(texture(Tiled, vec4(pow(texture(Tiled,
(floor(vTexCoord * param.TiledSize.xy) + 0.5) * (floor(vTexCoord * param.TiledSize.xy) + 0.5) *
@ -124,7 +134,8 @@ void main() {
} }
} else { } else {
// Sample original. // 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. // Do a perfectly sharp (nearest neighbor) sampling.
FragColor = vec4( FragColor = vec4(
texture(Input, (floor(tx_coord) + 0.5) * param.InputSize.zw) texture(Input, (floor(tx_coord) + 0.5) * param.InputSize.zw)
@ -137,7 +148,7 @@ void main() {
FragColor = pixel_aa( FragColor = pixel_aa(
Input, tx_per_px, tx_to_uv, tx_coord, param.PIX_AA_SHARP, Input, tx_per_px, tx_to_uv, tx_coord, param.PIX_AA_SHARP,
/* gamma_correct = */ false, param.PIX_AA_SUBPX > 0.5, /* 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);
} }
} }
} }

View file

@ -1,24 +1,30 @@
// 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.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_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_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_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 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 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 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 // clang-format on

View file

@ -2,29 +2,30 @@
// See compose.slang for copyright and other information. // See compose.slang for copyright and other information.
#include "../../../misc/shaders/scaling.slang"
#include "parameters.slang" #include "parameters.slang"
layout(push_constant) uniform Push { layout(push_constant) uniform Push {
vec4 InputSize; vec4 InputSize;
vec4 FinalViewportSize; vec4 FinalViewportSize;
uint Rotation;
float OS_CROP_TOP; float OS_CROP_TOP;
float OS_CROP_BOTTOM; float OS_CROP_BOTTOM;
float OS_CROP_LEFT; float OS_CROP_LEFT;
float OS_CROP_RIGHT; float OS_CROP_RIGHT;
float CENTER_CROP; float CENTER_AFTER_CROPPING;
float SAMPLE_SIZE; float SAMPLE_SIZE;
float BLUR_EXTEND_H; float EXTEND_H;
float BLUR_EXTEND_V; float EXTEND_V;
float MIRROR_BLUR; float MIRROR_BLUR;
float FORCE_ASPECT_RATIO; float FORCE_ASPECT_RATIO;
float ASPECT_H; float ASPECT_H;
float ASPECT_V; float ASPECT_V;
float FORCE_INTEGER_SCALING; float FORCE_INTEGER_SCALING_H;
float FORCE_INTEGER_SCALING_V;
} }
param; param;
#include "scaling.slang"
layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; } layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; }
global; global;
@ -32,14 +33,29 @@ global;
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 vec2 vTexCoord;
layout(location = 1) out vec2 tx_coord;
layout(location = 2) out vec4 input_corners;
void main() { void main() {
gl_Position = global.MVP * Position; gl_Position = global.MVP * Position;
vTexCoord = TexCoord; 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 #pragma stage fragment
layout(location = 0) in vec2 vTexCoord; 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(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source; 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; return period % 2 == 1 != x > 0.0 ? phase : w - phase;
} }
float extend_left(vec2 coord, vec4 input_extrema) { float extend_left(vec2 coord, vec4 input_corners) {
return input_extrema.x + return input_corners.x +
mirrored_repeat(param.SAMPLE_SIZE, coord.x - input_extrema.x); mirrored_repeat(param.SAMPLE_SIZE, coord.x - input_corners.x);
} }
float extend_right(vec2 coord, vec4 input_extrema) { float extend_right(vec2 coord, vec4 input_corners) {
return input_extrema.z - return input_corners.z -
mirrored_repeat(param.SAMPLE_SIZE, input_extrema.z - coord.x); mirrored_repeat(param.SAMPLE_SIZE, input_corners.z - coord.x);
} }
float extend_top(vec2 coord, vec4 input_extrema) { float extend_top(vec2 coord, vec4 input_corners) {
return input_extrema.y + return input_corners.y +
mirrored_repeat(param.SAMPLE_SIZE, coord.y - input_extrema.y); mirrored_repeat(param.SAMPLE_SIZE, coord.y - input_corners.y);
} }
float extend_bottom(vec2 coord, vec4 input_extrema) { float extend_bottom(vec2 coord, vec4 input_corners) {
return input_extrema.w - return input_corners.w -
mirrored_repeat(param.SAMPLE_SIZE, input_extrema.w - coord.y); mirrored_repeat(param.SAMPLE_SIZE, input_corners.w - coord.y);
} }
// This function samples in a very specific way which is the foundation for // 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 // - 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 // repeating sample is returned. The side of the frame that is sampled is given
// by the one that is closest to the sampled point. // by the one that is closest to the sampled point.
vec3 sample_mirrored_frame(sampler2D tex, vec2 coord, vec4 input_extrema) { vec3 sample_mirrored_frame(sampler2D tex, vec2 tx_coord, vec4 input_corners) {
if (coord.x < input_extrema.x) { const vec2 extend_fill =
if (param.BLUR_EXTEND_H < 0.5) { 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); return vec3(0.0);
} }
if (coord.y < input_extrema.y) { if (tx_coord.y < input_corners.y) {
// Top left corner extension // Top left corner extension
if (param.BLUR_EXTEND_V < 0.5) { if (extend_fill.y < 0.5) {
return vec3(0.0); return vec3(0.0);
} }
return texture(tex, vec2(extend_left(coord, input_extrema), return texture(tex, vec2(extend_left(tx_coord, input_corners),
extend_top(coord, input_extrema)) * extend_top(tx_coord, input_corners)) *
param.InputSize.zw) param.InputSize.zw)
.rgb; .rgb;
} else if (coord.y < input_extrema.w) { } else if (tx_coord.y < input_corners.w) {
// Left extension // Left extension
return texture(tex, return texture(tex, vec2(extend_left(tx_coord, input_corners),
vec2(extend_left(coord, input_extrema), coord.y) * tx_coord.y) *
param.InputSize.zw) param.InputSize.zw)
.rgb; .rgb;
} else { } else {
// Bottom left corner extension // Bottom left corner extension
if (param.BLUR_EXTEND_V < 0.5) { if (extend_fill.y < 0.5) {
return vec3(0.0); return vec3(0.0);
} }
return texture(tex, vec2(extend_left(coord, input_extrema), return texture(tex, vec2(extend_left(tx_coord, input_corners),
extend_bottom(coord, input_extrema)) * extend_bottom(tx_coord, input_corners)) *
param.InputSize.zw) param.InputSize.zw)
.rgb; .rgb;
} }
} else if (coord.x < input_extrema.z) { } else if (tx_coord.x < input_corners.z) {
if (coord.y < input_extrema.y) { if (tx_coord.y < input_corners.y) {
if (param.BLUR_EXTEND_V < 0.5) { if (extend_fill.y < 0.5) {
return vec3(0.0); return vec3(0.0);
} }
// Top extension // Top extension
return texture(tex, return texture(tex, vec2(tx_coord.x,
vec2(coord.x, extend_top(coord, input_extrema)) * extend_top(tx_coord, input_corners)) *
param.InputSize.zw) param.InputSize.zw)
.rgb; .rgb;
} else if (coord.y < input_extrema.w) { } else if (tx_coord.y < input_corners.w) {
const vec4 inner_extrema = const vec4 inner_corners =
input_extrema + vec4(param.SAMPLE_SIZE, param.SAMPLE_SIZE, input_corners + vec4(param.SAMPLE_SIZE, param.SAMPLE_SIZE,
-param.SAMPLE_SIZE, -param.SAMPLE_SIZE); -param.SAMPLE_SIZE, -param.SAMPLE_SIZE);
if (any(lessThan(coord, inner_extrema.xy)) || if (any(lessThan(tx_coord, inner_corners.xy)) ||
any(greaterThanEqual(coord, inner_extrema.zw))) { any(greaterThanEqual(tx_coord, inner_corners.zw))) {
// In frame band // 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 // Innermost -- mirrored repeat sampling from nearest side
const vec4 distances = const vec4 distances = vec4(
vec4(coord.x - inner_extrema.x, inner_extrema.z - coord.x, tx_coord.x - inner_corners.x, inner_corners.z - tx_coord.x,
coord.y - inner_extrema.y, inner_extrema.w - coord.y); tx_coord.y - inner_corners.y, inner_corners.w - tx_coord.y);
switch (argmin(distances)) { switch (argmin(distances)) {
case 0: case 0:
// left // left
return texture(tex, vec2(extend_left(coord, input_extrema), return texture(tex,
coord.y) * vec2(extend_left(tx_coord, input_corners),
param.InputSize.zw) tx_coord.y) *
param.InputSize.zw)
.rgb; .rgb;
case 1: case 1:
// right // right
return texture(tex, vec2(extend_right(coord, input_extrema), return texture(tex,
coord.y) * vec2(extend_right(tx_coord, input_corners),
param.InputSize.zw) tx_coord.y) *
param.InputSize.zw)
.rgb; .rgb;
case 2: case 2:
// top // top
return texture(tex, vec2(coord.x, return texture(tex,
extend_top(coord, input_extrema)) * vec2(tx_coord.x,
param.InputSize.zw) extend_top(tx_coord, input_corners)) *
param.InputSize.zw)
.rgb; .rgb;
case 3: case 3:
default: default:
// bottom // bottom
return texture(tex, return texture(tex, vec2(tx_coord.x,
vec2(coord.x, extend_bottom(tx_coord,
extend_bottom(coord, input_extrema)) * input_corners)) *
param.InputSize.zw) param.InputSize.zw)
.rgb; .rgb;
} }
} else { } else {
if (param.BLUR_EXTEND_V < 0.5) { if (extend_fill.y < 0.5) {
return vec3(0.0); return vec3(0.0);
} }
// Bottom extension // Bottom extension
return texture(tex, return texture(tex, vec2(tx_coord.x,
vec2(coord.x, extend_bottom(coord, input_extrema)) * extend_bottom(tx_coord, input_corners)) *
param.InputSize.zw) param.InputSize.zw)
.rgb; .rgb;
} }
} else { } else {
if (param.BLUR_EXTEND_H < 0.5) { if (extend_fill.x < 0.5) {
return vec3(0.0); return vec3(0.0);
} }
if (coord.y < input_extrema.y) { if (tx_coord.y < input_corners.y) {
// Top right corner extension // Top right corner extension
if (param.BLUR_EXTEND_V < 0.5) { if (extend_fill.y < 0.5) {
return vec3(0.0); return vec3(0.0);
} }
return texture(tex, vec2(extend_right(coord, input_extrema), return texture(tex, vec2(extend_right(tx_coord, input_corners),
extend_top(coord, input_extrema)) * extend_top(tx_coord, input_corners)) *
param.InputSize.zw) param.InputSize.zw)
.rgb; .rgb;
} else if (coord.y < input_extrema.w) { } else if (tx_coord.y < input_corners.w) {
// Right extension // Right extension
return texture(tex, return texture(tex, vec2(extend_right(tx_coord, input_corners),
vec2(extend_right(coord, input_extrema), coord.y) * tx_coord.y) *
param.InputSize.zw) param.InputSize.zw)
.rgb; .rgb;
} else { } else {
// Bottom right corner extension // Bottom right corner extension
if (param.BLUR_EXTEND_V < 0.5) { if (extend_fill.y < 0.5) {
return vec3(0.0); return vec3(0.0);
} }
return texture(tex, vec2(extend_right(coord, input_extrema), return texture(tex, vec2(extend_right(tx_coord, input_corners),
extend_bottom(coord, input_extrema)) * extend_bottom(tx_coord, input_corners)) *
param.InputSize.zw) param.InputSize.zw)
.rgb; .rgb;
} }
@ -216,11 +237,6 @@ vec3 sample_mirrored_frame(sampler2D tex, vec2 coord, vec4 input_extrema) {
} }
void main() { void main() {
const vec2 pixel_coord = o2i(vTexCoord); FragColor =
FragColor = vec4( vec4(sample_mirrored_frame(Source, tx_coord, input_corners), 1.0);
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);
} }

View file

@ -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; }

163
misc/shaders/scaling.slang Normal file
View file

@ -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;
}

View file

@ -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

View file

@ -1,7 +1,7 @@
#version 450 #version 450
/* /*
Pixel AA v1.2 by fishku Pixel AA v1.3 by fishku
Copyright (C) 2023 Copyright (C) 2023
Public domain license (CC0) Public domain license (CC0)
@ -24,15 +24,20 @@
subpixel anti-aliasing, results are identical to the "pixellate" shader. subpixel anti-aliasing, results are identical to the "pixellate" shader.
Changelog: Changelog:
v1.3: Account for screen rotation in subpixel sampling.
v1.2: Optimize and simplify algorithm. Enable sharpness < 1.0. Fix subpixel v1.2: Optimize and simplify algorithm. Enable sharpness < 1.0. Fix subpixel
sampling bug. sampling bug.
v1.1: Better subpixel sampling. v1.1: Better subpixel sampling.
v1.0: Initial release. v1.0: Initial release.
*/ */
#include "parameters.slang"
#include "shared.slang"
layout(push_constant) uniform Push { layout(push_constant) uniform Push {
vec4 SourceSize; vec4 SourceSize;
vec4 OutputSize; vec4 OutputSize;
uint Rotation;
float PIX_AA_SHARP; float PIX_AA_SHARP;
float PIX_AA_GAMMA; float PIX_AA_GAMMA;
float PIX_AA_SUBPX; float PIX_AA_SUBPX;
@ -64,11 +69,9 @@ layout(location = 2) 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;
#include "shared.slang"
void main() { void main() {
FragColor = FragColor =
pixel_aa(Source, tx_per_px, tx_to_uv, tx_coord, param.PIX_AA_SHARP, 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_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);
} }

View file

@ -1,13 +1,5 @@
// See pixel_aa.slang for copyright and other information. // 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. // Similar to smoothstep, but has a configurable slope at x = 0.5.
// Original smoothstep has a slope of 1.5 at x = 0.5 // Original smoothstep has a slope of 1.5 at x = 0.5
#define INSTANTIATE_SLOPESTEP(T) \ #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. // interpolation.
vec4 pixel_aa(sampler2D tex, vec2 tx_per_px, vec2 tx_to_uv, vec2 tx_coord, vec4 pixel_aa(sampler2D tex, vec2 tx_per_px, vec2 tx_to_uv, vec2 tx_coord,
float sharpness, bool gamma_correct, bool sample_subpx, float sharpness, bool gamma_correct, bool sample_subpx,
bool subpx_bgr) { bool subpx_bgr, uint rotation) {
if (sample_subpx) { if (sample_subpx) {
// Subpixel sampling: Shift the sampling by 1/3rd of an output pixel for // Subpixel sampling: Shift the sampling by 1/3rd of an output pixel for
// each subpixel, assuming that the output size is at monitor // each subpixel, assuming that the output size is at monitor
// resolution. // 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 = 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; vec3 res;
for (int i = -1; i < 2; ++i) { for (int i = -1; i < 2; ++i) {
res[i + 1] = sample_aa(tex, tx_per_px, tx_to_uv, res[i + 1] = sample_aa(tex, tx_per_px, tx_to_uv,