From f11132bb4be3277ba4df99bffbf9ae67dcffb3f3 Mon Sep 17 00:00:00 2001 From: Jaromil Date: Mon, 6 Oct 2025 22:32:36 +0200 Subject: [PATCH 1/4] fix: correct use of snprintf and strtok --- src/filter/curves/curves.c | 4 ++-- src/filter/measure/measure_pr0be.c | 15 +++++++-------- src/filter/measure/measure_pr0file.c | 19 +++++++++---------- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/filter/curves/curves.c b/src/filter/curves/curves.c index 5983ea5..edc789d 100644 --- a/src/filter/curves/curves.c +++ b/src/filter/curves/curves.c @@ -569,11 +569,11 @@ int tokenise(char *string, const char *delimiter, char ***tokens) int count = 0; char *input = strdup(string); char *result = NULL; - result = strtok(input, delimiter); + result = strtok_r(string, delimiter, &input); while (result != NULL) { *tokens = realloc(*tokens, (count + 1) * sizeof(char *)); (*tokens)[count++] = strdup(result); - result = strtok(NULL, delimiter); + result = strtok_r(NULL, delimiter, &input); } free(input); return count; diff --git a/src/filter/measure/measure_pr0be.c b/src/filter/measure/measure_pr0be.c index a9e6798..11527ad 100644 --- a/src/filter/measure/measure_pr0be.c +++ b/src/filter/measure/measure_pr0be.c @@ -38,7 +38,7 @@ Copyright (C) 2010 Marko Cebokli http://lea.hamradio.si/~s57uuu double PI=3.14159265358979; //--------------------------------------------------------------- -void draw_rectangle(float_rgba *s, int w, int h, float x, float y, float wr, float hr, float_rgba c) +static inline void draw_rectangle(float_rgba *s, int w, int h, float x, float y, float wr, float hr, float_rgba c) { int i,j; int zx,kx,zy,ky; @@ -139,7 +139,7 @@ draw_rectangle(s, w, h, x1, y1+1, v, 1, black); //justified //p=0 one decimal place p=1 three decimal places //m=1 always show sign -void forstr(float a, int p, int m, char *s) +inline static void forstr(float a, int p, int m, char *s) { float b; char *p3=" %5.3f"; @@ -192,14 +192,14 @@ if (mm==1) forstr(s.rms,1-u,0,rs); forstr(s.min,1-u,m,ns); forstr(s.max,1-u,m,xs); - sprintf(fs,"%s%s%s %s%s", lab, as, rs, ns, xs); + snprintf(fs,255,"%s%s%s %s%s", lab, as, rs, ns, xs); sprintf(str,fs,s.avg,s.rms,s.min,s.max); } else { forstr(s.avg,1-u,m,as); forstr(s.rms,1-u,0,rs); - sprintf(fs,"%s%s%s", lab, as, rs); + snprintf(fs,255,"%s%s%s", lab, as, rs); sprintf(str,fs,s.avg,s.rms); } } @@ -251,7 +251,7 @@ if (sx>np) s[w*y+x]=white; x=x0+(np+2)*vp-i-1; s[w*y+x]=white; - } + } } if (sy>np) { @@ -263,7 +263,7 @@ if (sy>np) s[w*y+x]=white; y=y0+(np+2)*vp-i-1; s[w*y+x]=white; - } + } } } @@ -301,7 +301,7 @@ y0=h/20; if (bw==1) //big window { vx=240; - vy = (m<=2) ? 320 : 300; + vy = (m<=2) ? 320 : 300; x0 = (*poz==0) ? h/20 : w-h/20-vx; np=25; //size of magnifier xn = (m<=2) ? x0+8 : x0+70; @@ -745,4 +745,3 @@ crosshair(in->sl, in->w, in->h, in->x, in->y, 2*in->sx+1, 2*in->sy+1, 15); floatrgba2color(in->sl, outframe, in->w , in->h); } - diff --git a/src/filter/measure/measure_pr0file.c b/src/filter/measure/measure_pr0file.c index 83c5ef6..72335b1 100644 --- a/src/filter/measure/measure_pr0file.c +++ b/src/filter/measure/measure_pr0file.c @@ -114,7 +114,7 @@ while (c[i]!=0) //justified //p=0 one decimal place p=1 three decimal places //m=1 always show sign -void forstr(float a, int p, int m, char *s) +inline static void forstr(float a, int p, int m, char *s) { float b; char *p3=" %5.3f"; @@ -320,7 +320,7 @@ if ((dit&0x00000001)!=0) //marker 1 value if (m1>0) { forstr(data[0],1-u,0,frs); - sprintf(fs,"%%s Mk1=%s", frs); + snprintf(fs,255,"%%s Mk1=%s", frs); sprintf(str,fs,str,data[0]); } else @@ -331,7 +331,7 @@ if ((dit&0x00000004)!=0) //marker 2 value if (m2>0) { forstr(data[1],1-u,0,frs); - sprintf(fs,"%%s Mk2=%s", frs); + snprintf(fs,255,"%%s Mk2=%s", frs); sprintf(str,fs,str,data[1]); } else @@ -342,7 +342,7 @@ if ((dit&0x00000010)!=0) //difference marker2-marker1 if ((m2>0)&&(m1>0)) { forstr(data[2],1-u,0,frs); - sprintf(fs,"%%s D=%s", frs); + snprintf(fs,255,"%%s D=%s", frs); sprintf(str,fs,str,data[2]); } else @@ -351,25 +351,25 @@ if ((dit&0x00000010)!=0) //difference marker2-marker1 if ((dit&0x00000020)!=0) //average of profile { forstr(data[3],1-u,0,frs); - sprintf(fs,"%%s Avg=%s", frs); + snprintf(fs,255,"%%s Avg=%s", frs); sprintf(str,fs,str,data[3]); } if ((dit&0x00000040)!=0) //RMS of profile { forstr(data[4],1-u,0,frs); - sprintf(fs,"%%s RMS=%s", frs); + snprintf(fs,255,"%%s RMS=%s", frs); sprintf(str,fs,str,data[4]); } if ((dit&0x00000080)!=0) //MIN of profile { forstr(data[5],1-u,0,frs); - sprintf(fs,"%%s Min=%s", frs); + snprintf(fs,255,"%%s Min=%s", frs); sprintf(str,fs,str,data[5]); } if ((dit&0x00000100)!=0) //MAX of profile { forstr(data[6],1-u,0,frs); - sprintf(fs,"%%s Max=%s", frs); + snprintf(fs,255,"%%s Max=%s", frs); sprintf(str,fs,str,data[6]); } } @@ -408,7 +408,7 @@ if (yh/2+20) *poz=0; //top x0=h/20; vx=w*15/16; -vy = h*6/16; +vy = h*6/16; y0 = (*poz==0) ? h/20 : h-h/20-vy; //end points of profile @@ -1035,4 +1035,3 @@ prof(in->sl, in->w, in->h, &in->poz, in->x, in->y, in->tilt, in->len, 1, in->mer floatrgba2color(in->sl, outframe, in->w , in->h); } - From 772d2e5edb96cd7368ed99fe9d510e781be7adf6 Mon Sep 17 00:00:00 2001 From: Jaromil Date: Mon, 6 Oct 2025 22:42:32 +0200 Subject: [PATCH 2/4] fix: strtok_r is _s on windows --- src/filter/curves/curves.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/filter/curves/curves.c b/src/filter/curves/curves.c index edc789d..f30181f 100644 --- a/src/filter/curves/curves.c +++ b/src/filter/curves/curves.c @@ -557,6 +557,10 @@ position pointOnBezier(double t, position points[4]) return pos; } +#if defined(_WIN32) || defined(_WIN64) +# define strtok_r strtok_s +#endif + /** * Splits given string into sub-strings at given delimiter. * \param string input string From def52c449fba9d55508a0e0df623f7e376391239 Mon Sep 17 00:00:00 2001 From: esmane <48299327+esmane@users.noreply.github.com> Date: Fri, 5 Sep 2025 21:36:17 -0400 Subject: [PATCH 3/4] Improve (and simplify) NTSC filter --- src/filter/ntsc/crt_core.c | 277 ++++++++++++++++++++++------------ src/filter/ntsc/crt_core.h | 74 +++++++-- src/filter/ntsc/crt_ntsc.c | 150 +++++++++--------- src/filter/ntsc/crt_ntsc.h | 61 ++------ src/filter/ntsc/ntsc-effect.c | 164 +++++++------------- 5 files changed, 384 insertions(+), 342 deletions(-) diff --git a/src/filter/ntsc/crt_core.c b/src/filter/ntsc/crt_core.c index 1202d38..3dfed0c 100644 --- a/src/filter/ntsc/crt_core.c +++ b/src/filter/ntsc/crt_core.c @@ -60,6 +60,23 @@ crt_sincos14(int *s, int *c, int n) } } +extern int +crt_bpp4fmt(int format) +{ + switch (format) { + case CRT_PIX_FORMAT_RGB: + case CRT_PIX_FORMAT_BGR: + return 3; + case CRT_PIX_FORMAT_ARGB: + case CRT_PIX_FORMAT_RGBA: + case CRT_PIX_FORMAT_ABGR: + case CRT_PIX_FORMAT_BGRA: + return 4; + default: + return 0; + } +} + /*****************************************************************************/ /********************************* FILTERS ***********************************/ /*****************************************************************************/ @@ -222,10 +239,11 @@ eqf(struct EQF *f, int s) /*****************************************************************************/ extern void -crt_resize(struct CRT *v, int w, int h, unsigned char *out) +crt_resize(struct CRT *v, int w, int h, int f, unsigned char *out) { v->outw = w; v->outh = h; + v->out_format = f; v->out = out; } @@ -239,22 +257,14 @@ crt_reset(struct CRT *v) v->black_point = 0; v->white_point = 100; v->hsync = 0; - v->vsync = 0; - - v->scanlines = 0; /* leave gaps between lines if necessary */ - v->blend = 0; /* blend new field onto previous image */ - - // these options were previously #defined in crt_core.h - v->crt_do_vsync = 1; - v->crt_do_hsync = 1; - v->do_vhs_noise = 0; + v->vsync = 0; } extern void -crt_init(struct CRT *v, int w, int h, unsigned char *out) +crt_init(struct CRT *v, int w, int h, int f, unsigned char *out) { memset(v, 0, sizeof(struct CRT)); - crt_resize(v, w, h, out); + crt_resize(v, w, h, f, out); crt_reset(v); v->rn = 194; @@ -293,58 +303,61 @@ crt_demodulate(struct CRT *v, int noise) int huesn, huecs; int xnudge = -3, ynudge = 3; int bright = v->brightness - (BLACK_LEVEL + v->black_point); - int pitch; + int bpp, pitch; +#if CRT_DO_BLOOM + int prev_e; /* filtered beam energy per scan line */ + int max_e; /* approx maximum energy in a scan line */ +#endif - pitch = v->outw * BPP; + bpp = crt_bpp4fmt(v->out_format); + if (bpp == 0) { + return; + } + pitch = v->outw * bpp; crt_sincos14(&huesn, &huecs, ((v->hue % 360) + 33) * 8192 / 180); huesn >>= 11; /* make 4-bit */ huecs >>= 11; rn = v->rn; - if(!v->crt_do_vsync) - { - /* determine field before we add noise, - * otherwise it's not reliably recoverable - */ - for (i = -CRT_VSYNC_WINDOW; i < CRT_VSYNC_WINDOW; i++) { - line = POSMOD(v->vsync + i, CRT_VRES); - sig = v->analog + line * CRT_HRES; - s = 0; - for (j = 0; j < CRT_HRES; j++) { - s += sig[j]; - if (s <= (CRT_VSYNC_THRESH * SYNC_LEVEL)) { - goto found_field; - } +#if !CRT_DO_VSYNC + /* determine field before we add noise, + * otherwise it's not reliably recoverable + */ + for (i = -CRT_VSYNC_WINDOW; i < CRT_VSYNC_WINDOW; i++) { + line = POSMOD(v->vsync + i, CRT_VRES); + sig = v->analog + line * CRT_HRES; + s = 0; + for (j = 0; j < CRT_HRES; j++) { + s += sig[j]; + if (s <= (CRT_VSYNC_THRESH * SYNC_LEVEL)) { + goto found_field; } } - found_field: - /* if vsync signal was in second half of line, odd field */ - field = (j > (CRT_HRES / 2)); - v->vsync = -3; - } - if(v->do_vhs_noise) - { - line = ((rand() % 8) - 4) + 14; } +found_field: + /* if vsync signal was in second half of line, odd field */ + field = (j > (CRT_HRES / 2)); + v->vsync = -3; +#endif +#if ((CRT_SYSTEM == CRT_SYSTEM_NTSCVHS) && CRT_VHS_NOISE) + line = ((rand() % 8) - 4) + 14; +#endif for (i = 0; i < CRT_INPUT_SIZE; i++) { int nn = noise; - if(v->do_vhs_noise) - { - rn = rand(); - if (i > (CRT_INPUT_SIZE - CRT_HRES * (16 + ((rand() % 20) - 10))) && - i < (CRT_INPUT_SIZE - CRT_HRES * (5 + ((rand() % 8) - 4)))) { - int ln, sn, cs; +#if ((CRT_SYSTEM == CRT_SYSTEM_NTSCVHS) && CRT_VHS_NOISE) + rn = rand(); + if (i > (CRT_INPUT_SIZE - CRT_HRES * (16 + ((rand() % 20) - 10))) && + i < (CRT_INPUT_SIZE - CRT_HRES * (5 + ((rand() % 8) - 4)))) { + int ln, sn, cs; - ln = (i * line) / CRT_HRES; - crt_sincos14(&sn, &cs, ln * 8192 / 180); - nn = cs >> 8; - } - } - else - { - rn = (214019 * rn + 140327895); + ln = (i * line) / CRT_HRES; + crt_sincos14(&sn, &cs, ln * 8192 / 180); + nn = cs >> 8; } +#else + rn = (214019 * rn + 140327895); +#endif /* signal + noise */ s = v->analog[i] + (((((rn >> 16) & 0xff) - 0x7f) * nn) >> 8); if (s > 127) { s = 127; } @@ -353,37 +366,40 @@ crt_demodulate(struct CRT *v, int noise) } v->rn = rn; - if(v->crt_do_vsync) - { - /* Look for vertical sync. - * - * This is done by integrating the signal and - * seeing if it exceeds a threshold. The threshold of - * the vertical sync pulse is much higher because the - * vsync pulse is a lot longer than the hsync pulse. - * The signal needs to be integrated to lessen - * the noise in the signal. - */ - for (i = -CRT_VSYNC_WINDOW; i < CRT_VSYNC_WINDOW; i++) { - line = POSMOD(v->vsync + i, CRT_VRES); - sig = v->inp + line * CRT_HRES; - s = 0; - for (j = 0; j < CRT_HRES; j++) { - s += sig[j]; - /* increase the multiplier to make the vsync - * more stable when there is a lot of noise - */ - if (s <= (CRT_VSYNC_THRESH * SYNC_LEVEL)) { - goto vsync_found; - } +#if CRT_DO_VSYNC + /* Look for vertical sync. + * + * This is done by integrating the signal and + * seeing if it exceeds a threshold. The threshold of + * the vertical sync pulse is much higher because the + * vsync pulse is a lot longer than the hsync pulse. + * The signal needs to be integrated to lessen + * the noise in the signal. + */ + for (i = -CRT_VSYNC_WINDOW; i < CRT_VSYNC_WINDOW; i++) { + line = POSMOD(v->vsync + i, CRT_VRES); + sig = v->inp + line * CRT_HRES; + s = 0; + for (j = 0; j < CRT_HRES; j++) { + s += sig[j]; + /* increase the multiplier to make the vsync + * more stable when there is a lot of noise + */ + if (s <= (CRT_VSYNC_THRESH * SYNC_LEVEL)) { + goto vsync_found; } } - vsync_found: - v->vsync = line; /* vsync found (or gave up) at this line */ - /* if vsync signal was in second half of line, odd field */ - field = (j > (CRT_HRES / 2)); - } + } +vsync_found: + v->vsync = line; /* vsync found (or gave up) at this line */ + /* if vsync signal was in second half of line, odd field */ + field = (j > (CRT_HRES / 2)); +#endif +#if CRT_DO_BLOOM + max_e = (128 + (noise / 2)) * AV_LEN; + prev_e = (16384 / 8); +#endif /* ratio of output height to active video lines in the signal */ ratio = (v->outh << 16) / CRT_LINES; ratio = (ratio + 32768) >> 16; @@ -405,6 +421,9 @@ crt_demodulate(struct CRT *v, int noise) int xpos, ypos; int beg, end; int phasealign; +#if CRT_DO_BLOOM + int line_w; +#endif beg = (line - CRT_TOP + 0) * (v->outh + v->v_fac) / CRT_LINES + field; end = (line - CRT_TOP + 1) * (v->outh + v->v_fac) / CRT_LINES + field; @@ -424,14 +443,11 @@ crt_demodulate(struct CRT *v, int noise) break; } } - if(v->crt_do_hsync) - { - v->hsync = POSMOD(i + v->hsync, CRT_HRES); - } - else - { - v->hsync = 0; - } +#if CRT_DO_HSYNC + v->hsync = POSMOD(i + v->hsync, CRT_HRES); +#else + v->hsync = 0; +#endif xpos = POSMOD(AV_BEG + v->hsync + xnudge, CRT_HRES); ypos = POSMOD(line + v->vsync + ynudge, CRT_VRES); @@ -493,13 +509,28 @@ crt_demodulate(struct CRT *v, int noise) } #endif sig = v->inp + pos; +#if CRT_DO_BLOOM + s = 0; + for (i = 0; i < AV_LEN; i++) { + s += sig[i]; /* sum up the scan line */ + } + /* bloom emulation */ + prev_e = (prev_e * 123 / 128) + ((((max_e >> 1) - s) << 10) / max_e); + line_w = (AV_LEN * 112 / 128) + (prev_e >> 9); + dx = (line_w << 12) / v->outw; + scanL = ((AV_LEN / 2) - (line_w >> 1) + 8) << 12; + scanR = (AV_LEN - 1) << 12; + + L = (scanL >> 12); + R = (scanR >> 12); +#else dx = ((AV_LEN - 1) << 12) / v->outw; scanL = 0; scanR = (AV_LEN - 1) << 12; L = 0; - R = AV_LEN; - + R = AV_LEN; +#endif reset_eq(&eqY); reset_eq(&eqI); reset_eq(&eqQ); @@ -552,8 +583,26 @@ crt_demodulate(struct CRT *v, int noise) if (v->blend) { aa = (r << 16 | g << 8 | b); - bb = cL[0] << 16 | cL[1] << 8 | cL[2]; + switch (v->out_format) { + case CRT_PIX_FORMAT_RGB: + case CRT_PIX_FORMAT_RGBA: + bb = cL[0] << 16 | cL[1] << 8 | cL[2]; + break; + case CRT_PIX_FORMAT_BGR: + case CRT_PIX_FORMAT_BGRA: + bb = cL[2] << 16 | cL[1] << 8 | cL[0]; + break; + case CRT_PIX_FORMAT_ARGB: + bb = cL[1] << 16 | cL[2] << 8 | cL[3]; + break; + case CRT_PIX_FORMAT_ABGR: + bb = cL[3] << 16 | cL[2] << 8 | cL[1]; + break; + default: + bb = 0; + break; + } /* blend with previous color there */ bb = (((aa & 0xfefeff) >> 1) + ((bb & 0xfefeff) >> 1)); @@ -561,12 +610,52 @@ crt_demodulate(struct CRT *v, int noise) bb = (r << 16 | g << 8 | b); } - cL[0] = bb >> 16 & 0xff; - cL[1] = bb >> 8 & 0xff; - cL[2] = bb >> 0 & 0xff; - cL[3] = 0xff; + switch (v->out_format) { + case CRT_PIX_FORMAT_RGB: + cL[0] = bb >> 16 & 0xff; + cL[1] = bb >> 8 & 0xff; + cL[2] = bb >> 0 & 0xff; + break; - cL += BPP; + case CRT_PIX_FORMAT_RGBA: + cL[0] = bb >> 16 & 0xff; + cL[1] = bb >> 8 & 0xff; + cL[2] = bb >> 0 & 0xff; + cL[3] = 0xff; + break; + + case CRT_PIX_FORMAT_BGR: + cL[0] = bb >> 0 & 0xff; + cL[1] = bb >> 8 & 0xff; + cL[2] = bb >> 16 & 0xff; + break; + + case CRT_PIX_FORMAT_BGRA: + cL[0] = bb >> 0 & 0xff; + cL[1] = bb >> 8 & 0xff; + cL[2] = bb >> 16 & 0xff; + cL[3] = 0xff; + break; + + case CRT_PIX_FORMAT_ARGB: + cL[0] = 0xff; + cL[1] = bb >> 16 & 0xff; + cL[2] = bb >> 8 & 0xff; + cL[3] = bb >> 0 & 0xff; + break; + + case CRT_PIX_FORMAT_ABGR: + cL[0] = 0xff; + cL[1] = bb >> 0 & 0xff; + cL[2] = bb >> 8 & 0xff; + cL[3] = bb >> 16 & 0xff; + break; + + default: + break; + } + + cL += bpp; } /* duplicate extra lines */ diff --git a/src/filter/ntsc/crt_core.h b/src/filter/ntsc/crt_core.h index eed9332..456191b 100644 --- a/src/filter/ntsc/crt_core.h +++ b/src/filter/ntsc/crt_core.h @@ -18,36 +18,72 @@ extern "C" { /* crt_core.h * * The demodulator. This is also where you can define which system to emulate. - * + * */ - + /* library version */ #define CRT_MAJOR 2 #define CRT_MINOR 3 #define CRT_PATCH 2 -#include "crt_ntsc.h" - -// frei0r always uses 4 bits per pixel so it is pointless for there to be any other possibilities -#define BPP 4 + +#define CRT_SYSTEM_NTSC 0 /* standard NTSC */ +#define CRT_SYSTEM_NES 1 /* decode 6 or 9-bit NES pixels */ +#define CRT_SYSTEM_PV1K 2 /* Casio PV-1000 */ +#define CRT_SYSTEM_SNES 3 /* SNES - uses RGB */ +#define CRT_SYSTEM_TEMP 4 /* template implementation */ +#define CRT_SYSTEM_NTSCVHS 5 /* standard NTSC VHS */ +#define CRT_SYSTEM_NESRGB 6 /* encode RGB image with NES artifacts */ + +/* the system to be compiled */ +#ifndef CRT_SYSTEM +#define CRT_SYSTEM CRT_SYSTEM_NTSC +#endif + +#if (CRT_SYSTEM == CRT_SYSTEM_NES) +#include "crt_nes.h" +#elif (CRT_SYSTEM == CRT_SYSTEM_SNES) +#include "crt_snes.h" +#elif (CRT_SYSTEM == CRT_SYSTEM_NTSC) +#include "crt_ntsc.h" +#elif (CRT_SYSTEM == CRT_SYSTEM_PV1K) +#include "crt_pv1k.h" +#elif (CRT_SYSTEM == CRT_SYSTEM_TEMP) +#include "crt_template.h" +#elif (CRT_SYSTEM == CRT_SYSTEM_NTSCVHS) +#include "crt_ntscvhs.h" +#elif (CRT_SYSTEM == CRT_SYSTEM_NESRGB) +#include "crt_nesrgb.h" +#else +#error No system defined +#endif + +/* NOTE: this library does not use the alpha channel at all */ +#define CRT_PIX_FORMAT_RGB 0 /* 3 bytes per pixel [R,G,B,R,G,B,R,G,B...] */ +#define CRT_PIX_FORMAT_BGR 1 /* 3 bytes per pixel [B,G,R,B,G,R,B,G,R...] */ +#define CRT_PIX_FORMAT_ARGB 2 /* 4 bytes per pixel [A,R,G,B,A,R,G,B...] */ +#define CRT_PIX_FORMAT_RGBA 3 /* 4 bytes per pixel [R,G,B,A,R,G,B,A...] */ +#define CRT_PIX_FORMAT_ABGR 4 /* 4 bytes per pixel [A,B,G,R,A,B,G,R...] */ +#define CRT_PIX_FORMAT_BGRA 5 /* 4 bytes per pixel [B,G,R,A,B,G,R,A...] */ + +/* do bloom emulation (side effect: makes screen have black borders) */ +#define CRT_DO_BLOOM 0 /* does not work for NES */ +#define CRT_DO_VSYNC 1 /* look for VSYNC */ +#define CRT_DO_HSYNC 1 /* look for HSYNC */ struct CRT { signed char analog[CRT_INPUT_SIZE]; signed char inp[CRT_INPUT_SIZE]; /* CRT input, can be noisy */ int outw, outh; /* output width/height */ + int out_format; /* output pixel format (one of the CRT_PIX_FORMATs) */ unsigned char *out; /* output image */ int hue, brightness, contrast, saturation; /* common monitor settings */ int black_point, white_point; /* user-adjustable */ int scanlines; /* leave gaps between lines if necessary */ int blend; /* blend new field onto previous image */ - unsigned v_fac; /* factor to stretch img vertically onto the output img */ - - // these options were previously #defined in crt_core.h - int crt_do_vsync; - int crt_do_hsync; - int do_vhs_noise; /* 0 = no additional vhs noise, 1 = with additional vhs noise */ + unsigned v_fac; /* factor to stretch img vertically onto the output img */ /* internal data */ int ccf[CRT_CC_VPER][CRT_CC_SAMPLES]; /* faster color carrier convergence */ @@ -61,7 +97,7 @@ struct CRT { * f - format of the output image * out - pointer to output image data */ -extern void crt_init(struct CRT *v, int w, int h, unsigned char *out); +extern void crt_init(struct CRT *v, int w, int h, int f, unsigned char *out); /* Updates the output image parameters * w - width of the output image @@ -69,7 +105,7 @@ extern void crt_init(struct CRT *v, int w, int h, unsigned char *out); * f - format of the output image * out - pointer to output image data */ -extern void crt_resize(struct CRT *v, int w, int h, unsigned char *out); +extern void crt_resize(struct CRT *v, int w, int h, int f, unsigned char *out); /* Resets the CRT settings back to their defaults */ extern void crt_reset(struct CRT *v); @@ -78,12 +114,20 @@ extern void crt_reset(struct CRT *v); * s - struct containing settings to apply to this field */ extern void crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s); - + /* Demodulates the NTSC signal generated by crt_modulate() * noise - the amount of noise added to the signal (0 - inf) */ extern void crt_demodulate(struct CRT *v, int noise); +/* Get the bytes per pixel for a certain CRT_PIX_FORMAT_ + * + * format - the format to get the bytes per pixel for + * + * returns 0 if the specified format does not exist + */ +extern int crt_bpp4fmt(int format); + /*****************************************************************************/ /*************************** FIXED POINT SIN/COS *****************************/ /*****************************************************************************/ diff --git a/src/filter/ntsc/crt_ntsc.c b/src/filter/ntsc/crt_ntsc.c index 7933b73..107a2ea 100644 --- a/src/filter/ntsc/crt_ntsc.c +++ b/src/filter/ntsc/crt_ntsc.c @@ -1,9 +1,9 @@ /*****************************************************************************/ /* * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation - * + * * by EMMIR 2018-2023 - * + * * YouTube: https://www.youtube.com/@EMMIR_KC/videos * Discord: https://discord.com/invite/hdYctSmyQJ */ @@ -11,7 +11,7 @@ #include "crt_core.h" -#if (CRT_SYSTEM == CRT_SYSTEM_NTSCVHS) +#if (CRT_SYSTEM == CRT_SYSTEM_NTSC) #include #include @@ -35,7 +35,7 @@ static int e11[] = { 15133, /* e^2 */ 41135, /* e^3 */ 111817 /* e^4 */ -}; +}; /* fixed point e^x */ static int @@ -61,7 +61,7 @@ expx(int n) if (idx > 0) { res = EXP_MUL(res, e11[idx]); } - + n &= EXP_MASK; nxt = EXP_ONE; acc = 0; @@ -99,7 +99,7 @@ static void init_iir(struct IIRLP *f, int freq, int limit) { int rate; /* cycles/pixel rate */ - + memset(f, 0, sizeof(struct IIRLP)); rate = (freq << 9) / limit; f->c = EXP_ONE - expx(-((EXP_PI << 9) / rate)); @@ -137,41 +137,29 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) int ccburst[CRT_CC_SAMPLES]; /* color phase for burst */ int sn, cs, n, ph; int inv_phase = 0; - int aberration = 0; + int bpp; if (!s->iirs_initialized) { - int y_freq = Y_FREQ_OFF; - int i_freq = I_FREQ_OFF; - int q_freq = Q_FREQ_OFF; - - switch(s->vhs_mode) - { - case VHS_OFF: - break; - case VHS_SP: - y_freq = Y_FREQ_SP; - i_freq = I_FREQ_SP; - q_freq = Q_FREQ_SP; - break; - case VHS_LP: - y_freq = Y_FREQ_LP; - i_freq = I_FREQ_LP; - q_freq = Q_FREQ_LP; - break; - case VHS_EP: - y_freq = Y_FREQ_EP; - i_freq = I_FREQ_EP; - q_freq = Q_FREQ_EP; - break; - default: - break; - } - - init_iir(&iirY, L_FREQ, y_freq); - init_iir(&iirI, L_FREQ, i_freq); - init_iir(&iirQ, L_FREQ, q_freq); + init_iir(&iirY, L_FREQ, Y_FREQ); + init_iir(&iirI, L_FREQ, I_FREQ); + init_iir(&iirQ, L_FREQ, Q_FREQ); s->iirs_initialized = 1; } +#if CRT_DO_BLOOM + if (s->raw) { + destw = s->w; + desth = s->h; + if (destw > ((AV_LEN * 55500) >> 16)) { + destw = ((AV_LEN * 55500) >> 16); + } + if (desth > ((CRT_LINES * 63500) >> 16)) { + desth = ((CRT_LINES * 63500) >> 16); + } + } else { + destw = (AV_LEN * 55500) >> 16; + desth = (CRT_LINES * 63500) >> 16; + } +#else if (s->raw) { destw = s->w; desth = s->h; @@ -182,7 +170,7 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) desth = ((CRT_LINES * 64500) >> 16); } } - +#endif if (s->as_color) { for (x = 0; x < CRT_CC_SAMPLES; x++) { n = s->hue + x * (360 / CRT_CC_SAMPLES); @@ -198,10 +186,14 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) memset(ccmodI, 0, sizeof(ccmodI)); memset(ccmodQ, 0, sizeof(ccmodQ)); } - + + bpp = crt_bpp4fmt(s->format); + if (bpp == 0) { + return; /* just to be safe */ + } xo = AV_BEG + s->xoffset + (AV_LEN - destw) / 2; yo = CRT_TOP + s->yoffset + (CRT_LINES - desth) / 2; - + s->field &= 1; s->frame &= 1; inv_phase = (s->field == s->frame); @@ -209,13 +201,11 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) /* align signal */ xo = (xo & ~3); - if (s->do_aberration) { - aberration = ((rand() % 12) - 8) + 14; - } + for (n = 0; n < CRT_VRES; n++) { int t; /* time */ signed char *line = &v->analog[n * CRT_HRES]; - + t = LINE_BEG; if (n <= 3 || (n >= 7 && n <= 9)) { @@ -238,13 +228,11 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) while (t < (offs[3] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; } else { int cb; - if (n < (CRT_VRES - aberration)) { - /* video line */ - while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ - while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ - } - while (t < AV_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ + /* video line */ + while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ + while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ + while (t < AV_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ if (n < CRT_TOP) { while (t < CRT_HRES) line[t++] = BLANK_LEVEL; } @@ -261,50 +249,67 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) iccf[t % CRT_CC_SAMPLES] = line[t]; } } - } - - if(s->vhs_mode != VHS_OFF) - { - /* reset hsync every frame so only the bottom part is warped */ - v->hsync = 0; } for (y = 0; y < desth; y++) { int field_offset; int sy; - + field_offset = (s->field * s->h + desth) / desth / 2; sy = (y * s->h) / desth; - + sy += field_offset; if (sy >= s->h) sy = s->h; - + sy *= s->w; - + reset_iir(&iirY); reset_iir(&iirI); reset_iir(&iirQ); - + for (x = 0; x < destw; x++) { int fy, fi, fq; int rA, gA, bA; const unsigned char *pix; int ire; /* composite signal */ int xoff; - - pix = s->data + ((((x * s->w) / destw) + sy) * BPP); - rA = pix[0]; - gA = pix[1]; - bA = pix[2]; - + + pix = s->data + ((((x * s->w) / destw) + sy) * bpp); + switch (s->format) { + case CRT_PIX_FORMAT_RGB: + case CRT_PIX_FORMAT_RGBA: + rA = pix[0]; + gA = pix[1]; + bA = pix[2]; + break; + case CRT_PIX_FORMAT_BGR: + case CRT_PIX_FORMAT_BGRA: + rA = pix[2]; + gA = pix[1]; + bA = pix[0]; + break; + case CRT_PIX_FORMAT_ARGB: + rA = pix[1]; + gA = pix[2]; + bA = pix[3]; + break; + case CRT_PIX_FORMAT_ABGR: + rA = pix[3]; + gA = pix[2]; + bA = pix[1]; + break; + default: + rA = gA = bA = 0; + break; + } /* RGB to YIQ */ fy = (19595 * rA + 38470 * gA + 7471 * bA) >> 14; fi = (39059 * rA - 18022 * gA - 21103 * bA) >> 14; fq = (13894 * rA - 34275 * gA + 20382 * bA) >> 14; ire = BLACK_LEVEL + v->black_point; - + xoff = (x + xo) % CRT_CC_SAMPLES; /* bandlimit Y,I,Q */ fy = iirf(&iirY, fy); @@ -319,14 +324,7 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) } for (n = 0; n < CRT_CC_VPER; n++) { for (x = 0; x < CRT_CC_SAMPLES; x++) { - if(s->vhs_mode != VHS_OFF) - { - v->ccf[n][x] = 0; - } - else - { - v->ccf[n][x] = iccf[x] << 7; - } + v->ccf[n][x] = iccf[x] << 7; } } } diff --git a/src/filter/ntsc/crt_ntsc.h b/src/filter/ntsc/crt_ntsc.h index a979ff9..2a6f0e9 100644 --- a/src/filter/ntsc/crt_ntsc.h +++ b/src/filter/ntsc/crt_ntsc.h @@ -8,18 +8,17 @@ * Discord: https://discord.com/invite/hdYctSmyQJ */ /*****************************************************************************/ -#ifndef _CRT_NTSC_VHS_H_ -#define _CRT_NTSC_VHS_H_ +#ifndef _CRT_NTSC_H_ +#define _CRT_NTSC_H_ #ifdef __cplusplus extern "C" { #endif -/* crt_ntscvhs.h - * - * An interface to convert a digital image to an analog NTSC signal with VHS - * quality and some optional signal aberration at the bottom. +/* crt_ntsc.h * + * An interface to convert a digital image to an analog NTSC signal. + * */ /* 0 = vertical chroma (228 chroma clocks per line) */ /* 1 = checkered chroma (227.5 chroma clocks per line) */ @@ -42,7 +41,7 @@ extern "C" { #define CRT_TOP 21 /* first line with active video */ #define CRT_BOT 261 /* final line with active video */ #define CRT_LINES (CRT_BOT - CRT_TOP) /* number of active video lines */ - + #define CRT_CC_SAMPLES 4 /* samples per chroma period (samples per 360 deg) */ #define CRT_CC_VPER 1 /* vertical period in which the artifacts repeat */ @@ -62,14 +61,14 @@ extern "C" { * |---------------------------------------------------------------------------| * HBLANK (~10900 ns) ACTIVE VIDEO (~52600 ns) * |-------------------||------------------------------------------------------| - * - * + * + * * WITHIN HBLANK PERIOD: - * + * * FP (~1500 ns) SYNC (~4700 ns) BW (~600 ns) CB (~2500 ns) BP (~1600 ns) * |--------------||---------------||------------||-------------||-------------| * BLANK SYNC BLANK BLANK BLANK - * + * */ #define LINE_BEG 0 #define FP_ns 1500 /* front porch */ @@ -95,33 +94,12 @@ extern "C" { /* somewhere between 7 and 12 cycles */ #define CB_CYCLES 10 - -#define VHS_OFF 0 -#define VHS_SP 1 -#define VHS_LP 2 -#define VHS_EP 3 - -#define VHS_MODE VHS_OFF - -/* frequencies for bandlimiting */ -#define L_FREQ 1431818 /* full line */ - -#define Y_FREQ_OFF 420000 /* Luma (Y) 4.2 MHz of the 14.31818 MHz */ -#define I_FREQ_OFF 150000 /* Chroma (I) 1.5 MHz of the 14.31818 MHz */ -#define Q_FREQ_OFF 55000 /* Chroma (Q) 0.55 MHz of the 14.31818 MHz */ - -#define Y_FREQ_SP 300000 /* Luma (Y) 3.0 MHz of the 14.31818 MHz */ -#define I_FREQ_SP 62700 /* Chroma (I) 627 kHz of the 14.31818 MHz */ -#define Q_FREQ_SP 62700 /* Chroma (Q) 627 kHz of the 14.31818 MHz */ - -#define Y_FREQ_LP 240000 /* Luma (Y) 2.4 MHz of the 14.31818 MHz */ -#define I_FREQ_LP 40000 /* Chroma (I) 400 kHz of the 14.31818 MHz */ -#define Q_FREQ_LP 40000 /* Chroma (Q) 400 kHz of the 14.31818 MHz */ - -#define Y_FREQ_EP 200000 /* Luma (Y) 2.0 MHz of the 14.31818 MHz */ -#define I_FREQ_EP 37000 /* Chroma (I) 370 kHz of the 14.31818 MHz */ -#define Q_FREQ_EP 37000 /* Chroma (Q) 370 kHz of the 14.31818 MHz */ +/* frequencies for bandlimiting */ +#define L_FREQ 1431818 /* full line */ +#define Y_FREQ 420000 /* Luma (Y) 4.2 MHz of the 14.31818 MHz */ +#define I_FREQ 150000 /* Chroma (I) 1.5 MHz of the 14.31818 MHz */ +#define Q_FREQ 55000 /* Chroma (Q) 0.55 MHz of the 14.31818 MHz */ /* IRE units (100 = 1.0V, -40 = 0.0V) */ #define WHITE_LEVEL 100 @@ -132,6 +110,7 @@ extern "C" { struct NTSC_SETTINGS { const unsigned char *data; /* image data */ + int format; /* pix format (one of the CRT_PIX_FORMATs in crt_core.h) */ int w, h; /* width and height of image */ int raw; /* 0 = scale image to fit monitor, 1 = don't scale */ int as_color; /* 0 = monochrome, 1 = full color */ @@ -140,14 +119,6 @@ struct NTSC_SETTINGS { int hue; /* 0-359 */ int xoffset; /* x offset in sample space. 0 is minimum value */ int yoffset; /* y offset in # of lines. 0 is minimum value */ - int do_aberration; /* 0 = no aberration, 1 = with aberration */ - - // these are changed by the vhs mode - int vhs_mode; - int y_freq; - int i_freq; - int q_freq; - /* make sure your NTSC_SETTINGS struct is zeroed out before you do anything */ int iirs_initialized; /* internal state */ }; diff --git a/src/filter/ntsc/ntsc-effect.c b/src/filter/ntsc/ntsc-effect.c index e2b0c3b..3f2290a 100644 --- a/src/filter/ntsc/ntsc-effect.c +++ b/src/filter/ntsc/ntsc-effect.c @@ -1,11 +1,13 @@ #include +#include #include "frei0r.h" #include "frei0r/math.h" #include "crt_core.h" -// the actual NTSC emulation code is modified from here: https://github.com/LMP88959/NTSC-CRT +// the actual NTSC emulation code is from here: https://github.com/LMP88959/NTSC-CRT + typedef struct ntsc_instance { @@ -15,16 +17,15 @@ typedef struct ntsc_instance // parameters struct CRT crt; - struct NTSC_SETTINGS ntsc_settings; + struct NTSC_SETTINGS ntsc; int noise; - double vhs_speed; - int field; int progressive; } ntsc_instance_t; + // these functions are for frei0r // mostly copy/paste/slightly modified from the other frei0r effects int f0r_init() @@ -44,8 +45,8 @@ void f0r_get_plugin_info(f0r_plugin_info_t* info) info->color_model = F0R_COLOR_MODEL_RGBA8888; info->frei0r_version = FREI0R_MAJOR_VERSION; info->major_version = 0; - info->minor_version = 1; - info->num_params = 8; + info->minor_version = 2; + info->num_params = 3; } void f0r_get_param_info(f0r_param_info_t* info, int param_index) @@ -57,51 +58,22 @@ void f0r_get_param_info(f0r_param_info_t* info, int param_index) info->explanation = "Amount of noise introduced into the NTSC signal."; info->type = F0R_PARAM_DOUBLE; break; - + case 1: - info->name = "VHS Speed"; - info->explanation = "Simulates VHS. 0 = off, 1 = SP, 2 = LP, 3 = EP"; - info->type = F0R_PARAM_DOUBLE; - break; - - case 2: - info->name = "VHS Noise"; - info->explanation = "Toggles VHS noise at the bottom of the screen"; - info->type = F0R_PARAM_BOOL; - break; - - case 3: - info->name = "Aberration"; - info->explanation = "Toggles VHS aberration at the bottom of the screen. Not visible if V-sync is on."; - info->type = F0R_PARAM_BOOL; - break; - - case 4: - info->name = "Force V-sync"; - info->explanation = "Forces V-sync even when the signal is really noisy."; - info->type = F0R_PARAM_BOOL; - break; - - case 5: - info->name = "Force H-sync"; - info->explanation = "Forces V-sync even when the signal is really noisy."; - info->type = F0R_PARAM_BOOL; - break; - - case 6: info->name = "Progressive Scan"; info->explanation = "Toggles progressive scan (Interlaced if off)."; info->type = F0R_PARAM_BOOL; break; - case 7: - info->name = "Blend"; - info->explanation = "Blends frames."; + case 2: + info->name = "Scanlines"; + info->explanation = "Draw borders between scanlines."; info->type = F0R_PARAM_BOOL; break; } } + f0r_instance_t f0r_construct(unsigned int width, unsigned int height) { ntsc_instance_t* inst = (ntsc_instance_t*)calloc(1, sizeof(*inst)); @@ -109,36 +81,28 @@ f0r_instance_t f0r_construct(unsigned int width, unsigned int height) inst->width = width; inst->height = height; - inst->vhs_speed = 0.0; - inst->field = 0; - inst->progressive = 1; - - crt_init(&(inst->crt), width, height, NULL); - - // init NTSC_SETTINGS - inst->ntsc_settings.data = NULL; - inst->ntsc_settings.w = width; - inst->ntsc_settings.h = height; - inst->ntsc_settings.raw = 0; - inst->ntsc_settings.as_color = 1; - inst->ntsc_settings.field = 0; - inst->ntsc_settings.frame = 0; - inst->ntsc_settings.hue = 0; - inst->ntsc_settings.xoffset = 0; - inst->ntsc_settings.yoffset = 0; - inst->ntsc_settings.do_aberration = 0; - - // these are changed by the vhs mode - inst->ntsc_settings.vhs_mode = 0; - inst->ntsc_settings.y_freq = 0; - inst->ntsc_settings.i_freq = 0; - inst->ntsc_settings.q_freq = 0; - - /* make sure your NTSC_SETTINGS struct is zeroed out before you do anything */ - inst->ntsc_settings.iirs_initialized = 0; - + inst->ntsc.format = CRT_PIX_FORMAT_RGBA; + inst->ntsc.w = width; + inst->ntsc.h = height; + inst->ntsc.raw = 0; + inst->ntsc.field = 0; + inst->ntsc.frame = 0; + inst->ntsc.as_color = 1; + inst->ntsc.hue = 0; + inst->ntsc.xoffset = 0; + inst->ntsc.yoffset = 0; + inst->ntsc.iirs_initialized = 0; + inst->noise = 0; + inst->progressive = 0; + inst->field = 0; + crt_init(&(inst->crt), width, height, CRT_PIX_FORMAT_RGBA, NULL); + inst->crt.blend = 0; + inst->crt.scanlines = 0; + + + return (f0r_instance_t)inst; } @@ -155,29 +119,13 @@ void f0r_set_param_value(f0r_instance_t instance, f0r_param_t param, int param_i switch(param_index) { case 0: - inst->noise = *((double*)param) * 100; + inst->noise = *((double*)param) * 200; break; case 1: - inst->ntsc_settings.vhs_mode = (int)CLAMP(*((double*)param) * 4, 0, 4); - inst->ntsc_settings.iirs_initialized = 0; - break; - case 2: - inst->crt.do_vhs_noise = (*((double*)param) >= 0.5); - break; - case 3: - inst->ntsc_settings.do_aberration = (*((double*)param) >= 0.5); - break; - case 4: - inst->crt.crt_do_vsync = !(*((double*)param) >= 0.5); - break; - case 5: - inst->crt.crt_do_hsync = !(*((double*)param) >= 0.5); - break; - case 6: inst->progressive = (*((double*)param) >= 0.5); break; - case 7: - inst->crt.blend = (*((double*)param) >= 0.5); + case 2: + inst->crt.scanlines = (*((double*)param) >= 0.5); break; } } @@ -188,48 +136,40 @@ void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int param_i switch(param_index) { case 0: - *((double*)param) = (inst->noise / 100); + *((double*)param) = (inst->noise / 200); break; case 1: - *((double*)param) = (inst->ntsc_settings.vhs_mode / 4); - break; - case 2: - *((double*)param) = (inst->crt.do_vhs_noise ? 1.0 : 0.0); - break; - case 3: - *((double*)param) = (inst->ntsc_settings.do_aberration ? 1.0 : 0.0); - break; - case 4: - *((double*)param) = !(inst->crt.crt_do_vsync ? 1.0 : 0.0); - break; - case 5: - *((double*)param) = !(inst->crt.crt_do_hsync ? 1.0 : 0.0); - break; - case 6: *((double*)param) = (inst->progressive ? 1.0 : 0.0); break; - case 7: - *((double*)param) = (inst->crt.blend ? 1.0 : 0.0); + case 2: + *((double*)param) = (inst->crt.scanlines ? 1.0 : 0.0); break; } } void f0r_update(f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe) -{ +{ ntsc_instance_t* inst = (ntsc_instance_t*)instance; - + + // clear the output frame + memset(outframe, 0, inst->width * inst->height * sizeof(uint32_t)); + + // set everything up for the simulation + inst->ntsc.data = (const char*)inframe; inst->crt.out = (char*)outframe; - inst->ntsc_settings.data = (const char*)inframe; - inst->ntsc_settings.field = inst->field & 1; - if (inst->ntsc_settings.field == 0) { + inst->ntsc.field = inst->field & 1; + + if (inst->ntsc.field == 0) { /* a frame is two fields */ - inst->ntsc_settings.frame ^= 1; + inst->ntsc.frame ^= 1; } - crt_modulate(&(inst->crt), &(inst->ntsc_settings)); + // encode and decode ntsc signal + crt_modulate(&(inst->crt), &(inst->ntsc)); crt_demodulate(&(inst->crt), inst->noise); + if(!inst->progressive) { inst->field ^= 1; From 69d23fc0a232aebcae3a989d4708a1a6f637296f Mon Sep 17 00:00:00 2001 From: esmane <48299327+esmane@users.noreply.github.com> Date: Sat, 6 Sep 2025 09:41:56 -0400 Subject: [PATCH 4/4] Improve progressive scan mode --- src/filter/ntsc/ntsc-effect.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/filter/ntsc/ntsc-effect.c b/src/filter/ntsc/ntsc-effect.c index 3f2290a..d0df0c1 100644 --- a/src/filter/ntsc/ntsc-effect.c +++ b/src/filter/ntsc/ntsc-effect.c @@ -154,11 +154,13 @@ void f0r_update(f0r_instance_t instance, double time, const uint32_t* inframe, u // clear the output frame memset(outframe, 0, inst->width * inst->height * sizeof(uint32_t)); + inst->crt.blend = 0; // set everything up for the simulation - inst->ntsc.data = (const char*)inframe; inst->crt.out = (char*)outframe; + inst->ntsc.data = (const char*)inframe; +_render_field: inst->ntsc.field = inst->field & 1; if (inst->ntsc.field == 0) { @@ -170,8 +172,16 @@ void f0r_update(f0r_instance_t instance, double time, const uint32_t* inframe, u crt_modulate(&(inst->crt), &(inst->ntsc)); crt_demodulate(&(inst->crt), inst->noise); - if(!inst->progressive) + inst->field ^= 1; + // if we are in progressive mode, we render both fields onto the frame. + // in interlaced mode, we will hit the opposite field on the next frame. + if(inst->field && inst->progressive) { - inst->field ^= 1; + // if we are not leaving scanlines blank, we will want to blend the frames in progressive mode + if(!inst->crt.scanlines) + { + inst->crt.blend = 1; + } + goto _render_field; } }