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