source: trunk/third/xscreensaver/hacks/analogtv.c @ 20287

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