// Blur Fill Background
// Fills pillarbox/letterbox areas with a blurred, stretched version of the game content
// Creates an immersive "ambient light" effect around the game

//!BGFX EFFECT
//!VERSION 1
//!NAME Blur Fill
//!CATEGORY Background
//!DESCRIPTION Fills the black bars with a heavily blurred and stretched version of the game content, creating an ambient backlight effect similar to Philips Ambilight.

//!PARAMETER
//!LABEL Blur Strength
//!DESC How blurry the background fill should be. Higher = more blur.
//!DEFAULT 16
//!MIN 4
//!MAX 32
//!STEP 1
float blurStrength;

//!PARAMETER
//!LABEL Background Brightness
//!DESC Brightness of the blurred background. Lower values are more subtle.
//!DEFAULT 0.5
//!MIN 0.1
//!MAX 1.0
//!STEP 0.05
float bgBrightness;

//!PARAMETER
//!LABEL Background Saturation
//!DESC Color saturation of the background. Lower = more muted colors.
//!DEFAULT 0.8
//!MIN 0.0
//!MAX 1.5
//!STEP 0.05
float bgSaturation;

//!TEXTURE
Texture2D INPUT;

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

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

//!COMMON

// Convert RGB to HSV
float3 RGBtoHSV(float3 c) {
    float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    float4 p = c.g < c.b ? float4(c.bg, K.wz) : float4(c.gb, K.xy);
    float4 q = c.r < p.x ? float4(p.xyw, c.r) : float4(c.r, p.yzx);
    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

// Convert HSV to RGB
float3 HSVtoRGB(float3 c) {
    float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y);
}

// Box blur sample
float3 BlurSample(float2 uv, float radius) {
    float2 pt = GetInputPt();
    float3 sum = float3(0, 0, 0);
    float count = 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;
            sum += INPUT.SampleLevel(linearSampler, uv + offset, 0).rgb;
            count += 1.0;
        }
    }

    return sum / count;
}

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

float4 Pass1(float2 pos) {
    // Calculate the content area within the output
    float2 inputSize = float2(GetInputSize());
    float2 outputSize = float2(GetOutputSize());
    float2 scale = GetScale();

    // Calculate aspect ratios
    float inputAspect = inputSize.x / inputSize.y;
    float outputAspect = outputSize.x / outputSize.y;

    // Determine content rectangle in output UV space
    float2 contentScale;
    float2 contentOffset;

    if (inputAspect > outputAspect) {
        // Letterboxing (bars on top/bottom)
        contentScale = float2(1.0, outputAspect / inputAspect);
        contentOffset = float2(0.0, (1.0 - contentScale.y) * 0.5);
    } else {
        // Pillarboxing (bars on sides)
        contentScale = float2(inputAspect / outputAspect, 1.0);
        contentOffset = float2((1.0 - contentScale.x) * 0.5, 0.0);
    }

    // Check if we're inside the content area
    float2 contentUV = (pos - contentOffset) / contentScale;
    bool insideContent = all(contentUV >= 0.0) && all(contentUV <= 1.0);

    if (insideContent) {
        // Inside content - sample normally
        return INPUT.SampleLevel(linearSampler, contentUV, 0);
    } else {
        // Outside content - create blurred fill
        // Map position to edge of content for sampling
        float2 clampedUV = saturate(contentUV);

        // Sample blurred content
        float3 blurred = BlurSample(clampedUV, blurStrength);

        // Apply saturation adjustment
        float3 hsv = RGBtoHSV(blurred);
        hsv.y *= bgSaturation;
        float3 color = HSVtoRGB(hsv);

        // Apply brightness
        color *= bgBrightness;

        // Fade out towards edges for smoother look
        float2 edgeDist = min(pos, 1.0 - pos);
        float edgeFade = saturate(min(edgeDist.x, edgeDist.y) * 10.0);
        color *= edgeFade;

        return float4(color, 1.0);
    }
}
