From http://www.jwz.org/xscreensaver/xscreensaver-5.40.tar.gz
[xscreensaver] / hacks / analogtv.c
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
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
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
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
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
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
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 #ifdef HAVE_MOBILE
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
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
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
462 static void analogtv_thread_destroy(void *thread_raw)
463 {
464 }
465
466 analogtv *
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
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
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
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
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
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
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; i+=4) {
1006         sig[i+1] += ANALOGTV_CB_LEVEL;
1007         sig[i+3] -= ANALOGTV_CB_LEVEL;
1008       }
1009     }
1010   }
1011 }
1012
1013 static void
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; i<32; 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) {
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 && lineno<ANALOGTV_V-3) { /* 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; i<8; 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) {
1065       sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
1066       for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; 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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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; /* 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
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
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;
2301   f->y_mult=2;
2302 }
2303
2304 int
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
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
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
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
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
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
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
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 * 4;
2419     s++;
2420   }
2421 }
2422
2423 void
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 * 4;
2428   x -= width/2;
2429
2430   analogtv_draw_string(input, f, s, x, y, ntsc);
2431 }