#version 330 core
// FX V2 `image` / `mask` kernel -- ported from Android
// EditorImageFxImageFilter and EditorImageFxMaskFilter, which share the
// two-texture blend vertex stage and differ only in the combine:
//   image : overlay(uv2) * input.a   -- input colour is discarded
//   mask  : input(uv)   * overlay.a  -- overlay only supplies a silhouette
//
// The overlay carries its own transform inside the same valueMap, applied
// here as an inverse map on uv2. u_overlayScale/Anchor/Translation arrive
// already converted to the bottom-up convention by the caller.
//
// The overlay texture comes from a QImage (row 0 = top) while the scratch
// is GPU-native (row 0 = bottom), hence the second flip on uv2.
//
// scaleType extension: fitCenter (contain, preserve the overlay's own
// aspect ratio, centered, transparent letterbox outside) and the Android
// 9-patch resizeX/resizeY stretch spec (the [0,start] and [end,1] slices of
// the overlay keep their native pixel size; only the middle stretches, so a
// viewfinder frame's corner ticks survive a resize). Both are no-ops when
// u_fitMode==0 (fitXY, the default) and u_resizeValid==(0,0).
//
// CAWP-21516: every mode (including fitXY) samples against u_contentRect --
// the clip's own visible footprint -- rather than the whole plate, so a
// border-style overlay's edges/corners land at the clip's boundary even
// when the plate letterboxes a mismatched aspect ratio.
in  vec2 TexCoord;
out vec4 FragColor;

uniform sampler2D u_src;
uniform sampler2D u_overlay;
uniform vec2      u_texScale;
uniform int       u_mode;              // 0 = image, 1 = mask
uniform vec2      u_overlayScale;
uniform vec2      u_overlayAnchor;
uniform vec2      u_overlayTranslation;
uniform int       u_fitMode;           // 0 = fitXY (stretch), 1 = fitCenter (contain), 2 = centerCrop (cover)
uniform vec2      u_overlayAspect;     // overlay px size / clip content px size, per axis
// CAWP-21516: the clip's own visible footprint as a fraction of the plate
// (x0, y0, w, h), bottom-up origin to match the flipped uv below -- smaller
// than the full [0,1] plate whenever the caller letterboxed a mismatched
// aspect ratio, and off-centre whenever the clip's dst rect is (a PiP box,
// or a clip moved by the Transform panel). The overlay must be placed
// against these edges, not the plate's, or its baked-in decoration (e.g. a
// border's rounded corners) lands in the invisible letterbox margin instead
// of at the clip's boundary.
uniform vec4      u_contentRect;
uniform vec4      u_resize;            // (startX, endX, startY, endY)
uniform vec2      u_resizeValid;       // per-axis 0 / 1 flag (Shader has no bool setter)

// 9-patch inverse map along one axis. k is the overlay's natural extent as
// a fraction of the canvas (u_overlayAspect's matching component).
//
// When the fixed head+tail slices would overflow the destination (a small
// render target relative to the overlay's native size), shrink both
// proportionally instead of letting one swallow the other -- which would
// otherwise create a jump discontinuity or silently drop the tail slice
// entirely. shrink==1 (the common case, head+tail<=1) makes this an exact
// identity to the un-guarded formula: o/head_s*start reduces to o/k, and
// the tail branch reduces the same way.
float ninePatch(float o, float start, float end, float k) {
    float head = start * k;
    float tail = (1.0 - end) * k;
    float shrink = min(1.0, 1.0 / max(head + tail, 1e-4));
    float headS = head * shrink;
    float tailS = tail * shrink;
    if (o < headS) {
        return (headS > 1e-4) ? (o / headS) * start : -1.0;
    }
    if (o > 1.0 - tailS) {
        return (tailS > 1e-4) ? end + (o - (1.0 - tailS)) / tailS * (1.0 - end) : 2.0;
    }
    float mid = max(1.0 - headS - tailS, 1e-4);
    return start + (end - start) * (o - headS) / mid;
}

void main() {
    vec2 uv = vec2(TexCoord.x, 1.0 - TexCoord.y);
    vec4 base = texture(u_src, uv * u_texScale);

    // Map into the clip's own content rect before anchoring the overlay, so
    // the overlay always spans the clip's visible edges (see u_contentRect).
    vec2 uvContent = (uv - u_contentRect.xy) / max(u_contentRect.zw, vec2(1e-4));
    vec2 p  = uvContent - u_overlayTranslation;
    vec2 o  = u_overlayAnchor + (p - u_overlayAnchor) / max(u_overlayScale, vec2(1e-4));

    // fitCenter (contain) and centerCrop (cover) both scale the overlay
    // uniformly -- preserving its own pixel aspect ratio -- and center it in
    // the [0,1] destination footprint. They differ only in which axis drives
    // the scale: contain takes the smaller so the whole overlay fits inside
    // (transparent letterbox outside), cover takes the larger so the
    // destination is fully painted and the overflowing axis is cropped.
    if (u_fitMode == 1 || u_fitMode == 2) {
        float sx = 1.0 / max(u_overlayAspect.x, 1e-4);
        float sy = 1.0 / max(u_overlayAspect.y, 1e-4);
        float fitScale = (u_fitMode == 1) ? min(sx, sy) : max(sx, sy);
        vec2 displayedFrac = u_overlayAspect * fitScale;
        vec2 margin = (vec2(1.0) - displayedFrac) * 0.5;
        o = (o - margin) / max(displayedFrac, vec2(1e-4));
    }

    if (u_resizeValid.x > 0.5) o.x = ninePatch(o.x, u_resize.x, u_resize.y, u_overlayAspect.x);
    if (u_resizeValid.y > 0.5) o.y = ninePatch(o.y, u_resize.z, u_resize.w, u_overlayAspect.y);

    vec4 ov = vec4(0.0);
    if (o.x >= 0.0 && o.x <= 1.0 && o.y >= 0.0 && o.y <= 1.0) {
        ov = texture(u_overlay, vec2(o.x, 1.0 - o.y));
    }

    FragColor = (u_mode == 0) ? ov * base.a
                              : base * ov.a;
}
