float insideUnit(float2 uv)
{
	return step(0.0, uv.x) * step(uv.x, 1.0) * step(0.0, uv.y) * step(uv.y, 1.0);
}

float4 blendLabel(float4 base, float4 labelSample, float inside)
{
	float labelMask = saturate(dot(labelSample.rgb, float3(0.2126, 0.7152, 0.0722)) * labelSample.a * inside);
	float3 screened = 1.0 - (1.0 - base.rgb) * (1.0 - labelSample.rgb);
	return float4(lerp(base.rgb, screened, labelMask), max(base.a, labelMask));
}

float4 shadeVideo(ShaderContext context)
{
	float4 color = context.sourceColor;

	float targetAspect = 16.0 / 9.0;
	float resolutionAspect = max(context.outputResolution.x, 1.0) / max(context.outputResolution.y, 1.0);
	float width = saturate(overlayScale);
	float height = width * resolutionAspect / targetAspect;
	// Keep the scope in a 16:9 frame, then shrink it if the requested scale
	// would push the overlay beyond the screen bounds.
	float fitScale = min(1.0 / max(width, 0.001), 1.0 / max(height, 0.001));
	width *= min(fitScale, 1.0);
	height *= min(fitScale, 1.0);
	float2 halfSize = float2(width, height) * 0.5;
	float2 center = clamp(saturate(overlayPosition), halfSize, float2(1.0) - halfSize);
	float2 overlayMin = center - halfSize;
	float2 overlayMax = center + halfSize;

	if (context.uv.x < overlayMin.x || context.uv.x > overlayMax.x ||
		context.uv.y < overlayMin.y || context.uv.y > overlayMax.y)
		return color;

	float2 boxUv = (context.uv - overlayMin) / max(float2(width, height), float2(0.001));
	float2 pad = min(float2(saturate(overlayPadding)), float2(0.45));
	float2 innerUv = (boxUv - pad) / max(float2(1.0) - pad * 2.0, float2(0.001));

	float3 bg = lerp(color.rgb, float3(0.0, 0.0, 0.0), saturate(backgroundOpacity));
	float labelHeight = min(max(pad.x * 0.95, 0.048), 0.12);
	// Label textures are authored in UV space, so compensate for the overlay
	// and output aspect ratios to keep the glyphs from stretching.
	float labelWidth = labelHeight * height * max(context.outputResolution.y, 1.0) / max(width * max(context.outputResolution.x, 1.0), 0.001);
	float labelX = max(pad.x * 0.5, labelWidth * 0.55);
	float y0 = pad.y;
	float y25 = pad.y + 0.25 * (1.0 - pad.y * 2.0);
	float y50 = pad.y + 0.50 * (1.0 - pad.y * 2.0);
	float y75 = pad.y + 0.75 * (1.0 - pad.y * 2.0);
	float y100 = 1.0 - pad.y;

	float4 label = float4(0.0);
	float2 labelUv100 = (boxUv - float2(labelX, y100)) / float2(labelWidth, labelHeight) + float2(0.5);
	label = blendLabel(label, label100Texture.Sample(labelUv100), insideUnit(labelUv100));
	float2 labelUv75 = (boxUv - float2(labelX, y75)) / float2(labelWidth, labelHeight) + float2(0.5);
	label = blendLabel(label, label75Texture.Sample(labelUv75), insideUnit(labelUv75));
	float2 labelUv50 = (boxUv - float2(labelX, y50)) / float2(labelWidth, labelHeight) + float2(0.5);
	label = blendLabel(label, label50Texture.Sample(labelUv50), insideUnit(labelUv50));
	float2 labelUv25 = (boxUv - float2(labelX, y25)) / float2(labelWidth, labelHeight) + float2(0.5);
	label = blendLabel(label, label25Texture.Sample(labelUv25), insideUnit(labelUv25));
	float2 labelUv0 = (boxUv - float2(labelX, y0)) / float2(labelWidth, labelHeight) + float2(0.5);
	label = blendLabel(label, label0Texture.Sample(labelUv0), insideUnit(labelUv0));

	if (innerUv.x < 0.0 || innerUv.x > 1.0 || innerUv.y < 0.0 || innerUv.y > 1.0)
		return float4(lerp(bg, label.rgb, label.a), color.a);

	float pixelThickness = max(lineThickness, 0.5) / max(context.outputResolution.y * height * (1.0 - pad.y * 2.0), 1.0);
	float requestedSamples = clamp(waveformSamples, 1.0, 96.0);
	float density = 0.0;

	// For each output pixel, march through source rows at the same X coordinate
	// and accumulate hits where sampled luma lands near this pixel's Y level.
	for (int sampleIndex = 0; sampleIndex < 96; sampleIndex++)
	{
		float samplePosition = float(sampleIndex);
		if (samplePosition >= requestedSamples)
			break;

		float sourceY = (samplePosition + 0.5) / requestedSamples;
		float luma = dot(sampleVideo(float2(innerUv.x, sourceY)).rgb, float3(0.2126, 0.7152, 0.0722));
		float targetY = saturate(luma);
		float sampleHit = 1.0 - smoothstep(pixelThickness, pixelThickness * 2.5, abs(innerUv.y - targetY));

		density += sampleHit;
	}

	float wave = saturate(density / requestedSamples * max(waveformGain, 0.0));
	float floor = saturate(waveformNoiseReduction);
	wave = smoothstep(floor, 1.0, wave);

	float grid = 0.0;
	grid = max(grid, 1.0 - smoothstep(0.002, 0.006, abs(innerUv.y - 0.00)));
	grid = max(grid, 1.0 - smoothstep(0.002, 0.006, abs(innerUv.y - 0.25)));
	grid = max(grid, 1.0 - smoothstep(0.002, 0.006, abs(innerUv.y - 0.50)));
	grid = max(grid, 1.0 - smoothstep(0.002, 0.006, abs(innerUv.y - 0.75)));
	grid = max(grid, 1.0 - smoothstep(0.002, 0.006, abs(innerUv.y - 1.00)));

	float3 withGrid = lerp(bg, float3(0.16, 0.22, 0.28), grid * saturate(gridOpacity));
	float3 withLabel = lerp(withGrid, label.rgb, label.a);
	float3 scoped = lerp(withLabel, waveformColor.rgb, wave * saturate(waveformOpacity) * waveformColor.a);
	return float4(scoped, color.a);
}
