slang-shaders/border/shaders/blur_fill/render_sampling_areas.slang
fishcu e366c7524c
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
2023-09-10 09:15:41 -05:00

243 lines
9.1 KiB
Plaintext

#version 450
// 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_AFTER_CROPPING;
float SAMPLE_SIZE;
float EXTEND_H;
float EXTEND_V;
float MIRROR_BLUR;
float FORCE_ASPECT_RATIO;
float ASPECT_H;
float ASPECT_V;
float FORCE_INTEGER_SCALING_H;
float FORCE_INTEGER_SCALING_V;
}
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 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;
float min_of_vec4(vec4 i) { return min(min(i.x, i.y), min(i.z, i.w)); }
int argmin(vec4 i) {
const vec4 m = vec4(min_of_vec4(i));
const vec4 ma = step(i, m) * vec4(0.0, -1.0, -2.0, -3.0);
return -int(min_of_vec4(ma));
}
// Returns a coordinate in [0, w) when repeating that interval (and optionally
// mirroring).
float mirrored_repeat(float w, float x) {
const float phase = mod(x, w);
if (param.MIRROR_BLUR < 0.5) {
return phase;
}
const int period = int(x / w);
return period % 2 == 1 != x > 0.0 ? phase : w - phase;
}
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_corners) {
return input_corners.z -
mirrored_repeat(param.SAMPLE_SIZE, input_corners.z - coord.x);
}
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_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
// blurring later.
// - If the sample coordinate is outside of the cropped input, Either black is
// returned if blur extension is turned off, or a repeated pattern from the
// sampling frame band is returned.
// - If the coordinate is inside the cropped input and within the frame band
// given by SAMPLE_SIZE, the original texture sample is returned.
// - 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 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 (tx_coord.y < input_corners.y) {
// Top left corner extension
if (extend_fill.y < 0.5) {
return vec3(0.0);
}
return texture(tex, vec2(extend_left(tx_coord, input_corners),
extend_top(tx_coord, input_corners)) *
param.InputSize.zw)
.rgb;
} else if (tx_coord.y < input_corners.w) {
// Left extension
return texture(tex, vec2(extend_left(tx_coord, input_corners),
tx_coord.y) *
param.InputSize.zw)
.rgb;
} else {
// Bottom left corner extension
if (extend_fill.y < 0.5) {
return vec3(0.0);
}
return texture(tex, vec2(extend_left(tx_coord, input_corners),
extend_bottom(tx_coord, input_corners)) *
param.InputSize.zw)
.rgb;
}
} 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(tx_coord.x,
extend_top(tx_coord, input_corners)) *
param.InputSize.zw)
.rgb;
} 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(tx_coord, inner_corners.xy)) ||
any(greaterThanEqual(tx_coord, inner_corners.zw))) {
// In frame band
return texture(tex, tx_coord * param.InputSize.zw).rgb;
}
// Innermost -- mirrored repeat sampling from nearest side
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(tx_coord, input_corners),
tx_coord.y) *
param.InputSize.zw)
.rgb;
case 1:
// right
return texture(tex,
vec2(extend_right(tx_coord, input_corners),
tx_coord.y) *
param.InputSize.zw)
.rgb;
case 2:
// top
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(tx_coord.x,
extend_bottom(tx_coord,
input_corners)) *
param.InputSize.zw)
.rgb;
}
} else {
if (extend_fill.y < 0.5) {
return vec3(0.0);
}
// Bottom extension
return texture(tex, vec2(tx_coord.x,
extend_bottom(tx_coord, input_corners)) *
param.InputSize.zw)
.rgb;
}
} else {
if (extend_fill.x < 0.5) {
return vec3(0.0);
}
if (tx_coord.y < input_corners.y) {
// Top right corner extension
if (extend_fill.y < 0.5) {
return vec3(0.0);
}
return texture(tex, vec2(extend_right(tx_coord, input_corners),
extend_top(tx_coord, input_corners)) *
param.InputSize.zw)
.rgb;
} else if (tx_coord.y < input_corners.w) {
// Right extension
return texture(tex, vec2(extend_right(tx_coord, input_corners),
tx_coord.y) *
param.InputSize.zw)
.rgb;
} else {
// Bottom right corner extension
if (extend_fill.y < 0.5) {
return vec3(0.0);
}
return texture(tex, vec2(extend_right(tx_coord, input_corners),
extend_bottom(tx_coord, input_corners)) *
param.InputSize.zw)
.rgb;
}
}
}
void main() {
FragColor =
vec4(sample_mirrored_frame(Source, tx_coord, input_corners), 1.0);
}