#include #include using namespace metal; struct NTSCParams { float source_width; float source_height; float a_value; float b_value; float cc_value; float scan_time; float notch_width; float y_freq; float i_freq; float q_freq; float _pad0; float _pad1; }; struct main0_out { float4 FragColor [[color(0)]]; }; struct main0_in { float2 v_uv [[user(locn0)]]; }; fragment main0_out main0(main0_in in [[stage_in]], constant NTSCParams& u [[buffer(0)]], texture2d Source [[texture(0)]], sampler SourceSmplr [[sampler(0)]]) { main0_out out = {}; float2 source_dims = float2(u.source_width, u.source_height); float4 BaseTexel = Source.sample(SourceSmplr, in.v_uv); float CCValue = u.cc_value; float ScanTime = u.scan_time; float NotchHalfWidth = u.notch_width / 2.0; float YFreqResponse = u.y_freq; float IFreqResponse = u.i_freq; float QFreqResponse = u.q_freq; float AValue = u.a_value; float BValue = u.b_value; float TimePerSample = ScanTime / (source_dims.x * 4.0); float Fc_y1 = (CCValue - NotchHalfWidth) * TimePerSample; float Fc_y2 = (CCValue + NotchHalfWidth) * TimePerSample; float Fc_y3 = YFreqResponse * TimePerSample; float Fc_i = IFreqResponse * TimePerSample; float Fc_q = QFreqResponse * TimePerSample; float Fc_i_2 = Fc_i * 2.0; float Fc_q_2 = Fc_q * 2.0; float Fc_y1_2 = Fc_y1 * 2.0; float Fc_y2_2 = Fc_y2 * 2.0; float Fc_y3_2 = Fc_y3 * 2.0; float Fc_i_pi2 = Fc_i * 6.283185482025146484375; float Fc_q_pi2 = Fc_q * 6.283185482025146484375; float Fc_y1_pi2 = Fc_y1 * 6.283185482025146484375; float Fc_y2_pi2 = Fc_y2 * 6.283185482025146484375; float Fc_y3_pi2 = Fc_y3 * 6.283185482025146484375; float PI2Length = 0.098174773156642913818359375; float W = (6.283185482025146484375 * CCValue) * ScanTime; float WoPI = W / 3.1415927410125732421875; float HOffset = BValue / WoPI; float VScale = (AValue * source_dims.y) / WoPI; float4 YAccum = float4(0.0); float4 IAccum = float4(0.0); float4 QAccum = float4(0.0); float4 Cy = float4(in.v_uv.y); float4 VPosition = Cy; float4 SincY1; float _259; float _274; float _290; float _306; float4 SincY2; float _322; float _337; float _352; float _367; float4 SincY3; float _383; float _398; float _413; float _428; float4 IdealI; float _457; float _474; float _491; float _508; float4 IdealQ; float _526; float _543; float _560; float _577; for (float i = 0.0; i < 64.0; i += 4.0) { float n = i - 32.0; float4 n4 = float4(n) + float4(0.0, 1.0, 2.0, 3.0); float4 Cx = float4(in.v_uv.x) + ((n4 * 0.25) / float4(source_dims.x)); float4 HPosition = Cx; float4 C = Source.sample(SourceSmplr, float2(Cx.x, Cy.x)); float4 T = (HPosition + float4(HOffset)) + (VPosition * float4(VScale)); float4 WT = T * W; float4 SincKernel = float4(0.540000021457672119140625) + (cos(n4 * PI2Length) * 0.4600000083446502685546875); float4 SincYIn1 = n4 * Fc_y1_pi2; float4 SincYIn2 = n4 * Fc_y2_pi2; float4 SincYIn3 = n4 * Fc_y3_pi2; float4 SincIIn = n4 * Fc_i_pi2; float4 SincQIn = n4 * Fc_q_pi2; if (SincYIn1.x != 0.0) { _259 = sin(SincYIn1.x) / SincYIn1.x; } else { _259 = 1.0; } SincY1.x = _259; if (SincYIn1.y != 0.0) { _274 = sin(SincYIn1.y) / SincYIn1.y; } else { _274 = 1.0; } SincY1.y = _274; if (SincYIn1.z != 0.0) { _290 = sin(SincYIn1.z) / SincYIn1.z; } else { _290 = 1.0; } SincY1.z = _290; if (SincYIn1.w != 0.0) { _306 = sin(SincYIn1.w) / SincYIn1.w; } else { _306 = 1.0; } SincY1.w = _306; if (SincYIn2.x != 0.0) { _322 = sin(SincYIn2.x) / SincYIn2.x; } else { _322 = 1.0; } SincY2.x = _322; if (SincYIn2.y != 0.0) { _337 = sin(SincYIn2.y) / SincYIn2.y; } else { _337 = 1.0; } SincY2.y = _337; if (SincYIn2.z != 0.0) { _352 = sin(SincYIn2.z) / SincYIn2.z; } else { _352 = 1.0; } SincY2.z = _352; if (SincYIn2.w != 0.0) { _367 = sin(SincYIn2.w) / SincYIn2.w; } else { _367 = 1.0; } SincY2.w = _367; if (SincYIn3.x != 0.0) { _383 = sin(SincYIn3.x) / SincYIn3.x; } else { _383 = 1.0; } SincY3.x = _383; if (SincYIn3.y != 0.0) { _398 = sin(SincYIn3.y) / SincYIn3.y; } else { _398 = 1.0; } SincY3.y = _398; if (SincYIn3.z != 0.0) { _413 = sin(SincYIn3.z) / SincYIn3.z; } else { _413 = 1.0; } SincY3.z = _413; if (SincYIn3.w != 0.0) { _428 = sin(SincYIn3.w) / SincYIn3.w; } else { _428 = 1.0; } SincY3.w = _428; float4 IdealY = ((SincY1 * Fc_y1_2) - (SincY2 * Fc_y2_2)) + (SincY3 * Fc_y3_2); if (SincIIn.x != 0.0) { _457 = sin(SincIIn.x) / SincIIn.x; } else { _457 = 1.0; } IdealI.x = Fc_i_2 * _457; if (SincIIn.y != 0.0) { _474 = sin(SincIIn.y) / SincIIn.y; } else { _474 = 1.0; } IdealI.y = Fc_i_2 * _474; if (SincIIn.z != 0.0) { _491 = sin(SincIIn.z) / SincIIn.z; } else { _491 = 1.0; } IdealI.z = Fc_i_2 * _491; if (SincIIn.w != 0.0) { _508 = sin(SincIIn.w) / SincIIn.w; } else { _508 = 1.0; } IdealI.w = Fc_i_2 * _508; if (SincQIn.x != 0.0) { _526 = sin(SincQIn.x) / SincQIn.x; } else { _526 = 1.0; } IdealQ.x = Fc_q_2 * _526; if (SincQIn.y != 0.0) { _543 = sin(SincQIn.y) / SincQIn.y; } else { _543 = 1.0; } IdealQ.y = Fc_q_2 * _543; if (SincQIn.z != 0.0) { _560 = sin(SincQIn.z) / SincQIn.z; } else { _560 = 1.0; } IdealQ.z = Fc_q_2 * _560; if (SincQIn.w != 0.0) { _577 = sin(SincQIn.w) / SincQIn.w; } else { _577 = 1.0; } IdealQ.w = Fc_q_2 * _577; float4 FilterY = SincKernel * IdealY; float4 FilterI = SincKernel * IdealI; float4 FilterQ = SincKernel * IdealQ; YAccum += (C * FilterY); IAccum += ((C * cos(WT)) * FilterI); QAccum += ((C * sin(WT)) * FilterQ); } float3 YIQ = float3(((YAccum.x + YAccum.y) + YAccum.z) + YAccum.w, (((IAccum.x + IAccum.y) + IAccum.z) + IAccum.w) * 2.0, (((QAccum.x + QAccum.y) + QAccum.z) + QAccum.w) * 2.0); float3 RGB = float3(dot(YIQ, float3(1.0, 0.95599997043609619140625, 0.620999991893768310546875)), dot(YIQ, float3(1.0, -0.272000014781951904296875, -0.647000014781951904296875)), dot(YIQ, float3(1.0, -1.10599994659423828125, 1.70299994945526123046875))); out.FragColor = float4(RGB, BaseTexel.w); return out; }