// Stretch Fill Background
// Fills pillarbox/letterbox areas by stretching/mirroring edge content
// Creates a seamless extended view effect

//!BGFX EFFECT
//!VERSION 1
//!NAME Stretch Fill
//!CATEGORY Background
//!DESCRIPTION Fills the black bars by stretching and mirroring the edge pixels of the game content, creating a seamless extended view.

//!PARAMETER
//!LABEL Fill Mode
//!DESC 0 = Mirror (reflects content), 1 = Stretch (extends edges)
//!DEFAULT 0
//!MIN 0
//!MAX 1
//!STEP 1
int fillMode;

//!PARAMETER
//!LABEL Edge Blur
//!DESC Amount of blur applied to the fill area. Higher = smoother transition.
//!DEFAULT 8
//!MIN 0
//!MAX 20
//!STEP 1
float edgeBlur;

//!PARAMETER
//!LABEL Fill Brightness
//!DESC Brightness of the fill area. Lower values make it more subtle.
//!DEFAULT 0.7
//!MIN 0.2
//!MAX 1.0
//!STEP 0.05
float fillBrightness;

//!PARAMETER
//!LABEL Vignette Strength
//!DESC Darkening towards the outer edges of the fill area.
//!DEFAULT 0.5
//!MIN 0.0
//!MAX 1.0
//!STEP 0.05
float vignetteStrength;

//!TEXTURE
Texture2D INPUT;

//!TEXTURE
//!WIDTH RENDERER_WIDTH
//!HEIGHT RENDERER_HEIGHT
Texture2D OUTPUT;

//!SAMPLER
//!FILTER LINEAR
SamplerState linearSampler;

//!COMMON

float3 SampleBlurred(float2 uv, float radius) {
    if (radius < 1.0) {
        return INPUT.SampleLevel(linearSampler, saturate(uv), 0).rgb;
    }

    float2 pt = GetInputPt();
    float3 sum = float3(0, 0, 0);
    float weight = 0;

    int r = (int)radius;
    for (int y = -r; y <= r; y += 2) {
        for (int x = -r; x <= r; x += 2) {
            float2 offset = float2(x, y) * pt;
            float w = 1.0 / (1.0 + length(float2(x, y)));
            sum += INPUT.SampleLevel(linearSampler, saturate(uv + offset), 0).rgb * w;
            weight += w;
        }
    }

    return sum / weight;
}

//!PASS 1
//!DESC Stretch Fill Background
//!STYLE PS
//!IN INPUT
//!OUT OUTPUT

float4 Pass1(float2 pos) {
    float2 inputSize = float2(GetInputSize());
    float2 outputSize = float2(GetOutputSize());

    float inputAspect = inputSize.x / inputSize.y;
    float outputAspect = outputSize.x / outputSize.y;

    float2 contentScale;
    float2 contentOffset;

    if (inputAspect > outputAspect) {
        contentScale = float2(1.0, outputAspect / inputAspect);
        contentOffset = float2(0.0, (1.0 - contentScale.y) * 0.5);
    } else {
        contentScale = float2(inputAspect / outputAspect, 1.0);
        contentOffset = float2((1.0 - contentScale.x) * 0.5, 0.0);
    }

    float2 contentUV = (pos - contentOffset) / contentScale;
    bool insideContent = all(contentUV >= 0.0) && all(contentUV <= 1.0);

    if (insideContent) {
        return INPUT.SampleLevel(linearSampler, contentUV, 0);
    }

    // Calculate fill UV
    float2 fillUV;

    if (fillMode == 0) {
        // Mirror mode - reflect around edges
        fillUV = contentUV;
        if (fillUV.x < 0.0) fillUV.x = -fillUV.x;
        if (fillUV.x > 1.0) fillUV.x = 2.0 - fillUV.x;
        if (fillUV.y < 0.0) fillUV.y = -fillUV.y;
        if (fillUV.y > 1.0) fillUV.y = 2.0 - fillUV.y;
        fillUV = saturate(fillUV);
    } else {
        // Stretch mode - clamp to edges
        fillUV = saturate(contentUV);
    }

    // Sample with optional blur
    float3 color = SampleBlurred(fillUV, edgeBlur);

    // Apply brightness
    color *= fillBrightness;

    // Apply vignette
    float2 distFromContent = abs(contentUV - saturate(contentUV));
    float distFactor = length(distFromContent);
    float vignette = 1.0 - saturate(distFactor * vignetteStrength * 3.0);
    color *= vignette;

    return float4(color, 1.0);
}
