1 /* analogtv, Copyright (c) 2003-2018 Trevor Blackwell <tlb@tlb.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or
9  * implied warranty.
10  */
11 
12 /*
13 
14   This is the code for implementing something that looks like a conventional
15   analog TV set. It simulates the following characteristics of standard
16   televisions:
17 
18   - Realistic rendering of a composite video signal
19   - Compression & brightening on the right, as the scan gets truncated
20     because of saturation in the flyback transformer
21   - Blooming of the picture dependent on brightness
22   - Overscan, cutting off a few pixels on the left side.
23   - Colored text in mixed graphics/text modes
24 
25   It's amazing how much it makes your high-end monitor look like at large
26   late-70s TV. All you need is to put a big "Solid State" logo in curly script
27   on it and you'd be set.
28 
29   In DirectColor or TrueColor modes, it generates pixel values
30   directly from RGB values it calculates across each scan line. In
31   PseudoColor mode, it consider each possible pattern of 5 preceding
32   bit values in each possible position modulo 4 and allocates a color
33   for each. A few things, like the brightening on the right side as
34   the horizontal trace slows down, aren't done in PseudoColor.
35 
36   I originally wrote it for the Apple ][ emulator, and generalized it
37   here for use with a rewrite of xteevee and possibly others.
38 
39   A maxim of technology is that failures reveal underlying mechanism.
40   A good way to learn how something works is to push it to failure.
41   The way it fails will usually tell you a lot about how it works. The
42   corollary for this piece of software is that in order to emulate
43   realistic failures of a TV set, it has to work just like a TV set.
44   So there is lots of DSP-style emulation of analog circuitry for
45   things like color decoding, H and V sync following, and more. In
46   2003, computers are just fast enough to do this at television signal
47   rates. We use a 14 MHz sample rate here, so we can do on the order
48   of a couple hundred instructions per sample and keep a good frame
49   rate.
50 
51   Trevor Blackwell <tlb@tlb.org>
52 */
53 
54 /*
55   2014-04-20, Dave Odell <dmo2118@gmail.com>:
56   API change: Folded analogtv_init_signal and *_add_signal into *_draw().
57   Added SMP support.
58   Replaced doubles with floats, including constants and transcendental functions.
59   Fixed a bug or two.
60 */
61 
62 /* 2015-02-27, Tomasz Sulej <tomeksul@gmail.com>:
63    - tint_control variable is used now
64    - removed unusable hashnoise code
65  */
66 
67 /*
68   2016-10-09, Dave Odell <dmo2118@gmail.com>:
69   Updated for new xshm.c.
70 */
71 
72 #ifdef HAVE_JWXYZ
73 # include "jwxyz.h"
74 #else /* !HAVE_JWXYZ */
75 # include <X11/Xlib.h>
76 # include <X11/Xutil.h>
77 #endif
78 #include <limits.h>
79 
80 #include <assert.h>
81 #include <errno.h>
82 #include "utils.h"
83 #include "resources.h"
84 #include "analogtv.h"
85 #include "yarandom.h"
86 #include "grabscreen.h"
87 #include "visual.h"
88 #include "font-retry.h"
89 #include "ximage-loader.h"
90 
91 /* #define DEBUG 1 */
92 
93 #if defined(DEBUG) && (defined(__linux) || defined(__FreeBSD__))
94 /* only works on linux + freebsd */
95 #include <machine/cpufunc.h>
96 
97 #define DTIME_DECL u_int64_t dtimes[100]; int n_dtimes
98 #define DTIME_START do {n_dtimes=0; dtimes[n_dtimes++]=rdtsc(); } while (0)
99 #define DTIME dtimes[n_dtimes++]=rdtsc()
100 #define DTIME_SHOW(DIV) \
101 do { \
102   double _dtime_div=(DIV); \
103   printf("time/%.1f: ",_dtime_div); \
104   for (i=1; i<n_dtimes; i++) \
105     printf(" %0.9f",(dtimes[i]-dtimes[i-1])* 1e-9 / _dtime_div); \
106   printf("\n"); \
107 } while (0)
108 
109 #else
110 
111 #define DTIME_DECL
112 #define DTIME_START  do { } while (0)
113 #define DTIME  do { } while (0)
114 #define DTIME_SHOW(DIV)  do { } while (0)
115 
116 #endif
117 
118 
119 #define FASTRND_A 1103515245
120 #define FASTRND_C 12345
121 #define FASTRND (fastrnd = fastrnd*FASTRND_A+FASTRND_C)
122 
123 static void analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
124                                  int start, int end, struct analogtv_yiq_s *it_yiq);
125 
puramp(const analogtv * it,float tc,float start,float over)126 static float puramp(const analogtv *it, float tc, float start, float over)
127 {
128   float pt=it->powerup-start;
129   float ret;
130   if (pt<0.0f) return 0.0f;
131   if (pt>900.0f || pt/tc>8.0f) return 1.0f;
132 
133   ret=(1.0f-expf(-pt/tc))*over;
134   if (ret>1.0f) return 1.0f;
135   return ret*ret;
136 }
137 
138 /*
139   There are actual standards for TV signals: NTSC and RS-170A describe the
140   system used in the US and Japan. Europe has slightly different systems, but
141   not different enough to make substantially different screensaver displays.
142   Sadly, the standards bodies don't do anything so useful as publish the spec on
143   the web. Best bets are:
144 
145     http://www.ee.washington.edu/conselec/CE/kuhn/ntsc/95x4.htm
146     http://www.ntsc-tv.com/ntsc-index-02.htm
147 
148   In DirectColor or TrueColor modes, it generates pixel values directly from RGB
149   values it calculates across each scan line. In PseudoColor mode, it consider
150   each possible pattern of 5 preceding bit values in each possible position
151   modulo 4 and allocates a color for each. A few things, like the brightening on
152   the right side as the horizontal trace slows down, aren't done in PseudoColor.
153 
154   I'd like to add a bit of visible retrace, but it conflicts with being able to
155   bitcopy the image when fast scrolling. After another couple of CPU
156   generations, we could probably regenerate the whole image from scratch every
157   time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling
158   looks too slow.
159 */
160 
161 /* localbyteorder is MSBFirst or LSBFirst */
162 static int localbyteorder;
163 static const double float_low8_ofs=8388608.0;
164 static int float_extraction_works;
165 
166 typedef union {
167   float f;
168   int i;
169 } float_extract_t;
170 
171 static void
analogtv_init(void)172 analogtv_init(void)
173 {
174   int i;
175   {
176     unsigned int localbyteorder_loc = (MSBFirst<<24) | (LSBFirst<<0);
177     localbyteorder=*(char *)&localbyteorder_loc;
178   }
179 
180   if (1) {
181     float_extract_t fe;
182     int ans;
183 
184     float_extraction_works=1;
185     for (i=0; i<256*4; i++) {
186       fe.f=float_low8_ofs+(double)i;
187       ans=fe.i&0x3ff;
188       if (ans != i) {
189 #ifdef DEBUG
190         printf("Float extraction failed for %d => %d\n",i,ans);
191 #endif
192         float_extraction_works=0;
193         break;
194       }
195     }
196   }
197 
198 }
199 
200 void
analogtv_set_defaults(analogtv * it,char * prefix)201 analogtv_set_defaults(analogtv *it, char *prefix)
202 {
203   char buf[256];
204 
205   sprintf(buf,"%sTVTint",prefix);
206   it->tint_control = get_float_resource(it->dpy, buf,"TVTint");
207   sprintf(buf,"%sTVColor",prefix);
208   it->color_control = get_float_resource(it->dpy, buf,"TVColor")/100.0;
209   sprintf(buf,"%sTVBrightness",prefix);
210   it->brightness_control = get_float_resource(it->dpy, buf,"TVBrightness") / 100.0;
211   sprintf(buf,"%sTVContrast",prefix);
212   it->contrast_control = get_float_resource(it->dpy, buf,"TVContrast") / 100.0;
213   it->height_control = 1.0;
214   it->width_control = 1.0;
215   it->squish_control = 0.0;
216   it->powerup=1000.0;
217 
218   it->hashnoise_rpm=0;
219   it->hashnoise_on=0;
220   it->hashnoise_enable=1;
221 
222   it->horiz_desync=frand(10.0)-5.0;
223   it->squeezebottom=frand(5.0)-1.0;
224 
225 #ifdef DEBUG
226   printf("analogtv: prefix=%s\n",prefix);
227   printf("  use: cmap=%d color=%d\n",
228          it->use_cmap,it->use_color);
229   printf("  controls: tint=%g color=%g brightness=%g contrast=%g\n",
230          it->tint_control, it->color_control, it->brightness_control,
231          it->contrast_control);
232 /*  printf("  freq_error %g: %g %d\n",
233          it->freq_error, it->freq_error_inc, it->flutter_tint); */
234   printf("  desync: %g %d\n",
235          it->horiz_desync, it->flutter_horiz_desync);
236   printf("  hashnoise rpm: %g\n",
237          it->hashnoise_rpm);
238   printf("  vis: %d %d\n",
239          it->visclass, it->visdepth);
240   printf("  shift: %d-%d %d-%d %d-%d\n",
241          it->red_invprec,it->red_shift,
242          it->green_invprec,it->green_shift,
243          it->blue_invprec,it->blue_shift);
244   printf("  size: %d %d  %d %d  xrepl=%d\n",
245          it->usewidth, it->useheight,
246          it->screen_xo, it->screen_yo, it->xrepl);
247 
248   printf("    ANALOGTV_V=%d\n",ANALOGTV_V);
249   printf("    ANALOGTV_TOP=%d\n",ANALOGTV_TOP);
250   printf("    ANALOGTV_VISLINES=%d\n",ANALOGTV_VISLINES);
251   printf("    ANALOGTV_BOT=%d\n",ANALOGTV_BOT);
252   printf("    ANALOGTV_H=%d\n",ANALOGTV_H);
253   printf("    ANALOGTV_SYNC_START=%d\n",ANALOGTV_SYNC_START);
254   printf("    ANALOGTV_BP_START=%d\n",ANALOGTV_BP_START);
255   printf("    ANALOGTV_CB_START=%d\n",ANALOGTV_CB_START);
256   printf("    ANALOGTV_PIC_START=%d\n",ANALOGTV_PIC_START);
257   printf("    ANALOGTV_PIC_LEN=%d\n",ANALOGTV_PIC_LEN);
258   printf("    ANALOGTV_FP_START=%d\n",ANALOGTV_FP_START);
259   printf("    ANALOGTV_PIC_END=%d\n",ANALOGTV_PIC_END);
260   printf("    ANALOGTV_HASHNOISE_LEN=%d\n",ANALOGTV_HASHNOISE_LEN);
261 
262 #endif
263 
264 }
265 
266 extern Bool mono_p; /* shoot me */
267 
268 static void
analogtv_free_image(analogtv * it)269 analogtv_free_image(analogtv *it)
270 {
271   if (it->image) {
272     destroy_xshm_image(it->dpy, it->image, &it->shm_info);
273     it->image=NULL;
274   }
275 }
276 
277 static void
analogtv_alloc_image(analogtv * it)278 analogtv_alloc_image(analogtv *it)
279 {
280   /* On failure, it->image is NULL. */
281 
282   unsigned bits_per_pixel = visual_pixmap_depth(it->screen, it->xgwa.visual);
283   unsigned align = thread_memory_alignment(it->dpy) * 8 - 1;
284   /* Width is in bits. */
285   unsigned width = (it->usewidth * bits_per_pixel + align) & ~align;
286 
287   it->image=create_xshm_image(it->dpy, it->xgwa.visual, it->xgwa.depth,
288                               ZPixmap, &it->shm_info,
289                               width / bits_per_pixel, it->useheight);
290 
291   if (it->image) {
292     memset (it->image->data, 0, it->image->height * it->image->bytes_per_line);
293   } else {
294     /* Not enough memory. Maybe try a smaller window. */
295     fprintf(stderr, "analogtv: %s\n", strerror(ENOMEM));
296   }
297 }
298 
299 
300 static void
analogtv_configure(analogtv * it)301 analogtv_configure(analogtv *it)
302 {
303   int oldwidth=it->usewidth;
304   int oldheight=it->useheight;
305   int wlim,hlim,height_diff;
306 
307   /* If the window is very small, don't let the image we draw get lower
308      than the actual TV resolution (266x200.)
309 
310      If the aspect ratio of the window is close to a 4:3 or 16:9 ratio --
311      or if it is a completely weird aspect ratio --
312      then scale the image to exactly fill the window.
313 
314      Otherwise, center the image either horizontally or vertically,
315      letterboxing or pillarboxing (but not both).
316 
317      If it's very close (2.5%) to a multiple of VISLINES, make it exact
318      For example, it maps 1024 => 1000.
319    */
320   float percent = 0.15;
321   float min_ratio =  4.0 / 3.0 * (1 - percent);
322   float max_ratio = 16.0 / 9.0 * (1 + percent);
323   float crazy_min_ratio = 10;
324   float crazy_max_ratio = 1/crazy_min_ratio;
325   float ratio;
326   float height_snap=0.025;
327 
328   hlim = it->xgwa.height;
329   wlim = it->xgwa.width;
330   ratio = wlim / (float) hlim;
331 
332 #if defined(HAVE_MOBILE) || defined(NO_CONSTRAIN_RATIO)
333   /* Fill the whole iPhone screen, even though that distorts the image. */
334   min_ratio = 0;
335   max_ratio = 10;
336 #endif
337 
338   if (wlim < 266 || hlim < 200)
339     {
340       wlim = 266;
341       hlim = 200;
342 # ifdef DEBUG
343       fprintf (stderr,
344                "size: minimal: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
345                wlim, hlim, it->xgwa.width, it->xgwa.height,
346                min_ratio, ratio, max_ratio);
347 # endif
348     }
349   else if (ratio > min_ratio && ratio < max_ratio)
350     {
351 # ifdef DEBUG
352       fprintf (stderr,
353                "size: close enough: %dx%d (%.3f < %.3f < %.3f)\n",
354                wlim, hlim, min_ratio, ratio, max_ratio);
355 # endif
356     }
357   else if (ratio >= max_ratio)
358     {
359       wlim = hlim*max_ratio;
360 # ifdef DEBUG
361       fprintf (stderr,
362                "size: center H: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
363                wlim, hlim, it->xgwa.width, it->xgwa.height,
364                min_ratio, ratio, max_ratio);
365 # endif
366     }
367   else /* ratio <= min_ratio */
368     {
369       hlim = wlim/min_ratio;
370 # ifdef DEBUG
371       fprintf (stderr,
372                "size: center V: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
373                wlim, hlim, it->xgwa.width, it->xgwa.height,
374                min_ratio, ratio, max_ratio);
375 # endif
376     }
377 
378   if (ratio < crazy_min_ratio || ratio > crazy_max_ratio)
379     {
380       if (ratio < crazy_min_ratio)
381         hlim = it->xgwa.height;
382       else
383         wlim = it->xgwa.width;
384 # ifdef DEBUG
385       fprintf (stderr,
386                "size: aspect: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
387                wlim, hlim, it->xgwa.width, it->xgwa.height,
388                min_ratio, ratio, max_ratio);
389 # endif
390     }
391 
392 
393   height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
394   if (height_diff != 0 && abs(height_diff) < hlim * height_snap)
395     {
396       hlim -= height_diff;
397     }
398 
399 
400   /* Most times this doesn't change */
401   if (wlim != oldwidth || hlim != oldheight) {
402 
403     it->usewidth=wlim;
404     it->useheight=hlim;
405 
406     it->xrepl=1+it->usewidth/640;
407     if (it->xrepl>2) it->xrepl=2;
408     it->subwidth=it->usewidth/it->xrepl;
409 
410     analogtv_free_image(it);
411     analogtv_alloc_image(it);
412   }
413 
414   it->screen_xo = (it->xgwa.width-it->usewidth)/2;
415   it->screen_yo = (it->xgwa.height-it->useheight)/2;
416   it->need_clear=1;
417 }
418 
419 void
analogtv_reconfigure(analogtv * it)420 analogtv_reconfigure(analogtv *it)
421 {
422   XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
423   analogtv_configure(it);
424 }
425 
426 /* Can be any power-of-two <= 32. 16 a slightly better choice for 2-3 threads. */
427 #define ANALOGTV_SUBTOTAL_LEN 32
428 
429 typedef struct analogtv_thread_s
430 {
431   analogtv *it;
432   unsigned thread_id;
433   size_t signal_start, signal_end;
434 } analogtv_thread;
435 
436 #define SIGNAL_OFFSET(thread_id) \
437   ((ANALOGTV_SIGNAL_LEN * (thread_id) / threads->count) & align)
438 
analogtv_thread_create(void * thread_raw,struct threadpool * threads,unsigned thread_id)439 static int analogtv_thread_create(void *thread_raw, struct threadpool *threads,
440                                   unsigned thread_id)
441 {
442   analogtv_thread *thread = (analogtv_thread *)thread_raw;
443   unsigned align;
444 
445   thread->it = GET_PARENT_OBJ(analogtv, threads, threads);
446   thread->thread_id = thread_id;
447 
448   align = thread_memory_alignment(thread->it->dpy) /
449             sizeof(thread->it->signal_subtotals[0]);
450   if (!align)
451     align = 1;
452   align = ~(align * ANALOGTV_SUBTOTAL_LEN - 1);
453 
454   thread->signal_start = SIGNAL_OFFSET(thread_id);
455   thread->signal_end = thread_id + 1 == threads->count ?
456                        ANALOGTV_SIGNAL_LEN :
457                        SIGNAL_OFFSET(thread_id + 1);
458 
459   return 0;
460 }
461 
analogtv_thread_destroy(void * thread_raw)462 static void analogtv_thread_destroy(void *thread_raw)
463 {
464 }
465 
466 analogtv *
analogtv_allocate(Display * dpy,Window window)467 analogtv_allocate(Display *dpy, Window window)
468 {
469   static const struct threadpool_class cls = {
470     sizeof(analogtv_thread),
471     analogtv_thread_create,
472     analogtv_thread_destroy
473   };
474 
475   XGCValues gcv;
476   analogtv *it=NULL;
477   int i;
478   const size_t rx_signal_len = ANALOGTV_SIGNAL_LEN + 2*ANALOGTV_H;
479 
480   analogtv_init();
481 
482   it=(analogtv *)calloc(1,sizeof(analogtv));
483   if (!it) return 0;
484   it->threads.count=0;
485   it->rx_signal=NULL;
486   it->signal_subtotals=NULL;
487 
488   it->dpy=dpy;
489   it->window=window;
490 
491   if (thread_malloc((void **)&it->rx_signal, dpy,
492                     sizeof(it->rx_signal[0]) * rx_signal_len))
493     goto fail;
494 
495   assert(!(ANALOGTV_SIGNAL_LEN % ANALOGTV_SUBTOTAL_LEN));
496   if (thread_malloc((void **)&it->signal_subtotals, dpy,
497                     sizeof(it->signal_subtotals[0]) *
498                      (rx_signal_len / ANALOGTV_SUBTOTAL_LEN)))
499     goto fail;
500 
501   if (threadpool_create(&it->threads, &cls, dpy, hardware_concurrency(dpy)))
502     goto fail;
503 
504   assert(it->threads.count);
505 
506   it->shrinkpulse=-1;
507 
508   it->n_colors=0;
509 
510   XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
511 
512   it->screen=it->xgwa.screen;
513   it->colormap=it->xgwa.colormap;
514   it->visclass=visual_class(it->xgwa.screen, it->xgwa.visual);
515   it->visdepth=it->xgwa.depth;
516   if (it->visclass == TrueColor || it->visclass == DirectColor) {
517     if (get_integer_resource (it->dpy, "use_cmap", "Integer")) {
518       it->use_cmap=1;
519     } else {
520       it->use_cmap=0;
521     }
522     it->use_color=!mono_p;
523   }
524   else if (it->visclass == PseudoColor || it->visclass == StaticColor) {
525     it->use_cmap=1;
526     it->use_color=!mono_p;
527   }
528   else {
529     it->use_cmap=1;
530     it->use_color=0;
531   }
532 
533   visual_rgb_masks (it->xgwa.screen, it->xgwa.visual,
534                     &it->red_mask, &it->green_mask, &it->blue_mask);
535   it->red_shift=it->red_invprec=-1;
536   it->green_shift=it->green_invprec=-1;
537   it->blue_shift=it->blue_invprec=-1;
538   if (!it->use_cmap) {
539     /* Is there a standard way to do this? Does this handle all cases? */
540     int shift, prec;
541     for (shift=0; shift<32; shift++) {
542       for (prec=1; prec<16 && prec<40-shift; prec++) {
543         unsigned long mask=(0xffffUL>>(16-prec)) << shift;
544         if (it->red_shift<0 && mask==it->red_mask)
545           it->red_shift=shift, it->red_invprec=16-prec;
546         if (it->green_shift<0 && mask==it->green_mask)
547           it->green_shift=shift, it->green_invprec=16-prec;
548         if (it->blue_shift<0 && mask==it->blue_mask)
549           it->blue_shift=shift, it->blue_invprec=16-prec;
550       }
551     }
552     if (it->red_shift<0 || it->green_shift<0 || it->blue_shift<0) {
553       if (0) fprintf(stderr,"Can't figure out color space\n");
554       goto fail;
555     }
556 
557     for (i=0; i<ANALOGTV_CV_MAX; i++) {
558       int intensity=pow(i/256.0, 0.8)*65535.0; /* gamma correction */
559       if (intensity>65535) intensity=65535;
560       it->red_values[i]=((intensity>>it->red_invprec)<<it->red_shift);
561       it->green_values[i]=((intensity>>it->green_invprec)<<it->green_shift);
562       it->blue_values[i]=((intensity>>it->blue_invprec)<<it->blue_shift);
563     }
564 
565   }
566 
567   gcv.background=get_pixel_resource(it->dpy, it->colormap,
568                                     "background", "Background");
569 
570   it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
571 # ifdef HAVE_JWXYZ
572   jwxyz_XSetAntiAliasing (it->dpy, it->gc, False);
573 # endif
574   XSetWindowBackground(it->dpy, it->window, gcv.background);
575   XClearWindow(dpy,window);
576 
577   analogtv_configure(it);
578 
579   return it;
580 
581  fail:
582   if (it) {
583     if(it->threads.count)
584       threadpool_destroy(&it->threads);
585     thread_free(it->signal_subtotals);
586     thread_free(it->rx_signal);
587     free(it);
588   }
589   return NULL;
590 }
591 
592 void
analogtv_release(analogtv * it)593 analogtv_release(analogtv *it)
594 {
595   if (it->image) {
596     destroy_xshm_image(it->dpy, it->image, &it->shm_info);
597     it->image=NULL;
598   }
599   if (it->gc) XFreeGC(it->dpy, it->gc);
600   it->gc=NULL;
601   if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
602   it->n_colors=0;
603   threadpool_destroy(&it->threads);
604   thread_free(it->rx_signal);
605   thread_free(it->signal_subtotals);
606   free(it);
607 }
608 
609 
610 /*
611   First generate the I and Q reference signals, which we'll multiply
612   the input signal by to accomplish the demodulation. Normally they
613   are shifted 33 degrees from the colorburst. I think this was convenient
614   for inductor-capacitor-vacuum tube implementation.
615 
616   The tint control, FWIW, just adds a phase shift to the chroma signal,
617   and the color control controls the amplitude.
618 
619   In text modes (colormode==0) the system disabled the color burst, and no
620   color was detected by the monitor.
621 
622   freq_error gives a mismatch between the built-in oscillator and the
623   TV's colorbust. Some II Plus machines seemed to occasionally get
624   instability problems -- the crystal oscillator was a single
625   transistor if I remember correctly -- and the frequency would vary
626   enough that the tint would change across the width of the screen.
627   The left side would be in correct tint because it had just gotten
628   resynchronized with the color burst.
629 
630   If we're using a colormap, set it up.
631 */
632 int
analogtv_set_demod(analogtv * it)633 analogtv_set_demod(analogtv *it)
634 {
635   int y_levels=10,i_levels=5,q_levels=5;
636 
637   /*
638     In principle, we might be able to figure out how to adjust the
639     color map frame-by-frame to get some nice color bummage. But I'm
640     terrified of changing the color map because we'll get flashing.
641 
642     I can hardly believe we still have to deal with colormaps. They're
643     like having NEAR PTRs: an enormous hassle for the programmer just
644     to save on memory. They should have been deprecated by 1995 or
645     so. */
646 
647  cmap_again:
648   if (it->use_cmap && !it->n_colors) {
649 
650     if (it->n_colors) {
651       XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
652       it->n_colors=0;
653     }
654 
655     {
656       int yli,qli,ili;
657       for (yli=0; yli<y_levels; yli++) {
658         for (ili=0; ili<i_levels; ili++) {
659           for (qli=0; qli<q_levels; qli++) {
660             double interpy,interpi,interpq;
661             double levelmult=700.0;
662             int r,g,b;
663             XColor col;
664 
665             interpy=100.0 * ((double)yli/y_levels);
666             interpi=50.0 * (((double)ili-(0.5*i_levels))/(double)i_levels);
667             interpq=50.0 * (((double)qli-(0.5*q_levels))/(double)q_levels);
668 
669             r=(int)((interpy + 1.04*interpi + 0.624*interpq)*levelmult);
670             g=(int)((interpy - 0.276*interpi - 0.639*interpq)*levelmult);
671             b=(int)((interpy - 1.105*interpi + 1.729*interpq)*levelmult);
672             if (r<0) r=0;
673             if (r>65535) r=65535;
674             if (g<0) g=0;
675             if (g>65535) g=65535;
676             if (b<0) b=0;
677             if (b>65535) b=65535;
678 
679 #ifdef DEBUG
680             printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n",
681                    interpy, interpi, interpq,
682                    r/256,g/256,b/256);
683 #endif
684 
685             col.red=r;
686             col.green=g;
687             col.blue=b;
688             col.pixel=0;
689             if (!XAllocColor(it->dpy, it->colormap, &col)) {
690               if (q_levels > y_levels*4/12)
691                 q_levels--;
692               else if (i_levels > y_levels*5/12)
693                 i_levels--;
694               else
695                 y_levels--;
696 
697               if (y_levels<2)
698                 return -1;
699               goto cmap_again;
700             }
701             it->colors[it->n_colors++]=col.pixel;
702           }
703         }
704       }
705 
706       it->cmap_y_levels=y_levels;
707       it->cmap_i_levels=i_levels;
708       it->cmap_q_levels=q_levels;
709     }
710   }
711 
712   return 0;
713 }
714 
715 #if 0
716 unsigned int
717 analogtv_line_signature(analogtv_input *input, int lineno)
718 {
719   int i;
720   char *origsignal=&input->signal[(lineno+input->vsync)
721                                   %ANALOGTV_V][input->line_hsync[lineno]];
722   unsigned int hash=0;
723 
724   /* probably lame */
725   for (i=0; i<ANALOGTV_PIC_LEN; i++) {
726     int c=origsignal[i];
727     hash = hash + (hash<<17) + c;
728   }
729 
730   hash += input->line_hsync[lineno];
731   hash ^= hash >> 2;
732   /*
733   hash += input->hashnoise_times[lineno];
734   hash ^= hash >> 2;
735   */
736 
737   return hash;
738 }
739 #endif
740 
741 
742 /* Here we model the analog circuitry of an NTSC television.
743    Basically, it splits the signal into 3 signals: Y, I and Q. Y
744    corresponds to luminance, and you get it by low-pass filtering the
745    input signal to below 3.57 MHz.
746 
747    I and Q are the in-phase and quadrature components of the 3.57 MHz
748    subcarrier. We get them by multiplying by cos(3.57 MHz*t) and
749    sin(3.57 MHz*t), and low-pass filtering. Because the eye has less
750    resolution in some colors than others, the I component gets
751    low-pass filtered at 1.5 MHz and the Q at 0.5 MHz. The I component
752    is approximately orange-blue, and Q is roughly purple-green. See
753    http://www.ntsc-tv.com for details.
754 
755    We actually do an awful lot to the signal here. I suspect it would
756    make sense to wrap them all up together by calculating impulse
757    response and doing FFT convolutions.
758 
759 */
760 
761 static void
analogtv_ntsc_to_yiq(const analogtv * it,int lineno,const float * signal,int start,int end,struct analogtv_yiq_s * it_yiq)762 analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
763                      int start, int end, struct analogtv_yiq_s *it_yiq)
764 {
765   enum {MAXDELAY=32};
766   int i;
767   const float *sp;
768   int phasecorr=(signal-it->rx_signal)&3;
769   struct analogtv_yiq_s *yiq;
770   int colormode;
771   float agclevel=it->agclevel;
772   float brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
773   float delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
774   float multiq2[4];
775 
776   {
777 
778     double cb_i=(it->line_cb_phase[lineno][(2+phasecorr)&3]-
779                  it->line_cb_phase[lineno][(0+phasecorr)&3])/16.0;
780     double cb_q=(it->line_cb_phase[lineno][(3+phasecorr)&3]-
781                  it->line_cb_phase[lineno][(1+phasecorr)&3])/16.0;
782 
783     colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
784 
785     if (colormode) {
786       multiq2[0] = (cb_i*it->tint_i - cb_q*it->tint_q) * it->color_control;
787       multiq2[1] = (cb_q*it->tint_i + cb_i*it->tint_q) * it->color_control;
788       multiq2[2]=-multiq2[0];
789       multiq2[3]=-multiq2[1];
790     }
791   }
792 
793 #if 0
794   if (lineno==100) {
795     printf("multiq = [%0.3f %0.3f %0.3f %0.3f] ",
796            it->multiq[60],it->multiq[61],it->multiq[62],it->multiq[63]);
797     printf("it->line_cb_phase = [%0.3f %0.3f %0.3f %0.3f]\n",
798            it->line_cb_phase[lineno][0],it->line_cb_phase[lineno][1],
799            it->line_cb_phase[lineno][2],it->line_cb_phase[lineno][3]);
800     printf("multiq2 = [%0.3f %0.3f %0.3f %0.3f]\n",
801            multiq2[0],multiq2[1],multiq2[2],multiq2[3]);
802   }
803 #endif
804 
805   dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
806   for (i=0; i<5; i++) dp[i]=0.0f;
807 
808   assert(start>=0);
809   assert(end < ANALOGTV_PIC_LEN+10);
810 
811   dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
812   for (i=0; i<24; i++) dp[i]=0.0;
813   for (i=start, yiq=it_yiq+start, sp=signal+start;
814        i<end;
815        i++, dp--, yiq++, sp++) {
816 
817     /* Now filter them. These are infinite impulse response filters
818        calculated by the script at
819        http://www-users.cs.york.ac.uk/~fisher/mkfilter. This is
820        fixed-point integer DSP, son. No place for wimps. We do it in
821        integer because you can count on integer being faster on most
822        CPUs. We care about speed because we need to recalculate every
823        time we blink text, and when we spew random bytes into screen
824        memory. This is roughly 16.16 fixed point arithmetic, but we
825        scale some filter values up by a few bits to avoid some nasty
826        precision errors. */
827 
828     /* Filter Y with a 4-pole low-pass Butterworth filter at 3.5 MHz
829        with an extra zero at 3.5 MHz, from
830        mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
831        Delay about 2 */
832 
833     dp[0] = sp[0] * 0.0469904257251935f * agclevel;
834     dp[8] = (+1.0f*(dp[6]+dp[0])
835              +4.0f*(dp[5]+dp[1])
836              +7.0f*(dp[4]+dp[2])
837              +8.0f*(dp[3])
838              -0.0176648f*dp[12]
839              -0.4860288f*dp[10]);
840     yiq->y = dp[8] + brightadd;
841   }
842 
843   if (colormode) {
844     dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
845     for (i=0; i<27; i++) dp[i]=0.0;
846 
847     for (i=start, yiq=it_yiq+start, sp=signal+start;
848          i<end;
849          i++, dp--, yiq++, sp++) {
850       float sig=*sp;
851 
852       /* Filter I and Q with a 3-pole low-pass Butterworth filter at
853          1.5 MHz with an extra zero at 3.5 MHz, from
854          mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 -Z 2.5000000000e-01 -l
855          Delay about 3.
856       */
857 
858       dp[0] = sig*multiq2[i&3] * 0.0833333333333f;
859       yiq->i=dp[8] = (dp[5] + dp[0]
860                       +3.0f*(dp[4] + dp[1])
861                       +4.0f*(dp[3] + dp[2])
862                       -0.3333333333f * dp[10]);
863 
864       dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333f;
865       yiq->q=dp[24] = (dp[16+5] + dp[16+0]
866                        +3.0f*(dp[16+4] + dp[16+1])
867                        +4.0f*(dp[16+3] + dp[16+2])
868                        -0.3333333333f * dp[24+2]);
869     }
870   } else {
871     for (i=start, yiq=it_yiq+start; i<end; i++, yiq++) {
872       yiq->i = yiq->q = 0.0f;
873     }
874   }
875 }
876 
877 void
analogtv_setup_teletext(analogtv_input * input)878 analogtv_setup_teletext(analogtv_input *input)
879 {
880   int x,y;
881   int teletext=ANALOGTV_BLACK_LEVEL;
882 
883   /* Teletext goes in line 21. But I suspect there are other things
884      in the vertical retrace interval */
885 
886   for (y=19; y<22; y++) {
887     for (x=ANALOGTV_PIC_START; x<ANALOGTV_PIC_END; x++) {
888       if ((x&7)==0) {
889         teletext=(random()&1) ? ANALOGTV_WHITE_LEVEL : ANALOGTV_BLACK_LEVEL;
890       }
891       input->signal[y][x]=teletext;
892     }
893   }
894 }
895 
896 void
analogtv_setup_frame(analogtv * it)897 analogtv_setup_frame(analogtv *it)
898 {
899   /*  int i,x,y;*/
900 
901   it->redraw_all=0;
902 
903   if (it->flutter_horiz_desync) {
904     /* Horizontal sync during vertical sync instability. */
905     it->horiz_desync += -0.10*(it->horiz_desync-3.0) +
906       ((int)(random()&0xff)-0x80) *
907       ((int)(random()&0xff)-0x80) *
908       ((int)(random()&0xff)-0x80) * 0.000001;
909   }
910 
911   /* it wasn't used
912   for (i=0; i<ANALOGTV_V; i++) {
913     it->hashnoise_times[i]=0;
914   }
915   */
916 
917   /* let's leave it to process shrinkpulse */
918   if (it->hashnoise_enable && !it->hashnoise_on) {
919     if (random()%10000==0) {
920       it->hashnoise_on=1;
921       it->shrinkpulse=random()%ANALOGTV_V;
922     }
923   }
924   if (random()%1000==0) {
925     it->hashnoise_on=0;
926   }
927 
928 #if 0  /* never used */
929   if (it->hashnoise_on) {
930     it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
931       ((int)(random()%2000)-1000)*0.1;
932   } else {
933     it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
934     if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
935   }
936   if (it->hashnoise_rpm > 0.0) {
937     int hni;
938     double hni_double;
939     int hnc=it->hashnoise_counter; /* in 24.8 format */
940 
941     /* Convert rpm of a 16-pole motor into dots in 24.8 format */
942     hni_double = ANALOGTV_V * ANALOGTV_H * 256.0 /
943                 (it->hashnoise_rpm * 16.0 / 60.0 / 60.0);
944     hni = (hni_double <= INT_MAX) ? (int)hni_double : INT_MAX;
945 
946     while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
947       y=(hnc>>8)/ANALOGTV_H;
948       x=(hnc>>8)%ANALOGTV_H;
949 
950       if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
951         it->hashnoise_times[y]=x;
952       }
953       /* hnc += hni + (int)(random()%65536)-32768; */
954       {
955         hnc += (int)(random()%65536)-32768;
956         if ((hnc >= 0) && (INT_MAX - hnc < hni)) break;
957         hnc += hni;
958       }
959     }
960   }
961 #endif /* 0 */
962 
963 /*    hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;*/
964 
965 
966   if (it->rx_signal_level != 0.0)
967     it->agclevel = 1.0/it->rx_signal_level;
968 
969 
970 #ifdef DEBUG2
971   printf("filter: ");
972   for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
973     printf(" %0.3f",it->ghostfir[i]);
974   }
975   printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel);
976 #endif
977 }
978 
979 void
analogtv_setup_sync(analogtv_input * input,int do_cb,int do_ssavi)980 analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
981 {
982   int i,lineno,vsync;
983   signed char *sig;
984 
985   int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
986 
987   for (lineno=0; lineno<ANALOGTV_V; lineno++) {
988     vsync=lineno>=3 && lineno<7;
989 
990     sig=input->signal[lineno];
991 
992     i=ANALOGTV_SYNC_START;
993     if (vsync) {
994       while (i<ANALOGTV_BP_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
995       while (i<ANALOGTV_H) sig[i++]=synclevel;
996     } else {
997       while (i<ANALOGTV_BP_START) sig[i++]=synclevel;
998       while (i<ANALOGTV_PIC_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
999       while (i<ANALOGTV_FP_START) sig[i++]=ANALOGTV_BLACK_LEVEL;
1000     }
1001     while (i<ANALOGTV_H) sig[i++]=ANALOGTV_BLANK_LEVEL;
1002 
1003     if (do_cb) {
1004       /* 9 cycles of colorburst */
1005       for (i=ANALOGTV_CB_START; i<ANALOGTV_CB_START+36*ANALOGTV_SCALE; i+=4*ANALOGTV_SCALE) {
1006         sig[i+1] += ANALOGTV_CB_LEVEL;
1007         sig[i+3] -= ANALOGTV_CB_LEVEL;
1008       }
1009     }
1010   }
1011 }
1012 
1013 static void
analogtv_sync(analogtv * it)1014 analogtv_sync(analogtv *it)
1015 {
1016   int cur_hsync=it->cur_hsync;
1017   int cur_vsync=it->cur_vsync;
1018   int lineno = 0;
1019   int i,j;
1020   float osc,filt;
1021   float *sp;
1022   float cbfc=1.0f/128.0f;
1023 
1024 /*  sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;*/
1025   for (i=-32*ANALOGTV_SCALE; i<32*ANALOGTV_SCALE; i++) {
1026     lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1027     sp = it->rx_signal + lineno*ANALOGTV_H;
1028     filt=0.0f;
1029     for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/(16*ANALOGTV_SCALE)) {
1030       filt += sp[j];
1031     }
1032     filt *= it->agclevel;
1033 
1034     osc = (float)(ANALOGTV_V+i)/(float)ANALOGTV_V;
1035 
1036     if (osc >= 1.05f+0.0002f * filt) break;
1037   }
1038   cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1039 
1040   for (lineno=0; lineno<ANALOGTV_V; lineno++) {
1041 
1042     if (lineno>5*ANALOGTV_SCALE && lineno<ANALOGTV_V-3*ANALOGTV_SCALE) { /* ignore vsync interval */
1043       unsigned lineno2 = (lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V;
1044       if (!lineno2) lineno2 = ANALOGTV_V;
1045       sp = it->rx_signal + lineno2*ANALOGTV_H + cur_hsync;
1046       for (i=-8*ANALOGTV_SCALE; i<8*ANALOGTV_SCALE; i++) {
1047         osc = (float)(ANALOGTV_H+i)/(float)ANALOGTV_H;
1048         filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
1049 
1050         if (osc >= 1.005f + 0.0001f*filt) break;
1051       }
1052       cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
1053     }
1054 
1055     it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START +
1056                             ANALOGTV_H) % ANALOGTV_H;
1057 
1058     /* Now look for the colorburst, which is a few cycles after the H
1059        sync pulse, and store its phase.
1060        The colorburst is 9 cycles long, and we look at the middle 5
1061        cycles.
1062     */
1063 
1064     if (lineno>15*ANALOGTV_SCALE) {
1065       sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
1066       for (i=ANALOGTV_CB_START+8*ANALOGTV_SCALE; i<ANALOGTV_CB_START+(36-8)*ANALOGTV_SCALE; i++) {
1067         it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0f-cbfc) +
1068           sp[i]*it->agclevel*cbfc;
1069       }
1070     }
1071 
1072     {
1073       float tot=0.1f;
1074       float cbgain;
1075 
1076       for (i=0; i<4; i++) {
1077         tot += it->cb_phase[i]*it->cb_phase[i];
1078       }
1079       cbgain = 32.0f/sqrtf(tot);
1080 
1081       for (i=0; i<4; i++) {
1082         it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
1083       }
1084     }
1085 
1086 #ifdef DEBUG
1087     if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n",
1088                   cur_hsync,
1089                   it->cb_phase[0], it->cb_phase[1],
1090                   it->cb_phase[2], it->cb_phase[3]);
1091 #endif
1092 
1093     /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */
1094   }
1095 
1096   it->cur_hsync = cur_hsync;
1097   it->cur_vsync = cur_vsync;
1098 }
1099 
1100 static double
analogtv_levelmult(const analogtv * it,int level)1101 analogtv_levelmult(const analogtv *it, int level)
1102 {
1103   static const double levelfac[3]={-7.5, 5.5, 24.5};
1104   return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
1105 }
1106 
1107 static int
analogtv_level(const analogtv * it,int y,int ytop,int ybot)1108 analogtv_level(const analogtv *it, int y, int ytop, int ybot)
1109 {
1110   int level;
1111   if (ybot-ytop>=7) {
1112     if (y==ytop || y==ybot-1) level=0;
1113     else if (y==ytop+1 || y==ybot-2) level=1;
1114     else level=2;
1115   }
1116   else if (ybot-ytop>=5) {
1117     if (y==ytop || y==ybot-1) level=0;
1118     else level=2;
1119   }
1120   else if (ybot-ytop>=3) {
1121     if (y==ytop) level=0;
1122     else level=2;
1123   }
1124   else {
1125     level=2;
1126   }
1127   return level;
1128 }
1129 
1130 /*
1131   The point of this stuff is to ensure that when useheight is not a
1132   multiple of VISLINES so that TV scan lines map to different numbers
1133   of vertical screen pixels, the total brightness of each scan line
1134   remains the same.
1135   ANALOGTV_MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
1136   it interpolates extra black lines.
1137  */
1138 
1139 static void
analogtv_setup_levels(analogtv * it,double avgheight)1140 analogtv_setup_levels(analogtv *it, double avgheight)
1141 {
1142   int i,height;
1143   static const double levelfac[3]={-7.5, 5.5, 24.5};
1144 
1145   for (height=0; height<avgheight+2.0 && height<=ANALOGTV_MAX_LINEHEIGHT; height++) {
1146 
1147     for (i=0; i<height; i++) {
1148       it->leveltable[height][i].index = 2;
1149     }
1150 
1151     if (avgheight>=3) {
1152       it->leveltable[height][0].index=0;
1153     }
1154     if (avgheight>=5) {
1155       if (height >= 1) it->leveltable[height][height-1].index=0;
1156     }
1157     if (avgheight>=7) {
1158       it->leveltable[height][1].index=1;
1159       if (height >= 2) it->leveltable[height][height-2].index=1;
1160     }
1161 
1162     for (i=0; i<height; i++) {
1163       it->leveltable[height][i].value =
1164         (40.0 + levelfac[it->leveltable[height][i].index]*puramp(it, 3.0, 6.0, 1.0)) / 256.0;
1165     }
1166 
1167   }
1168 }
1169 
rnd_combine(unsigned * a0,unsigned * c0,unsigned a1,unsigned c1)1170 static void rnd_combine(unsigned *a0, unsigned *c0, unsigned a1, unsigned c1)
1171 {
1172   *a0 = (*a0 * a1) & 0xffffffffu;
1173   *c0 = (c1 + a1 * *c0) & 0xffffffffu;
1174 }
1175 
rnd_seek_ac(unsigned * a,unsigned * c,unsigned dist)1176 static void rnd_seek_ac(unsigned *a, unsigned *c, unsigned dist)
1177 {
1178   unsigned int a1 = *a, c1 = *c;
1179   *a = 1, *c = 0;
1180 
1181   while(dist)
1182   {
1183     if(dist & 1)
1184       rnd_combine(a, c, a1, c1);
1185     dist >>= 1;
1186     rnd_combine(&a1, &c1, a1, c1);
1187   }
1188 }
1189 
rnd_seek(unsigned a,unsigned c,unsigned rnd,unsigned dist)1190 static unsigned int rnd_seek(unsigned a, unsigned c, unsigned rnd, unsigned dist)
1191 {
1192   rnd_seek_ac(&a, &c, dist);
1193   return a * rnd + c;
1194 }
1195 
analogtv_init_signal(const analogtv * it,double noiselevel,unsigned start,unsigned end)1196 static void analogtv_init_signal(const analogtv *it, double noiselevel, unsigned start, unsigned end)
1197 {
1198   float *ps=it->rx_signal + start;
1199   float *pe=it->rx_signal + end;
1200   float *p=ps;
1201   unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random0, start);
1202   unsigned int fastrnd_offset;
1203   float nm1,nm2;
1204   float noisemul = sqrt(noiselevel*150)/(float)0x7fffffff;
1205 
1206   fastrnd_offset = fastrnd - 0x7fffffff;
1207   nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
1208   while (p != pe) {
1209     nm2=nm1;
1210     fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1211     fastrnd_offset = fastrnd - 0x7fffffff;
1212     nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
1213     *p++ = nm1*nm2;
1214   }
1215 }
1216 
analogtv_add_signal(const analogtv * it,const analogtv_reception * rec,unsigned start,unsigned end,int ec)1217 static void analogtv_add_signal(const analogtv *it, const analogtv_reception *rec, unsigned start, unsigned end, int ec)
1218 {
1219   analogtv_input *inp=rec->input;
1220   float *ps=it->rx_signal + start;
1221   float *pe=it->rx_signal + end;
1222   float *p=ps;
1223   signed char *ss=&inp->signal[0][0];
1224   signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
1225   signed char *s=ss + ((start + (unsigned)rec->ofs) % ANALOGTV_SIGNAL_LEN);
1226   signed char *s2;
1227   int i;
1228   float level=rec->level;
1229   float hfloss=rec->hfloss;
1230   unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random1, start);
1231   float dp[5];
1232 
1233   const float noise_decay = 0.99995f;
1234   float noise_ampl = 1.3f * powf(noise_decay, start);
1235 
1236   if (ec > end)
1237     ec = end;
1238 
1239   /* assert((se-ss)%4==0 && (se-s)%4==0); */
1240 
1241   for (i = start; i < ec; i++) { /* Sometimes start > ec. */
1242 
1243     /* Do a big noisy transition. We can make the transition noise of
1244        high constant strength regardless of signal strength.
1245 
1246        There are two separate state machines. here, One is the noise
1247        process and the other is the
1248 
1249        We don't bother with the FIR filter here
1250     */
1251 
1252     float sig0=(float)s[0];
1253     unsigned int fastrnd_offset = fastrnd - 0x7fffffff;
1254     float noise = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * (50.0f/(float)0x7fffffff);
1255     fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1256 
1257     p[0] += sig0 * level * (1.0f - noise_ampl) + noise * noise_ampl;
1258 
1259     noise_ampl *= noise_decay;
1260 
1261     p++;
1262     s++;
1263     if (s>=se) s=ss;
1264   }
1265 
1266   dp[0]=0.0;
1267   s2 = s;
1268   for (i=1; i<5; i++) {
1269     s2 -= 4;
1270     if (s2 < ss)
1271       s2 += ANALOGTV_SIGNAL_LEN;
1272     dp[i] = (float)((int)s2[0]+(int)s2[1]+(int)s2[2]+(int)s2[3]);
1273   }
1274 
1275   assert(p <= pe);
1276   assert(!((pe - p) % 4));
1277   while (p != pe) {
1278     float sig0,sig1,sig2,sig3,sigr;
1279 
1280     sig0=(float)s[0];
1281     sig1=(float)s[1];
1282     sig2=(float)s[2];
1283     sig3=(float)s[3];
1284 
1285     dp[0]=sig0+sig1+sig2+sig3;
1286 
1287     /* Get the video out signal, and add some ghosting, typical of RF
1288        monitor cables. This corresponds to a pretty long cable, but
1289        looks right to me.
1290     */
1291 
1292     sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] +
1293           dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]);
1294     dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0];
1295 
1296     p[0] += (sig0+sigr + sig2*hfloss) * level;
1297     p[1] += (sig1+sigr + sig3*hfloss) * level;
1298     p[2] += (sig2+sigr + sig0*hfloss) * level;
1299     p[3] += (sig3+sigr + sig1*hfloss) * level;
1300 
1301     p += 4;
1302     s += 4;
1303     if (s>=se) s = ss + (s-se);
1304   }
1305 
1306   assert(p == pe);
1307 }
1308 
analogtv_thread_add_signals(void * thread_raw)1309 static void analogtv_thread_add_signals(void *thread_raw)
1310 {
1311   const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1312   const analogtv *it = thread->it;
1313   unsigned i, j;
1314   unsigned subtotal_end;
1315 
1316   unsigned start = thread->signal_start;
1317   while(start != thread->signal_end)
1318   {
1319     float *p;
1320 
1321     /* Work on 8 KB blocks; these should fit in L1. */
1322     /* (Though it doesn't seem to help much on my system.) */
1323     unsigned end = start + 2048;
1324     if(end > thread->signal_end)
1325       end = thread->signal_end;
1326 
1327     analogtv_init_signal (it, it->noiselevel, start, end);
1328 
1329     for (i = 0; i != it->rec_count; ++i) {
1330       analogtv_add_signal (it, it->recs[i], start, end,
1331                            !i ? it->channel_change_cycles : 0);
1332     }
1333 
1334     assert (!(start % ANALOGTV_SUBTOTAL_LEN));
1335     assert (!(end % ANALOGTV_SUBTOTAL_LEN));
1336 
1337     p = it->rx_signal + start;
1338     subtotal_end = end / ANALOGTV_SUBTOTAL_LEN;
1339     for (i = start / ANALOGTV_SUBTOTAL_LEN; i != subtotal_end; ++i) {
1340       float sum = p[0];
1341       for (j = 1; j != ANALOGTV_SUBTOTAL_LEN; ++j)
1342         sum += p[j];
1343       it->signal_subtotals[i] = sum;
1344       p += ANALOGTV_SUBTOTAL_LEN;
1345     }
1346 
1347     start = end;
1348   }
1349 }
1350 
analogtv_get_line(const analogtv * it,int lineno,int * slineno,int * ytop,int * ybot,unsigned * signal_offset)1351 static int analogtv_get_line(const analogtv *it, int lineno, int *slineno,
1352                              int *ytop, int *ybot, unsigned *signal_offset)
1353 {
1354   *slineno=lineno-ANALOGTV_TOP;
1355   *ytop=(int)((*slineno*it->useheight/ANALOGTV_VISLINES -
1356                   it->useheight/2)*it->puheight) + it->useheight/2;
1357   *ybot=(int)(((*slineno+1)*it->useheight/ANALOGTV_VISLINES -
1358                   it->useheight/2)*it->puheight) + it->useheight/2;
1359 #if 0
1360   int linesig=analogtv_line_signature(input,lineno)
1361     + it->hashnoise_times[lineno];
1362 #endif
1363   *signal_offset = ((lineno+it->cur_vsync+ANALOGTV_V) % ANALOGTV_V) * ANALOGTV_H +
1364                     it->line_hsync[lineno];
1365 
1366   if (*ytop==*ybot) return 0;
1367   if (*ybot<0 || *ytop>it->useheight) return 0;
1368   if (*ytop<0) *ytop=0;
1369   if (*ybot>it->useheight) *ybot=it->useheight;
1370 
1371   if (*ybot > *ytop+ANALOGTV_MAX_LINEHEIGHT) *ybot=*ytop+ANALOGTV_MAX_LINEHEIGHT;
1372   return 1;
1373 }
1374 
1375 static void
analogtv_blast_imagerow(const analogtv * it,float * rgbf,float * rgbf_end,int ytop,int ybot)1376 analogtv_blast_imagerow(const analogtv *it,
1377                         float *rgbf, float *rgbf_end,
1378                         int ytop, int ybot)
1379 {
1380   int i,j,x,y;
1381   float *rpf;
1382   char *level_copyfrom[3];
1383   int xrepl=it->xrepl;
1384   unsigned lineheight = ybot - ytop;
1385   if (lineheight > ANALOGTV_MAX_LINEHEIGHT) lineheight = ANALOGTV_MAX_LINEHEIGHT;
1386   for (i=0; i<3; i++) level_copyfrom[i]=NULL;
1387 
1388   for (y=ytop; y<ybot; y++) {
1389     char *rowdata=it->image->data + y*it->image->bytes_per_line;
1390     unsigned line = y-ytop;
1391 
1392     int level=it->leveltable[lineheight][line].index;
1393     float levelmult=it->leveltable[lineheight][line].value;
1394 
1395     /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
1396        why standard graphics sw has to be fast, or else people will have to
1397        work around it and risk incompatibility. The quickdraw folks
1398        understood this. The other answer would be for X11 to have fewer
1399        formats for bitm.. oh, never mind. If neither of these cases work
1400        (they probably cover 99% of setups) it falls back on the Xlib
1401        routines. */
1402 
1403     if (level_copyfrom[level]) {
1404       memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line);
1405     }
1406     else {
1407       level_copyfrom[level] = rowdata;
1408 
1409       if (0) {
1410       }
1411       else if (it->image->format==ZPixmap &&
1412                it->image->bits_per_pixel==32 &&
1413                sizeof(unsigned int)==4 &&
1414                it->image->byte_order==localbyteorder) {
1415         /* int is more likely to be 32 bits than long */
1416         unsigned int *pixelptr=(unsigned int *)rowdata;
1417         unsigned int pix;
1418 
1419         for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1420           int ntscri=rpf[0]*levelmult;
1421           int ntscgi=rpf[1]*levelmult;
1422           int ntscbi=rpf[2]*levelmult;
1423           if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1424           if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1425           if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1426           pix = (it->red_values[ntscri] |
1427                  it->green_values[ntscgi] |
1428                  it->blue_values[ntscbi]);
1429           pixelptr[0] = pix;
1430           if (xrepl>=2) {
1431             pixelptr[1] = pix;
1432             if (xrepl>=3) pixelptr[2] = pix;
1433           }
1434           pixelptr+=xrepl;
1435         }
1436       }
1437       else if (it->image->format==ZPixmap &&
1438                it->image->bits_per_pixel==16 &&
1439                sizeof(unsigned short)==2 &&
1440                float_extraction_works &&
1441                it->image->byte_order==localbyteorder) {
1442         unsigned short *pixelptr=(unsigned short *)rowdata;
1443         float r2,g2,b2;
1444         float_extract_t r1,g1,b1;
1445         unsigned short pix;
1446 
1447         for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1448           r2=rpf[0]; g2=rpf[1]; b2=rpf[2];
1449           r1.f=r2 * levelmult+float_low8_ofs;
1450           g1.f=g2 * levelmult+float_low8_ofs;
1451           b1.f=b2 * levelmult+float_low8_ofs;
1452           pix = (it->red_values[r1.i & 0x3ff] |
1453                  it->green_values[g1.i & 0x3ff] |
1454                  it->blue_values[b1.i & 0x3ff]);
1455           pixelptr[0] = pix;
1456           if (xrepl>=2) {
1457             pixelptr[1] = pix;
1458             if (xrepl>=3) pixelptr[2] = pix;
1459           }
1460           pixelptr+=xrepl;
1461         }
1462       }
1463       else if (it->image->format==ZPixmap &&
1464                it->image->bits_per_pixel==16 &&
1465                sizeof(unsigned short)==2 &&
1466                it->image->byte_order==localbyteorder) {
1467         unsigned short *pixelptr=(unsigned short *)rowdata;
1468         unsigned short pix;
1469 
1470         for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1471           int r1=rpf[0] * levelmult;
1472           int g1=rpf[1] * levelmult;
1473           int b1=rpf[2] * levelmult;
1474           if (r1>=ANALOGTV_CV_MAX) r1=ANALOGTV_CV_MAX-1;
1475           if (g1>=ANALOGTV_CV_MAX) g1=ANALOGTV_CV_MAX-1;
1476           if (b1>=ANALOGTV_CV_MAX) b1=ANALOGTV_CV_MAX-1;
1477           pix = it->red_values[r1] | it->green_values[g1] | it->blue_values[b1];
1478           pixelptr[0] = pix;
1479           if (xrepl>=2) {
1480             pixelptr[1] = pix;
1481             if (xrepl>=3) pixelptr[2] = pix;
1482           }
1483           pixelptr+=xrepl;
1484         }
1485       }
1486       else {
1487         for (x=0, rpf=rgbf; rpf!=rgbf_end ; x++, rpf+=3) {
1488           int ntscri=rpf[0]*levelmult;
1489           int ntscgi=rpf[1]*levelmult;
1490           int ntscbi=rpf[2]*levelmult;
1491           if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1492           if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1493           if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1494           for (j=0; j<xrepl; j++) {
1495             XPutPixel(it->image, x*xrepl + j, y,
1496                       it->red_values[ntscri] | it->green_values[ntscgi] |
1497                       it->blue_values[ntscbi]);
1498           }
1499         }
1500       }
1501     }
1502   }
1503 }
1504 
analogtv_thread_draw_lines(void * thread_raw)1505 static void analogtv_thread_draw_lines(void *thread_raw)
1506 {
1507   const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1508   const analogtv *it = thread->it;
1509 
1510   int lineno;
1511 
1512   float *raw_rgb_start;
1513   float *raw_rgb_end;
1514   raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
1515 
1516   if (! raw_rgb_start) return;
1517 
1518   raw_rgb_end=raw_rgb_start+3*it->subwidth;
1519 
1520   for (lineno=ANALOGTV_TOP + thread->thread_id;
1521        lineno<ANALOGTV_BOT;
1522        lineno += it->threads.count) {
1523     int i,j,x,y;
1524 
1525     int slineno, ytop, ybot;
1526     unsigned signal_offset;
1527 
1528     const float *signal;
1529 
1530     int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
1531     float *rgb_start, *rgb_end;
1532     float pixbright;
1533     int pixmultinc;
1534 
1535     float *rrp;
1536 
1537     struct analogtv_yiq_s yiq[ANALOGTV_PIC_LEN+10];
1538 
1539     if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot,
1540         &signal_offset))
1541       continue;
1542 
1543     signal = it->rx_signal + signal_offset;
1544 
1545     {
1546 
1547       float bloomthisrow,shiftthisrow;
1548       float viswidth,middle;
1549       float scanwidth;
1550       int scw,scl,scr;
1551 
1552       bloomthisrow = -10.0f * it->crtload[lineno];
1553       if (bloomthisrow<-10.0f) bloomthisrow=-10.0f;
1554       if (bloomthisrow>2.0f) bloomthisrow=2.0f;
1555       if (slineno<16) {
1556         shiftthisrow=it->horiz_desync * (expf(-0.17f*slineno) *
1557                                          (0.7f+cosf(slineno*0.6f)));
1558       } else {
1559         shiftthisrow=0.0f;
1560       }
1561 
1562       viswidth=ANALOGTV_PIC_LEN * 0.79f - 5.0f*bloomthisrow;
1563       middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
1564 
1565       scanwidth=it->width_control * puramp(it, 0.5f, 0.3f, 1.0f);
1566 
1567       scw=it->subwidth*scanwidth;
1568       if (scw>it->subwidth) scw=it->usewidth;
1569       scl=it->subwidth/2 - scw/2;
1570       scr=it->subwidth/2 + scw/2;
1571 
1572       pixrate=(int)((viswidth*65536.0f*1.0f)/it->subwidth)/scanwidth;
1573       scanstart_i=(int)((middle-viswidth*0.5f)*65536.0f);
1574       scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
1575       squishright_i=(int)((middle+viswidth*(0.25f + 0.25f*puramp(it, 2.0f, 0.0f, 1.1f)
1576                                             - it->squish_control)) *65536.0f);
1577       squishdiv=it->subwidth/15;
1578 
1579       rgb_start=raw_rgb_start+scl*3;
1580       rgb_end=raw_rgb_start+scr*3;
1581 
1582       assert(scanstart_i>=0);
1583 
1584 #ifdef DEBUG
1585       if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
1586                     lineno,
1587                     scanstart_i/65536.0f,
1588                     squishright_i/65536.0f,
1589                     scanend_i/65536.0f,
1590                     scl,scr,scw);
1591 #endif
1592     }
1593 
1594     if (it->use_cmap) {
1595       for (y=ytop; y<ybot; y++) {
1596         int level=analogtv_level(it, y, ytop, ybot);
1597         float levelmult=analogtv_levelmult(it, level);
1598         float levelmult_y = levelmult * it->contrast_control
1599           * puramp(it, 1.0f, 0.0f, 1.0f) / (0.5f+0.5f*it->puheight) * 0.070f;
1600         float levelmult_iq = levelmult * 0.090f;
1601 
1602         analogtv_ntsc_to_yiq(it, lineno, signal,
1603                              (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1604         pixmultinc=pixrate;
1605 
1606         x=0;
1607         i=scanstart_i;
1608         while (i<0 && x<it->usewidth) {
1609           XPutPixel(it->image, x, y, it->colors[0]);
1610           i+=pixmultinc;
1611           x++;
1612         }
1613 
1614         while (i<scanend_i && x<it->usewidth) {
1615           float pixfrac=(i&0xffff)/65536.0f;
1616           float invpixfrac=(1.0f-pixfrac);
1617           int pati=i>>16;
1618           int yli,ili,qli,cmi;
1619 
1620           float interpy=(yiq[pati].y*invpixfrac
1621                          + yiq[pati+1].y*pixfrac) * levelmult_y;
1622           float interpi=(yiq[pati].i*invpixfrac
1623                          + yiq[pati+1].i*pixfrac) * levelmult_iq;
1624           float interpq=(yiq[pati].q*invpixfrac
1625                          + yiq[pati+1].q*pixfrac) * levelmult_iq;
1626 
1627           yli = (int)(interpy * it->cmap_y_levels);
1628           ili = (int)((interpi+0.5f) * it->cmap_i_levels);
1629           qli = (int)((interpq+0.5f) * it->cmap_q_levels);
1630           if (yli<0) yli=0;
1631           if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
1632           if (ili<0) ili=0;
1633           if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1;
1634           if (qli<0) qli=0;
1635           if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1;
1636 
1637           cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli);
1638 
1639 #ifdef DEBUG
1640           if ((random()%65536)==0) {
1641             printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n",
1642                    interpy, interpi, interpq,
1643                    yli, ili, qli,
1644                    cmi);
1645           }
1646 #endif
1647 
1648           for (j=0; j<it->xrepl; j++) {
1649             XPutPixel(it->image, x, y,
1650                       it->colors[cmi]);
1651             x++;
1652           }
1653           if (i >= squishright_i) {
1654             pixmultinc += pixmultinc/squishdiv;
1655           }
1656           i+=pixmultinc;
1657         }
1658         while (x<it->usewidth) {
1659           XPutPixel(it->image, x, y, it->colors[0]);
1660           x++;
1661         }
1662       }
1663     }
1664     else {
1665       analogtv_ntsc_to_yiq(it, lineno, signal,
1666                            (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1667 
1668       pixbright=it->contrast_control * puramp(it, 1.0f, 0.0f, 1.0f)
1669         / (0.5f+0.5f*it->puheight) * 1024.0f/100.0f;
1670       pixmultinc=pixrate;
1671       i=scanstart_i; rrp=rgb_start;
1672       while (i<0 && rrp!=rgb_end) {
1673         rrp[0]=rrp[1]=rrp[2]=0;
1674         i+=pixmultinc;
1675         rrp+=3;
1676       }
1677       while (i<scanend_i && rrp!=rgb_end) {
1678         float pixfrac=(i&0xffff)/65536.0f;
1679         float invpixfrac=1.0f-pixfrac;
1680         int pati=i>>16;
1681         float r,g,b;
1682 
1683         float interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
1684         float interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
1685         float interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
1686 
1687         /*
1688           According to the NTSC spec, Y,I,Q are generated as:
1689 
1690           y=0.30 r + 0.59 g + 0.11 b
1691           i=0.60 r - 0.28 g - 0.32 b
1692           q=0.21 r - 0.52 g + 0.31 b
1693 
1694           So if you invert the implied 3x3 matrix you get what standard
1695           televisions implement with a bunch of resistors (or directly in the
1696           CRT -- don't ask):
1697 
1698           r = y + 0.948 i + 0.624 q
1699           g = y - 0.276 i - 0.639 q
1700           b = y - 1.105 i + 1.729 q
1701         */
1702 
1703         r=(interpy + 0.948f*interpi + 0.624f*interpq) * pixbright;
1704         g=(interpy - 0.276f*interpi - 0.639f*interpq) * pixbright;
1705         b=(interpy - 1.105f*interpi + 1.729f*interpq) * pixbright;
1706         if (r<0.0f) r=0.0f;
1707         if (g<0.0f) g=0.0f;
1708         if (b<0.0f) b=0.0f;
1709         rrp[0]=r;
1710         rrp[1]=g;
1711         rrp[2]=b;
1712 
1713         if (i>=squishright_i) {
1714           pixmultinc += pixmultinc/squishdiv;
1715           pixbright += pixbright/squishdiv/2;
1716         }
1717         i+=pixmultinc;
1718         rrp+=3;
1719       }
1720       while (rrp != rgb_end) {
1721         rrp[0]=rrp[1]=rrp[2]=0.0f;
1722         rrp+=3;
1723       }
1724 
1725       analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end,
1726                               ytop,ybot);
1727     }
1728   }
1729 
1730   free(raw_rgb_start);
1731 }
1732 
1733 void
analogtv_draw(analogtv * it,double noiselevel,const analogtv_reception * const * recs,unsigned rec_count)1734 analogtv_draw(analogtv *it, double noiselevel,
1735               const analogtv_reception *const *recs, unsigned rec_count)
1736 {
1737   int i,lineno;
1738   /*  int bigloadchange,drawcount;*/
1739   double baseload;
1740   int overall_top, overall_bot;
1741 
1742   /* AnalogTV isn't very interesting if there isn't enough RAM. */
1743   if (!it->image)
1744     return;
1745 
1746   it->rx_signal_level = noiselevel;
1747   for (i = 0; i != rec_count; ++i) {
1748     const analogtv_reception *rec = recs[i];
1749     double level = rec->level;
1750     analogtv_input *inp=rec->input;
1751 
1752     it->rx_signal_level =
1753       sqrt(it->rx_signal_level * it->rx_signal_level +
1754            (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] +
1755                                         rec->ghostfir[2] + rec->ghostfir[3]))));
1756 
1757     /* duplicate the first line into the Nth line to ease wraparound computation */
1758     memcpy(inp->signal[ANALOGTV_V], inp->signal[0],
1759            ANALOGTV_H * sizeof(inp->signal[0][0]));
1760   }
1761 
1762   analogtv_setup_frame(it);
1763   analogtv_set_demod(it);
1764 
1765   it->random0 = random();
1766   it->random1 = random();
1767   it->noiselevel = noiselevel;
1768   it->recs = recs;
1769   it->rec_count = rec_count;
1770   threadpool_run(&it->threads, analogtv_thread_add_signals);
1771   threadpool_wait(&it->threads);
1772 
1773   it->channel_change_cycles=0;
1774 
1775   /* rx_signal has an extra 2 lines at the end, where we copy the
1776      first 2 lines so we can index into it while only worrying about
1777      wraparound on a per-line level */
1778   memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN],
1779          &it->rx_signal[0],
1780          2*ANALOGTV_H*sizeof(it->rx_signal[0]));
1781 
1782   /* Repeat for signal_subtotals. */
1783 
1784   memcpy(&it->signal_subtotals[ANALOGTV_SIGNAL_LEN / ANALOGTV_SUBTOTAL_LEN],
1785          &it->signal_subtotals[0],
1786          (2*ANALOGTV_H/ANALOGTV_SUBTOTAL_LEN)*sizeof(it->signal_subtotals[0]));
1787 
1788   analogtv_sync(it); /* Requires the add_signals be complete. */
1789 
1790   baseload=0.5;
1791   /* if (it->hashnoise_on) baseload=0.5; */
1792 
1793   /*bigloadchange=1;
1794     drawcount=0;*/
1795   it->crtload[ANALOGTV_TOP-1]=baseload;
1796   it->puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
1797     (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
1798 
1799   analogtv_setup_levels(it, it->puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
1800 
1801   /* calculate tint once per frame */
1802   it->tint_i = -cos((103 + it->tint_control)*3.1415926/180);
1803   it->tint_q = sin((103 + it->tint_control)*3.1415926/180);
1804 
1805   for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
1806     int slineno, ytop, ybot;
1807     unsigned signal_offset;
1808     if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot, &signal_offset))
1809       continue;
1810 
1811     if (lineno==it->shrinkpulse) {
1812       baseload += 0.4;
1813       /*bigloadchange=1;*/
1814       it->shrinkpulse=-1;
1815     }
1816 
1817 #if 0
1818     if (it->hashnoise_rpm>0.0 &&
1819         !(bigloadchange ||
1820           it->redraw_all ||
1821           (slineno<20 && it->flutter_horiz_desync) ||
1822           it->gaussiannoise_level>30 ||
1823           ((it->gaussiannoise_level>2.0 ||
1824             it->multipath) && random()%4) ||
1825           linesig != it->onscreen_signature[lineno])) {
1826       continue;
1827     }
1828     it->onscreen_signature[lineno] = linesig;
1829 #endif
1830     /*    drawcount++;*/
1831 
1832     /*
1833       Interpolate the 600-dotclock line into however many horizontal
1834       screen pixels we're using, and convert to RGB.
1835 
1836       We add some 'bloom', variations in the horizontal scan width with
1837       the amount of brightness, extremely common on period TV sets. They
1838       had a single oscillator which generated both the horizontal scan and
1839       (during the horizontal retrace interval) the high voltage for the
1840       electron beam. More brightness meant more load on the oscillator,
1841       which caused an decrease in horizontal deflection. Look for
1842       (bloomthisrow).
1843 
1844       Also, the A2 did a bad job of generating horizontal sync pulses
1845       during the vertical blanking interval. This, and the fact that the
1846       horizontal frequency was a bit off meant that TVs usually went a bit
1847       out of sync during the vertical retrace, and the top of the screen
1848       would be bent a bit to the left or right. Look for (shiftthisrow).
1849 
1850       We also add a teeny bit of left overscan, just enough to be
1851       annoying, but you can still read the left column of text.
1852 
1853       We also simulate compression & brightening on the right side of the
1854       screen. Most TVs do this, but you don't notice because they overscan
1855       so it's off the right edge of the CRT. But the A2 video system used
1856       so much of the horizontal scan line that you had to crank the
1857       horizontal width down in order to not lose the right few characters,
1858       and you'd see the compression on the right edge. Associated with
1859       compression is brightening; since the electron beam was scanning
1860       slower, the same drive signal hit the phosphor harder. Look for
1861       (squishright_i) and (squishdiv).
1862     */
1863 
1864     {
1865       /* This used to be an int, I suspect by mistake. - Dave */
1866       float totsignal=0;
1867       float ncl/*,diff*/;
1868       unsigned frac;
1869       size_t end0, end1;
1870       const float *p;
1871 
1872       frac = signal_offset & (ANALOGTV_SUBTOTAL_LEN - 1);
1873       p = it->rx_signal + (signal_offset & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1874       for (i=0; i != frac; i++) {
1875         totsignal -= p[i];
1876       }
1877 
1878       end0 = (signal_offset + ANALOGTV_PIC_LEN);
1879 
1880       end1 = end0 / ANALOGTV_SUBTOTAL_LEN;
1881       for (i=signal_offset / ANALOGTV_SUBTOTAL_LEN; i<end1; i++) {
1882         totsignal += it->signal_subtotals[i];
1883       }
1884 
1885       frac = end0 & (ANALOGTV_SUBTOTAL_LEN - 1);
1886       p = it->rx_signal + (end0 & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1887       for (i=0; i != frac; i++) {
1888         totsignal += p[i];
1889       }
1890 
1891       totsignal *= it->agclevel;
1892       ncl = 0.95f * it->crtload[lineno-1] +
1893         0.05f*(baseload +
1894                (totsignal-30000)/100000.0f +
1895                (slineno>184 ? (slineno-184)*(lineno-184)*0.001f * it->squeezebottom
1896                 : 0.0f));
1897       /*diff=ncl - it->crtload[lineno];*/
1898       /*bigloadchange = (diff>0.01 || diff<-0.01);*/
1899       it->crtload[lineno]=ncl;
1900     }
1901   }
1902 
1903   threadpool_run(&it->threads, analogtv_thread_draw_lines);
1904   threadpool_wait(&it->threads);
1905 
1906 #if 0
1907   /* poor attempt at visible retrace */
1908   for (i=0; i<15; i++) {
1909     int ytop=(int)((i*it->useheight/15 -
1910                     it->useheight/2)*puheight) + it->useheight/2;
1911     int ybot=(int)(((i+1)*it->useheight/15 -
1912                     it->useheight/2)*puheight) + it->useheight/2;
1913     int div=it->usewidth*3/2;
1914 
1915     for (x=0; x<it->usewidth; x++) {
1916       y = ytop + (ybot-ytop)*x / div;
1917       if (y<0 || y>=it->useheight) continue;
1918       XPutPixel(it->image, x, y, 0xffffff);
1919     }
1920   }
1921 #endif
1922 
1923   if (it->need_clear) {
1924     XClearWindow(it->dpy, it->window);
1925     it->need_clear=0;
1926   }
1927 
1928   /*
1929     Subtle change: overall_bot was the bottom of the last scan line. Now it's
1930     the top of the next-after-the-last scan line. This is the same until
1931     the y-dimension is > 2400, note ANALOGTV_MAX_LINEHEIGHT.
1932   */
1933 
1934   overall_top=(int)(it->useheight*(1-it->puheight)/2);
1935   overall_bot=(int)(it->useheight*(1+it->puheight)/2);
1936 
1937   if (overall_top<0) overall_top=0;
1938   if (overall_bot>it->useheight) overall_bot=it->useheight;
1939 
1940   if (overall_top>0) {
1941     XClearArea(it->dpy, it->window,
1942                it->screen_xo, it->screen_yo,
1943                it->usewidth, overall_top, 0);
1944   }
1945   if (it->useheight > overall_bot) {
1946     XClearArea(it->dpy, it->window,
1947                it->screen_xo, it->screen_yo+overall_bot,
1948                it->usewidth, it->useheight-overall_bot, 0);
1949   }
1950 
1951   if (overall_bot > overall_top) {
1952     put_xshm_image(it->dpy, it->window, it->gc, it->image,
1953                    0, overall_top,
1954                    it->screen_xo, it->screen_yo+overall_top,
1955                    it->usewidth, overall_bot - overall_top,
1956                    &it->shm_info);
1957   }
1958 
1959 #ifdef DEBUG
1960   if (0) {
1961     struct timeval tv;
1962     double fps;
1963     char buf[256];
1964     gettimeofday(&tv,NULL);
1965 
1966     fps=1.0/((tv.tv_sec - it->last_display_time.tv_sec)
1967              + 0.000001*(tv.tv_usec - it->last_display_time.tv_usec));
1968     sprintf(buf, "FPS=%0.1f",fps);
1969     XDrawString(it->dpy, it->window, it->gc, 50, it->useheight*2/3,
1970                 buf, strlen(buf));
1971 
1972     it->last_display_time=tv;
1973   }
1974 #endif
1975 }
1976 
1977 analogtv_input *
analogtv_input_allocate()1978 analogtv_input_allocate()
1979 {
1980   analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input));
1981 
1982   return ret;
1983 }
1984 
1985 /*
1986   This takes a screen image and encodes it as a video camera would,
1987   including all the bandlimiting and YIQ modulation.
1988   This isn't especially tuned for speed.
1989 
1990   xoff, yoff: top left corner of rendered image, in window pixels.
1991   w, h: scaled size of rendered image, in window pixels.
1992   mask: BlackPixel means don't render (it's not full alpha)
1993 */
1994 
1995 int
analogtv_load_ximage(analogtv * it,analogtv_input * input,XImage * pic_im,XImage * mask_im,int xoff,int yoff,int target_w,int target_h)1996 analogtv_load_ximage(analogtv *it, analogtv_input *input,
1997                      XImage *pic_im, XImage *mask_im,
1998                      int xoff, int yoff, int target_w, int target_h)
1999 {
2000   int i,x,y;
2001   int img_w,img_h;
2002   int fyx[7],fyy[7];
2003   int fix[4],fiy[4];
2004   int fqx[4],fqy[4];
2005   XColor col1[ANALOGTV_PIC_LEN];
2006   XColor col2[ANALOGTV_PIC_LEN];
2007   char mask[ANALOGTV_PIC_LEN];
2008   int multiq[ANALOGTV_PIC_LEN+4];
2009   unsigned long black = 0; /* not BlackPixelOfScreen (it->xgwa.screen); */
2010 
2011   int x_length=ANALOGTV_PIC_LEN;
2012   int y_overscan=5*ANALOGTV_SCALE; /* overscan this much top and bottom */
2013   int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
2014 
2015   if (target_w > 0) x_length     = x_length     * target_w / it->xgwa.width;
2016   if (target_h > 0) y_scanlength = y_scanlength * target_h / it->xgwa.height;
2017 
2018   img_w = pic_im->width;
2019   img_h = pic_im->height;
2020 
2021   xoff = ANALOGTV_PIC_LEN  * xoff / it->xgwa.width;
2022   yoff = ANALOGTV_VISLINES * yoff / it->xgwa.height;
2023 
2024   for (i=0; i<x_length+4; i++) {
2025     double phase=90.0-90.0*i;
2026     double ampl=1.0;
2027     multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
2028   }
2029 
2030   for (y=0; y<y_scanlength; y++) {
2031     int picy1=(y*img_h)/y_scanlength;
2032     int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
2033 
2034     for (x=0; x<x_length; x++) {
2035       int picx=(x*img_w)/x_length;
2036       col1[x].pixel=XGetPixel(pic_im, picx, picy1);
2037       col2[x].pixel=XGetPixel(pic_im, picx, picy2);
2038       if (mask_im)
2039         mask[x] = (XGetPixel(mask_im, picx, picy1) != black);
2040       else
2041         mask[x] = 1;
2042     }
2043     XQueryColors(it->dpy, it->colormap, col1, x_length);
2044     XQueryColors(it->dpy, it->colormap, col2, x_length);
2045     for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
2046     for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
2047 
2048     for (x=0; x<x_length; x++) {
2049       int rawy,rawi,rawq;
2050       int filty,filti,filtq;
2051       int composite;
2052 
2053       if (!mask[x]) continue;
2054 
2055       /* Compute YIQ as:
2056            y=0.30 r + 0.59 g + 0.11 b
2057            i=0.60 r - 0.28 g - 0.32 b
2058            q=0.21 r - 0.52 g + 0.31 b
2059           The coefficients below are in .4 format */
2060 
2061       rawy=( 5*col1[x].red + 11*col1[x].green + 2*col1[x].blue +
2062              5*col2[x].red + 11*col2[x].green + 2*col2[x].blue)>>7;
2063       rawi=(10*col1[x].red -  4*col1[x].green - 5*col1[x].blue +
2064             10*col2[x].red -  4*col2[x].green - 5*col2[x].blue)>>7;
2065       rawq=( 3*col1[x].red -  8*col1[x].green + 5*col1[x].blue +
2066              3*col2[x].red -  8*col2[x].green + 5*col2[x].blue)>>7;
2067 
2068       /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz
2069          with an extra zero at 3.5 MHz, from
2070          mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l */
2071 
2072       fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3];
2073       fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6];
2074       fyx[6] = (rawy * 1897) >> 16;
2075       fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3];
2076       fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6];
2077       fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
2078         + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
2079       filty = fyy[6];
2080 
2081       /* Filter I at 1.5 MHz. 3 pole Butterworth from
2082          mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */
2083 
2084       fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
2085       fix[3] = (rawi * 1413) >> 16;
2086       fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
2087       fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
2088         + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
2089       filti = fiy[3];
2090 
2091       /* Filter Q at 0.5 MHz. 3 pole Butterworth from
2092          mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */
2093 
2094       fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
2095       fqx[3] = (rawq * 75) >> 16;
2096       fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
2097       fqy[3] = (fqx[0]+fqx[3]) + 3 * (fqx[1]+fqx[2])
2098         + ((2612*fqy[0] - 9007*fqy[1] + 10453 * fqy[2]) >> 12);
2099       filtq = fqy[3];
2100 
2101 
2102       composite = filty + ((multiq[x] * filti + multiq[x+3] * filtq)>>12);
2103       composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
2104       if (composite>125) composite=125;
2105       if (composite<0) composite=0;
2106 
2107       input->signal[y-y_overscan+ANALOGTV_TOP+yoff][x+ANALOGTV_PIC_START+xoff] = composite;
2108     }
2109   }
2110 
2111   return 1;
2112 }
2113 
2114 #if 0
2115 void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
2116 {
2117   int x,y,newsig;
2118   int change=random()%ANALOGTV_V;
2119   unsigned int fastrnd=random();
2120   double hso=(int)(random()%1000)-500;
2121   int yofs=random()%ANALOGTV_V;
2122   int noise;
2123 
2124   for (y=change; y<ANALOGTV_V; y++) {
2125     int s2y=(y+yofs)%ANALOGTV_V;
2126     int filt=0;
2127     int noiselevel=60000 / (y-change+100);
2128 
2129     it->line_hsync[y] = s2->line_hsync[y] + (int)hso;
2130     hso *= 0.9;
2131     for (x=0; x<ANALOGTV_H; x++) {
2132       FASTRND;
2133       filt+= (-filt/16) + (int)(fastrnd&0xfff)-0x800;
2134       noise=(filt*noiselevel)>>16;
2135       newsig=s2->signal[s2y][x] + noise;
2136       if (newsig>120) newsig=120;
2137       if (newsig<0) newsig=0;
2138       it->signal[y][x]=newsig;
2139     }
2140   }
2141   s2->vsync=yofs;
2142 }
2143 #endif
2144 
2145 
2146 #ifdef FIXME
2147 /* add hash */
2148   if (it->hashnoise_times[lineno]) {
2149     int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno];
2150 
2151     if (hnt>=0 && hnt<ANALOGTV_PIC_LEN) {
2152       double maxampl=1.0;
2153       double cur=frand(150.0)-20.0;
2154       int len=random()%15+3;
2155       if (len > ANALOGTV_PIC_LEN-hnt) len=ANALOGTV_PIC_LEN-hnt;
2156       for (i=0; i<len; i++) {
2157         double sig=signal[hnt];
2158 
2159         sig += cur*maxampl;
2160         cur += frand(5.0)-5.0;
2161         maxampl = maxampl*0.9;
2162 
2163         signal[hnt]=sig;
2164         hnt++;
2165       }
2166     }
2167   }
2168 #endif
2169 
2170 
2171 void
analogtv_reception_update(analogtv_reception * rec)2172 analogtv_reception_update(analogtv_reception *rec)
2173 {
2174   int i;
2175 
2176   if (rec->multipath > 0.0) {
2177     for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2178       rec->ghostfir2[i] +=
2179         -(rec->ghostfir2[i]/16.0) + rec->multipath * (frand(0.02)-0.01);
2180     }
2181     if (random()%20==0) {
2182       rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)]
2183         = rec->multipath * (frand(0.08)-0.04);
2184     }
2185     for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2186       rec->ghostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i];
2187     }
2188 
2189     if (0) {
2190       rec->hfloss2 += -(rec->hfloss2/16.0) + rec->multipath * (frand(0.08)-0.04);
2191       rec->hfloss = 0.5*rec->hfloss + 0.5*rec->hfloss2;
2192     }
2193 
2194   } else {
2195     for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2196       rec->ghostfir[i] = (i>=ANALOGTV_GHOSTFIR_LEN/2) ? ((i&1) ? +0.04 : -0.08) /ANALOGTV_GHOSTFIR_LEN
2197         : 0.0;
2198     }
2199   }
2200 }
2201 
2202 
2203 /* jwz: since MacOS doesn't have "6x10", I dumped this font to a PNG...
2204  */
2205 
2206 #include "images/gen/6x10font_png.h"
2207 
2208 void
analogtv_make_font(Display * dpy,Window window,analogtv_font * f,int w,int h,char * fontname)2209 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
2210                    int w, int h, char *fontname)
2211 {
2212   int i;
2213   XFontStruct *font;
2214   Pixmap text_pm;
2215   GC gc;
2216   XGCValues gcv;
2217   XWindowAttributes xgwa;
2218 
2219   f->char_w = w;
2220   f->char_h = h;
2221 
2222   XGetWindowAttributes (dpy, window, &xgwa);
2223 
2224   if (fontname && !strcmp (fontname, "6x10")) {
2225 
2226     int pix_w, pix_h;
2227     XWindowAttributes xgwa;
2228     Pixmap m = 0;
2229     Pixmap p = image_data_to_pixmap (dpy, window,
2230                                      _6x10font_png, sizeof(_6x10font_png),
2231                                      &pix_w, &pix_h, &m);
2232     XImage *im = XGetImage (dpy, p, 0, 0, pix_w, pix_h, ~0L, ZPixmap);
2233     XImage *mm = XGetImage (dpy, m, 0, 0, pix_w, pix_h, 1, XYPixmap);
2234     unsigned long black = BlackPixelOfScreen (DefaultScreenOfDisplay (dpy));
2235     int x, y;
2236 
2237     XFreePixmap (dpy, p);
2238     XFreePixmap (dpy, m);
2239     if (pix_w != 256*7) abort();
2240     if (pix_h != 10) abort();
2241 
2242     XGetWindowAttributes (dpy, window, &xgwa);
2243     f->text_im = XCreateImage (dpy, xgwa.visual, 1, XYBitmap, 0, 0,
2244                                pix_w, pix_h, 8, 0);
2245     f->text_im->data = malloc (f->text_im->bytes_per_line * f->text_im->height);
2246 
2247     /* Convert deep image to 1 bit */
2248     for (y = 0; y < pix_h; y++)
2249       for (x = 0; x < pix_w; x++)
2250         XPutPixel (f->text_im, x, y,
2251                    (XGetPixel (mm, x, y)
2252                     ? XGetPixel (im, x, y) == black
2253                     : 0));
2254     XDestroyImage (im);
2255     XDestroyImage (mm);
2256 
2257   } else if (fontname) {
2258 
2259     font = load_font_retry (dpy, fontname);
2260     if (!font) {
2261       fprintf(stderr, "analogtv: can't load font %s\n", fontname);
2262       abort();
2263     }
2264 
2265     text_pm=XCreatePixmap(dpy, window, 256*f->char_w, f->char_h, 1);
2266 
2267     memset(&gcv, 0, sizeof(gcv));
2268     gcv.foreground=1;
2269     gcv.background=0;
2270     gcv.font=font->fid;
2271     gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
2272 # ifdef HAVE_JWXYZ
2273   jwxyz_XSetAntiAliasing (dpy, gc, False);
2274 # endif
2275 
2276     XSetForeground(dpy, gc, 0);
2277     XFillRectangle(dpy, text_pm, gc, 0, 0, 256*f->char_w, f->char_h);
2278     XSetForeground(dpy, gc, 1);
2279     for (i=0; i<256; i++) {
2280       char c=i;
2281       int x=f->char_w*i+1;
2282       int y=f->char_h*8/10;
2283       XDrawString(dpy, text_pm, gc, x, y, &c, 1);
2284     }
2285     f->text_im = XGetImage(dpy, text_pm, 0, 0, 256*f->char_w, f->char_h,
2286                            1, XYPixmap);
2287 # if 0
2288     XWriteBitmapFile(dpy, "/tmp/tvfont.xbm", text_pm,
2289                      256*f->char_w, f->char_h, -1, -1);
2290 # endif
2291     XFreeGC(dpy, gc);
2292     XFreePixmap(dpy, text_pm);
2293   } else {
2294     f->text_im = XCreateImage(dpy, xgwa.visual, 1, XYPixmap, 0, 0,
2295                               256*f->char_w, f->char_h, 8, 0);
2296     f->text_im->data = (char *)calloc(f->text_im->height,
2297                                       f->text_im->bytes_per_line);
2298 
2299   }
2300   f->x_mult=4 * ANALOGTV_SCALE;
2301   f->y_mult=2 * ANALOGTV_SCALE;
2302 }
2303 
2304 int
analogtv_font_pixel(analogtv_font * f,int c,int x,int y)2305 analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
2306 {
2307   if (x<0 || x>=f->char_w) return 0;
2308   if (y<0 || y>=f->char_h) return 0;
2309   if (c<0 || c>=256) return 0;
2310   return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
2311 }
2312 
2313 void
analogtv_font_set_pixel(analogtv_font * f,int c,int x,int y,int value)2314 analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
2315 {
2316   if (x<0 || x>=f->char_w) return;
2317   if (y<0 || y>=f->char_h) return;
2318   if (c<0 || c>=256) return;
2319 
2320   XPutPixel(f->text_im, c*f->char_w + x, y, value);
2321 }
2322 
2323 void
analogtv_font_set_char(analogtv_font * f,int c,char * s)2324 analogtv_font_set_char(analogtv_font *f, int c, char *s)
2325 {
2326   int value,x,y;
2327 
2328   if (c<0 || c>=256) return;
2329 
2330   for (y=0; y<f->char_h; y++) {
2331     for (x=0; x<f->char_w; x++) {
2332       if (!*s) return;
2333       value=(*s==' ') ? 0 : 1;
2334       analogtv_font_set_pixel(f, c, x, y, value);
2335       s++;
2336     }
2337   }
2338 }
2339 
2340 void
analogtv_lcp_to_ntsc(double luma,double chroma,double phase,int ntsc[4])2341 analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
2342 {
2343   int i;
2344   for (i=0; i<4; i++) {
2345     double w=90.0*i + phase;
2346     double val=luma + chroma * (cos(3.1415926/180.0*w));
2347     if (val<0.0) val=0.0;
2348     if (val>127.0) val=127.0;
2349     ntsc[i]=(int)val;
2350   }
2351 }
2352 
2353 void
analogtv_draw_solid(analogtv_input * input,int left,int right,int top,int bot,int ntsc[4])2354 analogtv_draw_solid(analogtv_input *input,
2355                     int left, int right, int top, int bot,
2356                     int ntsc[4])
2357 {
2358   int x,y;
2359 
2360   if (right-left<4) right=left+4;
2361   if (bot-top<1) bot=top+1;
2362 
2363   for (y=top; y<bot; y++) {
2364     for (x=left; x<right; x++) {
2365       input->signal[y][x] = ntsc[x&3];
2366     }
2367   }
2368 }
2369 
2370 
2371 void
analogtv_draw_solid_rel_lcp(analogtv_input * input,double left,double right,double top,double bot,double luma,double chroma,double phase)2372 analogtv_draw_solid_rel_lcp(analogtv_input *input,
2373                             double left, double right, double top, double bot,
2374                             double luma, double chroma, double phase)
2375 {
2376   int ntsc[4];
2377 
2378   int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top);
2379   int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot);
2380   int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left);
2381   int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right);
2382 
2383   analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
2384   analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
2385 }
2386 
2387 
2388 void
analogtv_draw_char(analogtv_input * input,analogtv_font * f,int c,int x,int y,int ntsc[4])2389 analogtv_draw_char(analogtv_input *input, analogtv_font *f,
2390                    int c, int x, int y, int ntsc[4])
2391 {
2392   int yc,xc,ys,xs,pix;
2393 
2394   for (yc=0; yc<f->char_h; yc++) {
2395     for (ys=y + yc*f->y_mult; ys<y + (yc+1)*f->y_mult; ys++) {
2396       if (ys<0 || ys>=ANALOGTV_V) continue;
2397 
2398       for (xc=0; xc<f->char_w; xc++) {
2399         pix=analogtv_font_pixel(f, c, xc, yc);
2400 
2401         for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
2402           if (xs<0 || xs>=ANALOGTV_H) continue;
2403           if (pix) {
2404             input->signal[ys][xs] = ntsc[xs&3];
2405           }
2406         }
2407       }
2408     }
2409   }
2410 }
2411 
2412 void
analogtv_draw_string(analogtv_input * input,analogtv_font * f,char * s,int x,int y,int ntsc[4])2413 analogtv_draw_string(analogtv_input *input, analogtv_font *f,
2414                      char *s, int x, int y, int ntsc[4])
2415 {
2416   while (*s) {
2417     analogtv_draw_char(input, f, *s, x, y, ntsc);
2418     x += f->char_w * f->x_mult;
2419     s++;
2420   }
2421 }
2422 
2423 void
analogtv_draw_string_centered(analogtv_input * input,analogtv_font * f,char * s,int x,int y,int ntsc[4])2424 analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
2425                               char *s, int x, int y, int ntsc[4])
2426 {
2427   int width=strlen(s) * f->char_w * f->x_mult;
2428   x -= width/2;
2429 
2430   analogtv_draw_string(input, f, s, x, y, ntsc);
2431 }
2432