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