1 /* analogtv, Copyright (c) 2003, 2004 Trevor Blackwell <tlb@tlb.org>
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
14 This is the code for implementing something that looks like a conventional
15 analog TV set. It simulates the following characteristics of standard
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
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.
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.
36 I originally wrote it for the Apple ][ emulator, and generalized it
37 here for use with a rewrite of xteevee and possibly others.
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
51 Trevor Blackwell <tlb@tlb.org>
55 2014-04-20, Dave Odell <dmo2118@gmail.com>:
56 API change: Folded analogtv_init_signal and *_add_signal into *_draw().
58 Replaced doubles with floats, including constants and transcendental functions.
64 #else /* !HAVE_COCOA */
65 # include <X11/Xlib.h>
66 # include <X11/Xutil.h>
73 #include "resources.h"
76 #include "grabscreen.h"
81 #if defined(DEBUG) && (defined(__linux) || defined(__FreeBSD__))
82 /* only works on linux + freebsd */
83 #include <machine/cpufunc.h>
85 #define DTIME_DECL u_int64_t dtimes[100]; int n_dtimes
86 #define DTIME_START do {n_dtimes=0; dtimes[n_dtimes++]=rdtsc(); } while (0)
87 #define DTIME dtimes[n_dtimes++]=rdtsc()
88 #define DTIME_SHOW(DIV) \
90 double _dtime_div=(DIV); \
91 printf("time/%.1f: ",_dtime_div); \
92 for (i=1; i<n_dtimes; i++) \
93 printf(" %0.9f",(dtimes[i]-dtimes[i-1])* 1e-9 / _dtime_div); \
100 #define DTIME_START do { } while (0)
101 #define DTIME do { } while (0)
102 #define DTIME_SHOW(DIV) do { } while (0)
107 #define FASTRND_A 1103515245
108 #define FASTRND_C 12345
109 #define FASTRND (fastrnd = fastrnd*FASTRND_A+FASTRND_C)
111 static void analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
112 int start, int end, struct analogtv_yiq_s *it_yiq);
114 static float puramp(const analogtv *it, float tc, float start, float over)
116 float pt=it->powerup-start;
118 if (pt<0.0f) return 0.0f;
119 if (pt>900.0f || pt/tc>8.0f) return 1.0f;
121 ret=(1.0f-expf(-pt/tc))*over;
122 if (ret>1.0f) return 1.0f;
127 There are actual standards for TV signals: NTSC and RS-170A describe the
128 system used in the US and Japan. Europe has slightly different systems, but
129 not different enough to make substantially different screensaver displays.
130 Sadly, the standards bodies don't do anything so useful as publish the spec on
131 the web. Best bets are:
133 http://www.ee.washington.edu/conselec/CE/kuhn/ntsc/95x4.htm
134 http://www.ntsc-tv.com/ntsc-index-02.htm
136 In DirectColor or TrueColor modes, it generates pixel values directly from RGB
137 values it calculates across each scan line. In PseudoColor mode, it consider
138 each possible pattern of 5 preceding bit values in each possible position
139 modulo 4 and allocates a color for each. A few things, like the brightening on
140 the right side as the horizontal trace slows down, aren't done in PseudoColor.
142 I'd like to add a bit of visible retrace, but it conflicts with being able to
143 bitcopy the image when fast scrolling. After another couple of CPU
144 generations, we could probably regenerate the whole image from scratch every
145 time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling
149 /* localbyteorder is MSBFirst or LSBFirst */
150 static int localbyteorder;
151 static const double float_low8_ofs=8388608.0;
152 static int float_extraction_works;
164 unsigned int localbyteorder_loc = (MSBFirst<<24) | (LSBFirst<<0);
165 localbyteorder=*(char *)&localbyteorder_loc;
172 float_extraction_works=1;
173 for (i=0; i<256*4; i++) {
174 fe.f=float_low8_ofs+(double)i;
178 printf("Float extraction failed for %d => %d\n",i,ans);
180 float_extraction_works=0;
189 analogtv_set_defaults(analogtv *it, char *prefix)
193 sprintf(buf,"%sTVTint",prefix);
194 it->tint_control = get_float_resource(it->dpy, buf,"TVTint");
195 sprintf(buf,"%sTVColor",prefix);
196 it->color_control = get_float_resource(it->dpy, buf,"TVColor")/100.0;
197 sprintf(buf,"%sTVBrightness",prefix);
198 it->brightness_control = get_float_resource(it->dpy, buf,"TVBrightness") / 100.0;
199 sprintf(buf,"%sTVContrast",prefix);
200 it->contrast_control = get_float_resource(it->dpy, buf,"TVContrast") / 100.0;
201 it->height_control = 1.0;
202 it->width_control = 1.0;
203 it->squish_control = 0.0;
208 it->hashnoise_enable=1;
210 it->horiz_desync=frand(10.0)-5.0;
211 it->squeezebottom=frand(5.0)-1.0;
214 printf("analogtv: prefix=%s\n",prefix);
215 printf(" use: shm=%d cmap=%d color=%d\n",
216 it->use_shm,it->use_cmap,it->use_color);
217 printf(" controls: tint=%g color=%g brightness=%g contrast=%g\n",
218 it->tint_control, it->color_control, it->brightness_control,
219 it->contrast_control);
220 /* printf(" freq_error %g: %g %d\n",
221 it->freq_error, it->freq_error_inc, it->flutter_tint); */
222 printf(" desync: %g %d\n",
223 it->horiz_desync, it->flutter_horiz_desync);
224 printf(" hashnoise rpm: %g\n",
226 printf(" vis: %d %d %d\n",
227 it->visclass, it->visbits, it->visdepth);
228 printf(" shift: %d-%d %d-%d %d-%d\n",
229 it->red_invprec,it->red_shift,
230 it->green_invprec,it->green_shift,
231 it->blue_invprec,it->blue_shift);
232 printf(" size: %d %d %d %d xrepl=%d\n",
233 it->usewidth, it->useheight,
234 it->screen_xo, it->screen_yo, it->xrepl);
236 printf(" ANALOGTV_V=%d\n",ANALOGTV_V);
237 printf(" ANALOGTV_TOP=%d\n",ANALOGTV_TOP);
238 printf(" ANALOGTV_VISLINES=%d\n",ANALOGTV_VISLINES);
239 printf(" ANALOGTV_BOT=%d\n",ANALOGTV_BOT);
240 printf(" ANALOGTV_H=%d\n",ANALOGTV_H);
241 printf(" ANALOGTV_SYNC_START=%d\n",ANALOGTV_SYNC_START);
242 printf(" ANALOGTV_BP_START=%d\n",ANALOGTV_BP_START);
243 printf(" ANALOGTV_CB_START=%d\n",ANALOGTV_CB_START);
244 printf(" ANALOGTV_PIC_START=%d\n",ANALOGTV_PIC_START);
245 printf(" ANALOGTV_PIC_LEN=%d\n",ANALOGTV_PIC_LEN);
246 printf(" ANALOGTV_FP_START=%d\n",ANALOGTV_FP_START);
247 printf(" ANALOGTV_PIC_END=%d\n",ANALOGTV_PIC_END);
248 printf(" ANALOGTV_HASHNOISE_LEN=%d\n",ANALOGTV_HASHNOISE_LEN);
254 extern Bool mono_p; /* shoot me */
257 analogtv_free_image(analogtv *it)
261 #ifdef HAVE_XSHM_EXTENSION
262 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
265 thread_free(it->image->data);
266 it->image->data = NULL;
267 XDestroyImage(it->image);
274 analogtv_alloc_image(analogtv *it)
276 /* On failure, it->image is NULL. */
278 unsigned bits_per_pixel = get_bits_per_pixel(it->dpy, it->xgwa.depth);
279 unsigned align = thread_memory_alignment(it->dpy) * 8 - 1;
280 /* Width is in bits. */
281 unsigned width = (it->usewidth * bits_per_pixel + align) & ~align;
284 #ifdef HAVE_XSHM_EXTENSION
285 it->image=create_xshm_image(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0,
287 width / bits_per_pixel, it->useheight);
289 if (!it->image) it->use_shm=0;
292 it->image = XCreateImage(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0, 0,
293 it->usewidth, it->useheight, 8, width / 8);
295 if(thread_malloc((void **)&it->image->data, it->dpy,
296 it->image->height * it->image->bytes_per_line)) {
297 it->image->data = NULL;
298 XDestroyImage(it->image);
305 memset (it->image->data, 0, it->image->height * it->image->bytes_per_line);
307 /* Not enough memory. Maybe try a smaller window. */
308 fprintf(stderr, "analogtv: %s\n", strerror(ENOMEM));
314 analogtv_configure(analogtv *it)
316 int oldwidth=it->usewidth;
317 int oldheight=it->useheight;
318 int wlim,hlim,height_diff;
320 /* If the window is very small, don't let the image we draw get lower
321 than the actual TV resolution (266x200.)
323 If the aspect ratio of the window is close to a 4:3 or 16:9 ratio,
324 then scale the image to exactly fill the window.
326 Otherwise, center the image either horizontally or vertically,
327 letterboxing or pillarboxing (but not both).
329 If it's very close (2.5%) to a multiple of VISLINES, make it exact
330 For example, it maps 1024 => 1000.
332 float percent = 0.15;
333 float min_ratio = 4.0 / 3.0 * (1 - percent);
334 float max_ratio = 16.0 / 9.0 * (1 + percent);
336 float height_snap=0.025;
338 hlim = it->xgwa.height;
339 wlim = it->xgwa.width;
340 ratio = wlim / (float) hlim;
343 /* Fill the whole iPhone screen, even though that distorts the image. */
348 if (wlim < 266 || hlim < 200)
354 "size: minimal: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
355 wlim, hlim, it->xgwa.width, it->xgwa.height,
356 min_ratio, ratio, max_ratio);
359 else if (ratio > min_ratio && ratio < max_ratio)
363 "size: close enough: %dx%d (%.3f < %.3f < %.3f)\n",
364 wlim, hlim, min_ratio, ratio, max_ratio);
367 else if (ratio >= max_ratio)
369 wlim = hlim*max_ratio;
372 "size: center H: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
373 wlim, hlim, it->xgwa.width, it->xgwa.height,
374 min_ratio, ratio, max_ratio);
377 else /* ratio <= min_ratio */
379 hlim = wlim/min_ratio;
382 "size: center V: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
383 wlim, hlim, it->xgwa.width, it->xgwa.height,
384 min_ratio, ratio, max_ratio);
389 height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
390 if (height_diff != 0 && fabs(height_diff) < hlim * height_snap)
396 /* Most times this doesn't change */
397 if (wlim != oldwidth || hlim != oldheight) {
402 it->xrepl=1+it->usewidth/640;
403 if (it->xrepl>2) it->xrepl=2;
404 it->subwidth=it->usewidth/it->xrepl;
406 analogtv_free_image(it);
407 analogtv_alloc_image(it);
410 it->screen_xo = (it->xgwa.width-it->usewidth)/2;
411 it->screen_yo = (it->xgwa.height-it->useheight)/2;
416 analogtv_reconfigure(analogtv *it)
418 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
419 analogtv_configure(it);
422 /* Can be any power-of-two <= 32. 16 a slightly better choice for 2-3 threads. */
423 #define ANALOGTV_SUBTOTAL_LEN 32
425 typedef struct analogtv_thread_s
429 size_t signal_start, signal_end;
432 #define SIGNAL_OFFSET(thread_id) \
433 ((ANALOGTV_SIGNAL_LEN * (thread_id) / threads->count) & align)
435 static int analogtv_thread_create(void *thread_raw, struct threadpool *threads,
438 analogtv_thread *thread = (analogtv_thread *)thread_raw;
441 thread->it = GET_PARENT_OBJ(analogtv, threads, threads);
442 thread->thread_id = thread_id;
444 align = thread_memory_alignment(thread->it->dpy) /
445 sizeof(thread->it->signal_subtotals[0]);
448 align = ~(align * ANALOGTV_SUBTOTAL_LEN - 1);
450 thread->signal_start = SIGNAL_OFFSET(thread_id);
451 thread->signal_end = thread_id + 1 == threads->count ?
452 ANALOGTV_SIGNAL_LEN :
453 SIGNAL_OFFSET(thread_id + 1);
458 static void analogtv_thread_destroy(void *thread_raw)
463 analogtv_allocate(Display *dpy, Window window)
465 static const struct threadpool_class cls = {
466 sizeof(analogtv_thread),
467 analogtv_thread_create,
468 analogtv_thread_destroy
474 const size_t rx_signal_len = ANALOGTV_SIGNAL_LEN + 2*ANALOGTV_H;
478 it=(analogtv *)calloc(1,sizeof(analogtv));
482 it->signal_subtotals=NULL;
487 if (thread_malloc((void **)&it->rx_signal, dpy,
488 sizeof(it->rx_signal[0]) * rx_signal_len))
491 assert(!(ANALOGTV_SIGNAL_LEN % ANALOGTV_SUBTOTAL_LEN));
492 if (thread_malloc((void **)&it->signal_subtotals, dpy,
493 sizeof(it->signal_subtotals[0]) *
494 (rx_signal_len / ANALOGTV_SUBTOTAL_LEN)))
497 if (threadpool_create(&it->threads, &cls, dpy, hardware_concurrency(dpy)))
500 assert(it->threads.count);
506 #ifdef HAVE_XSHM_EXTENSION
512 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
514 it->screen=it->xgwa.screen;
515 it->colormap=it->xgwa.colormap;
516 it->visclass=it->xgwa.visual->class;
517 it->visbits=it->xgwa.visual->bits_per_rgb;
518 it->visdepth=it->xgwa.depth;
519 if (it->visclass == TrueColor || it->visclass == DirectColor) {
520 if (get_integer_resource (it->dpy, "use_cmap", "Integer")) {
525 it->use_color=!mono_p;
527 else if (it->visclass == PseudoColor || it->visclass == StaticColor) {
529 it->use_color=!mono_p;
536 it->red_mask=it->xgwa.visual->red_mask;
537 it->green_mask=it->xgwa.visual->green_mask;
538 it->blue_mask=it->xgwa.visual->blue_mask;
539 it->red_shift=it->red_invprec=-1;
540 it->green_shift=it->green_invprec=-1;
541 it->blue_shift=it->blue_invprec=-1;
543 /* Is there a standard way to do this? Does this handle all cases? */
545 for (shift=0; shift<32; shift++) {
546 for (prec=1; prec<16 && prec<40-shift; prec++) {
547 unsigned long mask=(0xffffUL>>(16-prec)) << shift;
548 if (it->red_shift<0 && mask==it->red_mask)
549 it->red_shift=shift, it->red_invprec=16-prec;
550 if (it->green_shift<0 && mask==it->green_mask)
551 it->green_shift=shift, it->green_invprec=16-prec;
552 if (it->blue_shift<0 && mask==it->blue_mask)
553 it->blue_shift=shift, it->blue_invprec=16-prec;
556 if (it->red_shift<0 || it->green_shift<0 || it->blue_shift<0) {
557 if (0) fprintf(stderr,"Can't figure out color space\n");
561 for (i=0; i<ANALOGTV_CV_MAX; i++) {
562 int intensity=pow(i/256.0, 0.8)*65535.0; /* gamma correction */
563 if (intensity>65535) intensity=65535;
564 it->red_values[i]=((intensity>>it->red_invprec)<<it->red_shift);
565 it->green_values[i]=((intensity>>it->green_invprec)<<it->green_shift);
566 it->blue_values[i]=((intensity>>it->blue_invprec)<<it->blue_shift);
571 gcv.background=get_pixel_resource(it->dpy, it->colormap,
572 "background", "Background");
574 it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
575 XSetWindowBackground(it->dpy, it->window, gcv.background);
576 XClearWindow(dpy,window);
578 analogtv_configure(it);
584 if(it->threads.count)
585 threadpool_destroy(&it->threads);
586 thread_free(it->signal_subtotals);
587 thread_free(it->rx_signal);
594 analogtv_release(analogtv *it)
598 #ifdef HAVE_XSHM_EXTENSION
599 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
602 thread_free(it->image->data);
603 it->image->data = NULL;
604 XDestroyImage(it->image);
608 if (it->gc) XFreeGC(it->dpy, it->gc);
610 if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
612 threadpool_destroy(&it->threads);
613 thread_free(it->rx_signal);
614 thread_free(it->signal_subtotals);
620 First generate the I and Q reference signals, which we'll multiply
621 the input signal by to accomplish the demodulation. Normally they
622 are shifted 33 degrees from the colorburst. I think this was convenient
623 for inductor-capacitor-vacuum tube implementation.
625 The tint control, FWIW, just adds a phase shift to the chroma signal,
626 and the color control controls the amplitude.
628 In text modes (colormode==0) the system disabled the color burst, and no
629 color was detected by the monitor.
631 freq_error gives a mismatch between the built-in oscillator and the
632 TV's colorbust. Some II Plus machines seemed to occasionally get
633 instability problems -- the crystal oscillator was a single
634 transistor if I remember correctly -- and the frequency would vary
635 enough that the tint would change across the width of the screen.
636 The left side would be in correct tint because it had just gotten
637 resynchronized with the color burst.
639 If we're using a colormap, set it up.
642 analogtv_set_demod(analogtv *it)
644 int y_levels=10,i_levels=5,q_levels=5;
647 In principle, we might be able to figure out how to adjust the
648 color map frame-by-frame to get some nice color bummage. But I'm
649 terrified of changing the color map because we'll get flashing.
651 I can hardly believe we still have to deal with colormaps. They're
652 like having NEAR PTRs: an enormous hassle for the programmer just
653 to save on memory. They should have been deprecated by 1995 or
657 if (it->use_cmap && !it->n_colors) {
660 XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
666 for (yli=0; yli<y_levels; yli++) {
667 for (ili=0; ili<i_levels; ili++) {
668 for (qli=0; qli<q_levels; qli++) {
669 double interpy,interpi,interpq;
670 double levelmult=700.0;
674 interpy=100.0 * ((double)yli/y_levels);
675 interpi=50.0 * (((double)ili-(0.5*i_levels))/(double)i_levels);
676 interpq=50.0 * (((double)qli-(0.5*q_levels))/(double)q_levels);
678 r=(int)((interpy + 1.04*interpi + 0.624*interpq)*levelmult);
679 g=(int)((interpy - 0.276*interpi - 0.639*interpq)*levelmult);
680 b=(int)((interpy - 1.105*interpi + 1.729*interpq)*levelmult);
682 if (r>65535) r=65535;
684 if (g>65535) g=65535;
686 if (b>65535) b=65535;
689 printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n",
690 interpy, interpi, interpq,
698 if (!XAllocColor(it->dpy, it->colormap, &col)) {
699 if (q_levels > y_levels*4/12)
701 else if (i_levels > y_levels*5/12)
710 it->colors[it->n_colors++]=col.pixel;
715 it->cmap_y_levels=y_levels;
716 it->cmap_i_levels=i_levels;
717 it->cmap_q_levels=q_levels;
726 analogtv_line_signature(analogtv_input *input, int lineno)
729 char *origsignal=&input->signal[(lineno+input->vsync)
730 %ANALOGTV_V][input->line_hsync[lineno]];
734 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
736 hash = hash + (hash<<17) + c;
739 hash += input->line_hsync[lineno];
742 hash += input->hashnoise_times[lineno];
751 /* Here we model the analog circuitry of an NTSC television.
752 Basically, it splits the signal into 3 signals: Y, I and Q. Y
753 corresponds to luminance, and you get it by low-pass filtering the
754 input signal to below 3.57 MHz.
756 I and Q are the in-phase and quadrature components of the 3.57 MHz
757 subcarrier. We get them by multiplying by cos(3.57 MHz*t) and
758 sin(3.57 MHz*t), and low-pass filtering. Because the eye has less
759 resolution in some colors than others, the I component gets
760 low-pass filtered at 1.5 MHz and the Q at 0.5 MHz. The I component
761 is approximately orange-blue, and Q is roughly purple-green. See
762 http://www.ntsc-tv.com for details.
764 We actually do an awful lot to the signal here. I suspect it would
765 make sense to wrap them all up together by calculating impulse
766 response and doing FFT convolutions.
771 analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
772 int start, int end, struct analogtv_yiq_s *it_yiq)
777 int phasecorr=(signal-it->rx_signal)&3;
778 struct analogtv_yiq_s *yiq;
780 float agclevel=it->agclevel;
781 float brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
782 float delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
787 double cb_i=(it->line_cb_phase[lineno][(2+phasecorr)&3]-
788 it->line_cb_phase[lineno][(0+phasecorr)&3])/16.0;
789 double cb_q=(it->line_cb_phase[lineno][(3+phasecorr)&3]-
790 it->line_cb_phase[lineno][(1+phasecorr)&3])/16.0;
792 colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
795 double tint_i = -cos((103 + it->color_control)*3.1415926/180);
796 double tint_q = sin((103 + it->color_control)*3.1415926/180);
798 multiq2[0] = (cb_i*tint_i - cb_q*tint_q) * it->color_control;
799 multiq2[1] = (cb_q*tint_i + cb_i*tint_q) * it->color_control;
800 multiq2[2]=-multiq2[0];
801 multiq2[3]=-multiq2[1];
807 printf("multiq = [%0.3f %0.3f %0.3f %0.3f] ",
808 it->multiq[60],it->multiq[61],it->multiq[62],it->multiq[63]);
809 printf("it->line_cb_phase = [%0.3f %0.3f %0.3f %0.3f]\n",
810 it->line_cb_phase[lineno][0],it->line_cb_phase[lineno][1],
811 it->line_cb_phase[lineno][2],it->line_cb_phase[lineno][3]);
812 printf("multiq2 = [%0.3f %0.3f %0.3f %0.3f]\n",
813 multiq2[0],multiq2[1],multiq2[2],multiq2[3]);
817 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
818 for (i=0; i<5; i++) dp[i]=0.0f;
821 assert(end < ANALOGTV_PIC_LEN+10);
823 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
824 for (i=0; i<24; i++) dp[i]=0.0;
825 for (i=start, yiq=it_yiq+start, sp=signal+start;
827 i++, dp--, yiq++, sp++) {
829 /* Now filter them. These are infinite impulse response filters
830 calculated by the script at
831 http://www-users.cs.york.ac.uk/~fisher/mkfilter. This is
832 fixed-point integer DSP, son. No place for wimps. We do it in
833 integer because you can count on integer being faster on most
834 CPUs. We care about speed because we need to recalculate every
835 time we blink text, and when we spew random bytes into screen
836 memory. This is roughly 16.16 fixed point arithmetic, but we
837 scale some filter values up by a few bits to avoid some nasty
840 /* Filter Y with a 4-pole low-pass Butterworth filter at 3.5 MHz
841 with an extra zero at 3.5 MHz, from
842 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
845 dp[0] = sp[0] * 0.0469904257251935f * agclevel;
846 dp[8] = (+1.0f*(dp[6]+dp[0])
852 yiq->y = dp[8] + brightadd;
856 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
857 for (i=0; i<27; i++) dp[i]=0.0;
859 for (i=start, yiq=it_yiq+start, sp=signal+start;
861 i++, dp--, yiq++, sp++) {
864 /* Filter I and Q with a 3-pole low-pass Butterworth filter at
865 1.5 MHz with an extra zero at 3.5 MHz, from
866 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 -Z 2.5000000000e-01 -l
870 dp[0] = sig*multiq2[i&3] * 0.0833333333333f;
871 yiq->i=dp[8] = (dp[5] + dp[0]
872 +3.0f*(dp[4] + dp[1])
873 +4.0f*(dp[3] + dp[2])
874 -0.3333333333f * dp[10]);
876 dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333f;
877 yiq->q=dp[24] = (dp[16+5] + dp[16+0]
878 +3.0f*(dp[16+4] + dp[16+1])
879 +4.0f*(dp[16+3] + dp[16+2])
880 -0.3333333333f * dp[24+2]);
883 for (i=start, yiq=it_yiq+start; i<end; i++, yiq++) {
884 yiq->i = yiq->q = 0.0f;
890 analogtv_setup_teletext(analogtv_input *input)
893 int teletext=ANALOGTV_BLACK_LEVEL;
895 /* Teletext goes in line 21. But I suspect there are other things
896 in the vertical retrace interval */
898 for (y=19; y<22; y++) {
899 for (x=ANALOGTV_PIC_START; x<ANALOGTV_PIC_END; x++) {
901 teletext=(random()&1) ? ANALOGTV_WHITE_LEVEL : ANALOGTV_BLACK_LEVEL;
903 input->signal[y][x]=teletext;
909 analogtv_setup_frame(analogtv *it)
915 if (it->flutter_horiz_desync) {
916 /* Horizontal sync during vertical sync instability. */
917 it->horiz_desync += -0.10*(it->horiz_desync-3.0) +
918 ((int)(random()&0xff)-0x80) *
919 ((int)(random()&0xff)-0x80) *
920 ((int)(random()&0xff)-0x80) * 0.000001;
923 for (i=0; i<ANALOGTV_V; i++) {
924 it->hashnoise_times[i]=0;
927 if (it->hashnoise_enable && !it->hashnoise_on) {
928 if (random()%10000==0) {
930 it->shrinkpulse=random()%ANALOGTV_V;
933 if (random()%1000==0) {
936 if (it->hashnoise_on) {
937 it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
938 ((int)(random()%2000)-1000)*0.1;
940 it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
941 if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
943 if (it->hashnoise_rpm > 0.0) {
946 int hnc=it->hashnoise_counter; /* in 24.8 format */
948 /* Convert rpm of a 16-pole motor into dots in 24.8 format */
949 hni_double = ANALOGTV_V * ANALOGTV_H * 256.0 /
950 (it->hashnoise_rpm * 16.0 / 60.0 / 60.0);
951 hni = (hni_double <= INT_MAX) ? (int)hni_double : INT_MAX;
953 while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
954 y=(hnc>>8)/ANALOGTV_H;
955 x=(hnc>>8)%ANALOGTV_H;
957 if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
958 it->hashnoise_times[y]=x;
960 /* hnc += hni + (int)(random()%65536)-32768; */
962 hnc += (int)(random()%65536)-32768;
963 if ((hnc >= 0) && (INT_MAX - hnc < hni)) break;
967 /* hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;*/
970 if (it->rx_signal_level != 0.0)
971 it->agclevel = 1.0/it->rx_signal_level;
976 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
977 printf(" %0.3f",it->ghostfir[i]);
979 printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel);
984 analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
989 int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
991 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
992 vsync=lineno>=3 && lineno<7;
994 sig=input->signal[lineno];
996 i=ANALOGTV_SYNC_START;
998 while (i<ANALOGTV_BP_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
999 while (i<ANALOGTV_H) sig[i++]=synclevel;
1001 while (i<ANALOGTV_BP_START) sig[i++]=synclevel;
1002 while (i<ANALOGTV_PIC_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
1003 while (i<ANALOGTV_FP_START) sig[i++]=ANALOGTV_BLACK_LEVEL;
1005 while (i<ANALOGTV_H) sig[i++]=ANALOGTV_BLANK_LEVEL;
1008 /* 9 cycles of colorburst */
1009 for (i=ANALOGTV_CB_START; i<ANALOGTV_CB_START+36; i+=4) {
1010 sig[i+1] += ANALOGTV_CB_LEVEL;
1011 sig[i+3] -= ANALOGTV_CB_LEVEL;
1018 analogtv_sync(analogtv *it)
1020 int cur_hsync=it->cur_hsync;
1021 int cur_vsync=it->cur_vsync;
1026 float cbfc=1.0f/128.0f;
1028 /* sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;*/
1029 for (i=-32; i<32; i++) {
1030 lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1031 sp = it->rx_signal + lineno*ANALOGTV_H;
1033 for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/16) {
1036 filt *= it->agclevel;
1038 osc = (float)(ANALOGTV_V+i)/(float)ANALOGTV_V;
1040 if (osc >= 1.05f+0.0002f * filt) break;
1042 cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1044 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
1046 if (lineno>5 && lineno<ANALOGTV_V-3) { /* ignore vsync interval */
1047 unsigned lineno2 = (lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V;
1048 if (!lineno2) lineno2 = ANALOGTV_V;
1049 sp = it->rx_signal + lineno2*ANALOGTV_H + cur_hsync;
1050 for (i=-8; i<8; i++) {
1051 osc = (float)(ANALOGTV_H+i)/(float)ANALOGTV_H;
1052 filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
1054 if (osc >= 1.005f + 0.0001f*filt) break;
1056 cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
1059 it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START +
1060 ANALOGTV_H) % ANALOGTV_H;
1062 /* Now look for the colorburst, which is a few cycles after the H
1063 sync pulse, and store its phase.
1064 The colorburst is 9 cycles long, and we look at the middle 5
1069 sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
1070 for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; i++) {
1071 it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0f-cbfc) +
1072 sp[i]*it->agclevel*cbfc;
1080 for (i=0; i<4; i++) {
1081 tot += it->cb_phase[i]*it->cb_phase[i];
1083 cbgain = 32.0f/sqrtf(tot);
1085 for (i=0; i<4; i++) {
1086 it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
1091 if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n",
1093 it->cb_phase[0], it->cb_phase[1],
1094 it->cb_phase[2], it->cb_phase[3]);
1097 /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */
1100 it->cur_hsync = cur_hsync;
1101 it->cur_vsync = cur_vsync;
1105 analogtv_levelmult(const analogtv *it, int level)
1107 static const double levelfac[3]={-7.5, 5.5, 24.5};
1108 return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
1112 analogtv_level(const analogtv *it, int y, int ytop, int ybot)
1116 if (y==ytop || y==ybot-1) level=0;
1117 else if (y==ytop+1 || y==ybot-2) level=1;
1120 else if (ybot-ytop>=5) {
1121 if (y==ytop || y==ybot-1) level=0;
1124 else if (ybot-ytop>=3) {
1125 if (y==ytop) level=0;
1135 The point of this stuff is to ensure that when useheight is not a
1136 multiple of VISLINES so that TV scan lines map to different numbers
1137 of vertical screen pixels, the total brightness of each scan line
1139 ANALOGTV_MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
1140 it interpolates extra black lines.
1144 analogtv_setup_levels(analogtv *it, double avgheight)
1147 static const double levelfac[3]={-7.5, 5.5, 24.5};
1149 for (height=0; height<avgheight+2.0 && height<=ANALOGTV_MAX_LINEHEIGHT; height++) {
1151 for (i=0; i<height; i++) {
1152 it->leveltable[height][i].index = 2;
1156 it->leveltable[height][0].index=0;
1159 if (height >= 1) it->leveltable[height][height-1].index=0;
1162 it->leveltable[height][1].index=1;
1163 if (height >= 2) it->leveltable[height][height-2].index=1;
1166 for (i=0; i<height; i++) {
1167 it->leveltable[height][i].value =
1168 (40.0 + levelfac[it->leveltable[height][i].index]*puramp(it, 3.0, 6.0, 1.0)) / 256.0;
1174 static void rnd_combine(unsigned *a0, unsigned *c0, unsigned a1, unsigned c1)
1176 *a0 = (*a0 * a1) & 0xffffffffu;
1177 *c0 = (c1 + a1 * *c0) & 0xffffffffu;
1180 static void rnd_seek_ac(unsigned *a, unsigned *c, unsigned dist)
1182 unsigned int a1 = *a, c1 = *c;
1188 rnd_combine(a, c, a1, c1);
1190 rnd_combine(&a1, &c1, a1, c1);
1194 static unsigned int rnd_seek(unsigned a, unsigned c, unsigned rnd, unsigned dist)
1196 rnd_seek_ac(&a, &c, dist);
1200 static void analogtv_init_signal(const analogtv *it, double noiselevel, unsigned start, unsigned end)
1202 float *ps=it->rx_signal + start;
1203 float *pe=it->rx_signal + end;
1205 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random0, start);
1206 unsigned int fastrnd_offset;
1208 float noisemul = sqrt(noiselevel*150)/(float)0x7fffffff;
1210 fastrnd_offset = fastrnd - 0x7fffffff;
1211 nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
1214 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1215 fastrnd_offset = fastrnd - 0x7fffffff;
1216 nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
1221 static void analogtv_add_signal(const analogtv *it, const analogtv_reception *rec, unsigned start, unsigned end, int ec)
1223 analogtv_input *inp=rec->input;
1224 float *ps=it->rx_signal + start;
1225 float *pe=it->rx_signal + end;
1227 signed char *ss=&inp->signal[0][0];
1228 signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
1229 signed char *s=ss + ((start + (unsigned)rec->ofs) % ANALOGTV_SIGNAL_LEN);
1232 float level=rec->level;
1233 float hfloss=rec->hfloss;
1234 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random1, start);
1237 const float noise_decay = 0.99995f;
1238 float noise_ampl = 1.3f * powf(noise_decay, start);
1243 /* assert((se-ss)%4==0 && (se-s)%4==0); */
1245 for (i = start; i < ec; i++) { /* Sometimes start > ec. */
1247 /* Do a big noisy transition. We can make the transition noise of
1248 high constant strength regardless of signal strength.
1250 There are two separate state machines. here, One is the noise
1251 process and the other is the
1253 We don't bother with the FIR filter here
1256 float sig0=(float)s[0];
1257 unsigned int fastrnd_offset = fastrnd - 0x7fffffff;
1258 float noise = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * (50.0f/(float)0x7fffffff);
1259 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1261 p[0] += sig0 * level * (1.0f - noise_ampl) + noise * noise_ampl;
1263 noise_ampl *= noise_decay;
1272 for (i=1; i<5; i++) {
1275 s2 += ANALOGTV_SIGNAL_LEN;
1276 dp[i] = (float)((int)s2[0]+(int)s2[1]+(int)s2[2]+(int)s2[3]);
1280 assert(!((pe - p) % 4));
1282 float sig0,sig1,sig2,sig3,sigr;
1289 dp[0]=sig0+sig1+sig2+sig3;
1291 /* Get the video out signal, and add some ghosting, typical of RF
1292 monitor cables. This corresponds to a pretty long cable, but
1296 sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] +
1297 dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]);
1298 dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0];
1300 p[0] += (sig0+sigr + sig2*hfloss) * level;
1301 p[1] += (sig1+sigr + sig3*hfloss) * level;
1302 p[2] += (sig2+sigr + sig0*hfloss) * level;
1303 p[3] += (sig3+sigr + sig1*hfloss) * level;
1307 if (s>=se) s = ss + (s-se);
1313 static void analogtv_thread_add_signals(void *thread_raw)
1315 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1316 const analogtv *it = thread->it;
1318 unsigned subtotal_end;
1320 unsigned start = thread->signal_start;
1321 while(start != thread->signal_end)
1325 /* Work on 8 KB blocks; these should fit in L1. */
1326 /* (Though it doesn't seem to help much on my system.) */
1327 unsigned end = start + 2048;
1328 if(end > thread->signal_end)
1329 end = thread->signal_end;
1331 analogtv_init_signal (it, it->noiselevel, start, end);
1333 for (i = 0; i != it->rec_count; ++i) {
1334 analogtv_add_signal (it, it->recs[i], start, end,
1335 !i ? it->channel_change_cycles : 0);
1338 assert (!(start % ANALOGTV_SUBTOTAL_LEN));
1339 assert (!(end % ANALOGTV_SUBTOTAL_LEN));
1341 p = it->rx_signal + start;
1342 subtotal_end = end / ANALOGTV_SUBTOTAL_LEN;
1343 for (i = start / ANALOGTV_SUBTOTAL_LEN; i != subtotal_end; ++i) {
1345 for (j = 1; j != ANALOGTV_SUBTOTAL_LEN; ++j)
1347 it->signal_subtotals[i] = sum;
1348 p += ANALOGTV_SUBTOTAL_LEN;
1355 static int analogtv_get_line(const analogtv *it, int lineno, int *slineno,
1356 int *ytop, int *ybot, unsigned *signal_offset)
1358 *slineno=lineno-ANALOGTV_TOP;
1359 *ytop=(int)((*slineno*it->useheight/ANALOGTV_VISLINES -
1360 it->useheight/2)*it->puheight) + it->useheight/2;
1361 *ybot=(int)(((*slineno+1)*it->useheight/ANALOGTV_VISLINES -
1362 it->useheight/2)*it->puheight) + it->useheight/2;
1364 int linesig=analogtv_line_signature(input,lineno)
1365 + it->hashnoise_times[lineno];
1367 *signal_offset = ((lineno+it->cur_vsync+ANALOGTV_V) % ANALOGTV_V) * ANALOGTV_H +
1368 it->line_hsync[lineno];
1370 if (*ytop==*ybot) return 0;
1371 if (*ybot<0 || *ytop>it->useheight) return 0;
1372 if (*ytop<0) *ytop=0;
1373 if (*ybot>it->useheight) *ybot=it->useheight;
1375 if (*ybot > *ytop+ANALOGTV_MAX_LINEHEIGHT) *ybot=*ytop+ANALOGTV_MAX_LINEHEIGHT;
1380 analogtv_blast_imagerow(const analogtv *it,
1381 float *rgbf, float *rgbf_end,
1386 char *level_copyfrom[3];
1387 int xrepl=it->xrepl;
1388 unsigned lineheight = ybot - ytop;
1389 if (lineheight > ANALOGTV_MAX_LINEHEIGHT) lineheight = ANALOGTV_MAX_LINEHEIGHT;
1390 for (i=0; i<3; i++) level_copyfrom[i]=NULL;
1392 for (y=ytop; y<ybot; y++) {
1393 char *rowdata=it->image->data + y*it->image->bytes_per_line;
1394 unsigned line = y-ytop;
1396 int level=it->leveltable[lineheight][line].index;
1397 float levelmult=it->leveltable[lineheight][line].value;
1399 /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
1400 why standard graphics sw has to be fast, or else people will have to
1401 work around it and risk incompatibility. The quickdraw folks
1402 understood this. The other answer would be for X11 to have fewer
1403 formats for bitm.. oh, never mind. If neither of these cases work
1404 (they probably cover 99% of setups) it falls back on the Xlib
1407 if (level_copyfrom[level]) {
1408 memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line);
1411 level_copyfrom[level] = rowdata;
1415 else if (it->image->format==ZPixmap &&
1416 it->image->bits_per_pixel==32 &&
1417 sizeof(unsigned int)==4 &&
1418 it->image->byte_order==localbyteorder) {
1419 /* int is more likely to be 32 bits than long */
1420 unsigned int *pixelptr=(unsigned int *)rowdata;
1423 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1424 int ntscri=rpf[0]*levelmult;
1425 int ntscgi=rpf[1]*levelmult;
1426 int ntscbi=rpf[2]*levelmult;
1427 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1428 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1429 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1430 pix = (it->red_values[ntscri] |
1431 it->green_values[ntscgi] |
1432 it->blue_values[ntscbi]);
1436 if (xrepl>=3) pixelptr[2] = pix;
1441 else if (it->image->format==ZPixmap &&
1442 it->image->bits_per_pixel==16 &&
1443 sizeof(unsigned short)==2 &&
1444 float_extraction_works &&
1445 it->image->byte_order==localbyteorder) {
1446 unsigned short *pixelptr=(unsigned short *)rowdata;
1448 float_extract_t r1,g1,b1;
1451 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1452 r2=rpf[0]; g2=rpf[1]; b2=rpf[2];
1453 r1.f=r2 * levelmult+float_low8_ofs;
1454 g1.f=g2 * levelmult+float_low8_ofs;
1455 b1.f=b2 * levelmult+float_low8_ofs;
1456 pix = (it->red_values[r1.i & 0x3ff] |
1457 it->green_values[g1.i & 0x3ff] |
1458 it->blue_values[b1.i & 0x3ff]);
1462 if (xrepl>=3) pixelptr[2] = pix;
1467 else if (it->image->format==ZPixmap &&
1468 it->image->bits_per_pixel==16 &&
1469 sizeof(unsigned short)==2 &&
1470 it->image->byte_order==localbyteorder) {
1471 unsigned short *pixelptr=(unsigned short *)rowdata;
1474 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1475 int r1=rpf[0] * levelmult;
1476 int g1=rpf[1] * levelmult;
1477 int b1=rpf[2] * levelmult;
1478 if (r1>=ANALOGTV_CV_MAX) r1=ANALOGTV_CV_MAX-1;
1479 if (g1>=ANALOGTV_CV_MAX) g1=ANALOGTV_CV_MAX-1;
1480 if (b1>=ANALOGTV_CV_MAX) b1=ANALOGTV_CV_MAX-1;
1481 pix = it->red_values[r1] | it->green_values[g1] | it->blue_values[b1];
1485 if (xrepl>=3) pixelptr[2] = pix;
1491 for (x=0, rpf=rgbf; rpf!=rgbf_end ; x++, rpf+=3) {
1492 int ntscri=rpf[0]*levelmult;
1493 int ntscgi=rpf[1]*levelmult;
1494 int ntscbi=rpf[2]*levelmult;
1495 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1496 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1497 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1498 for (j=0; j<xrepl; j++) {
1499 XPutPixel(it->image, x*xrepl + j, y,
1500 it->red_values[ntscri] | it->green_values[ntscgi] |
1501 it->blue_values[ntscbi]);
1509 static void analogtv_thread_draw_lines(void *thread_raw)
1511 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1512 const analogtv *it = thread->it;
1516 float *raw_rgb_start;
1518 raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
1520 if (! raw_rgb_start) return;
1522 raw_rgb_end=raw_rgb_start+3*it->subwidth;
1524 for (lineno=ANALOGTV_TOP + thread->thread_id;
1525 lineno<ANALOGTV_BOT;
1526 lineno += it->threads.count) {
1529 int slineno, ytop, ybot;
1530 unsigned signal_offset;
1532 const float *signal;
1534 int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
1535 float *rgb_start, *rgb_end;
1541 struct analogtv_yiq_s yiq[ANALOGTV_PIC_LEN+10];
1543 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot,
1547 signal = it->rx_signal + signal_offset;
1551 float bloomthisrow,shiftthisrow;
1552 float viswidth,middle;
1556 bloomthisrow = -10.0f * it->crtload[lineno];
1557 if (bloomthisrow<-10.0f) bloomthisrow=-10.0f;
1558 if (bloomthisrow>2.0f) bloomthisrow=2.0f;
1560 shiftthisrow=it->horiz_desync * (expf(-0.17f*slineno) *
1561 (0.7f+cosf(slineno*0.6f)));
1566 viswidth=ANALOGTV_PIC_LEN * 0.79f - 5.0f*bloomthisrow;
1567 middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
1569 scanwidth=it->width_control * puramp(it, 0.5f, 0.3f, 1.0f);
1571 scw=it->subwidth*scanwidth;
1572 if (scw>it->subwidth) scw=it->usewidth;
1573 scl=it->subwidth/2 - scw/2;
1574 scr=it->subwidth/2 + scw/2;
1576 pixrate=(int)((viswidth*65536.0f*1.0f)/it->subwidth)/scanwidth;
1577 scanstart_i=(int)((middle-viswidth*0.5f)*65536.0f);
1578 scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
1579 squishright_i=(int)((middle+viswidth*(0.25f + 0.25f*puramp(it, 2.0f, 0.0f, 1.1f)
1580 - it->squish_control)) *65536.0f);
1581 squishdiv=it->subwidth/15;
1583 rgb_start=raw_rgb_start+scl*3;
1584 rgb_end=raw_rgb_start+scr*3;
1586 assert(scanstart_i>=0);
1589 if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
1591 scanstart_i/65536.0f,
1592 squishright_i/65536.0f,
1599 for (y=ytop; y<ybot; y++) {
1600 int level=analogtv_level(it, y, ytop, ybot);
1601 float levelmult=analogtv_levelmult(it, level);
1602 float levelmult_y = levelmult * it->contrast_control
1603 * puramp(it, 1.0f, 0.0f, 1.0f) / (0.5f+0.5f*it->puheight) * 0.070f;
1604 float levelmult_iq = levelmult * 0.090f;
1606 analogtv_ntsc_to_yiq(it, lineno, signal,
1607 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1612 while (i<0 && x<it->usewidth) {
1613 XPutPixel(it->image, x, y, it->colors[0]);
1618 while (i<scanend_i && x<it->usewidth) {
1619 float pixfrac=(i&0xffff)/65536.0f;
1620 float invpixfrac=(1.0f-pixfrac);
1622 int yli,ili,qli,cmi;
1624 float interpy=(yiq[pati].y*invpixfrac
1625 + yiq[pati+1].y*pixfrac) * levelmult_y;
1626 float interpi=(yiq[pati].i*invpixfrac
1627 + yiq[pati+1].i*pixfrac) * levelmult_iq;
1628 float interpq=(yiq[pati].q*invpixfrac
1629 + yiq[pati+1].q*pixfrac) * levelmult_iq;
1631 yli = (int)(interpy * it->cmap_y_levels);
1632 ili = (int)((interpi+0.5f) * it->cmap_i_levels);
1633 qli = (int)((interpq+0.5f) * it->cmap_q_levels);
1635 if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
1637 if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1;
1639 if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1;
1641 cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli);
1644 if ((random()%65536)==0) {
1645 printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n",
1646 interpy, interpi, interpq,
1652 for (j=0; j<it->xrepl; j++) {
1653 XPutPixel(it->image, x, y,
1657 if (i >= squishright_i) {
1658 pixmultinc += pixmultinc/squishdiv;
1662 while (x<it->usewidth) {
1663 XPutPixel(it->image, x, y, it->colors[0]);
1669 analogtv_ntsc_to_yiq(it, lineno, signal,
1670 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1672 pixbright=it->contrast_control * puramp(it, 1.0f, 0.0f, 1.0f)
1673 / (0.5f+0.5f*it->puheight) * 1024.0f/100.0f;
1675 i=scanstart_i; rrp=rgb_start;
1676 while (i<0 && rrp!=rgb_end) {
1677 rrp[0]=rrp[1]=rrp[2]=0;
1681 while (i<scanend_i && rrp!=rgb_end) {
1682 float pixfrac=(i&0xffff)/65536.0f;
1683 float invpixfrac=1.0f-pixfrac;
1687 float interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
1688 float interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
1689 float interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
1692 According to the NTSC spec, Y,I,Q are generated as:
1694 y=0.30 r + 0.59 g + 0.11 b
1695 i=0.60 r - 0.28 g - 0.32 b
1696 q=0.21 r - 0.52 g + 0.31 b
1698 So if you invert the implied 3x3 matrix you get what standard
1699 televisions implement with a bunch of resistors (or directly in the
1702 r = y + 0.948 i + 0.624 q
1703 g = y - 0.276 i - 0.639 q
1704 b = y - 1.105 i + 1.729 q
1707 r=(interpy + 0.948f*interpi + 0.624f*interpq) * pixbright;
1708 g=(interpy - 0.276f*interpi - 0.639f*interpq) * pixbright;
1709 b=(interpy - 1.105f*interpi + 1.729f*interpq) * pixbright;
1717 if (i>=squishright_i) {
1718 pixmultinc += pixmultinc/squishdiv;
1719 pixbright += pixbright/squishdiv/2;
1724 while (rrp != rgb_end) {
1725 rrp[0]=rrp[1]=rrp[2]=0.0f;
1729 analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end,
1734 free(raw_rgb_start);
1738 analogtv_draw(analogtv *it, double noiselevel,
1739 const analogtv_reception *const *recs, unsigned rec_count)
1742 int /*bigloadchange,*/drawcount;
1744 int overall_top, overall_bot;
1746 /* AnalogTV isn't very interesting if there isn't enough RAM. */
1750 it->rx_signal_level = noiselevel;
1751 for (i = 0; i != rec_count; ++i) {
1752 const analogtv_reception *rec = recs[i];
1753 double level = rec->level;
1754 analogtv_input *inp=rec->input;
1756 it->rx_signal_level =
1757 sqrt(it->rx_signal_level * it->rx_signal_level +
1758 (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] +
1759 rec->ghostfir[2] + rec->ghostfir[3]))));
1761 /* duplicate the first line into the Nth line to ease wraparound computation */
1762 memcpy(inp->signal[ANALOGTV_V], inp->signal[0],
1763 ANALOGTV_H * sizeof(inp->signal[0][0]));
1766 analogtv_setup_frame(it);
1767 analogtv_set_demod(it);
1769 it->random0 = random();
1770 it->random1 = random();
1771 it->noiselevel = noiselevel;
1773 it->rec_count = rec_count;
1774 threadpool_run(&it->threads, analogtv_thread_add_signals);
1775 threadpool_wait(&it->threads);
1777 it->channel_change_cycles=0;
1779 /* rx_signal has an extra 2 lines at the end, where we copy the
1780 first 2 lines so we can index into it while only worrying about
1781 wraparound on a per-line level */
1782 memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN],
1784 2*ANALOGTV_H*sizeof(it->rx_signal[0]));
1786 /* Repeat for signal_subtotals. */
1788 memcpy(&it->signal_subtotals[ANALOGTV_SIGNAL_LEN / ANALOGTV_SUBTOTAL_LEN],
1789 &it->signal_subtotals[0],
1790 (2*ANALOGTV_H/ANALOGTV_SUBTOTAL_LEN)*sizeof(it->signal_subtotals[0]));
1792 analogtv_sync(it); /* Requires the add_signals be complete. */
1795 /* if (it->hashnoise_on) baseload=0.5; */
1797 /*bigloadchange=1;*/
1799 it->crtload[ANALOGTV_TOP-1]=baseload;
1800 it->puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
1801 (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
1803 analogtv_setup_levels(it, it->puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
1805 for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
1806 int slineno, ytop, ybot;
1807 unsigned signal_offset;
1808 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot, &signal_offset))
1811 if (lineno==it->shrinkpulse) {
1813 /*bigloadchange=1;*/
1818 if (it->hashnoise_rpm>0.0 &&
1821 (slineno<20 && it->flutter_horiz_desync) ||
1822 it->gaussiannoise_level>30 ||
1823 ((it->gaussiannoise_level>2.0 ||
1824 it->multipath) && random()%4) ||
1825 linesig != it->onscreen_signature[lineno])) {
1828 it->onscreen_signature[lineno] = linesig;
1833 Interpolate the 600-dotclock line into however many horizontal
1834 screen pixels we're using, and convert to RGB.
1836 We add some 'bloom', variations in the horizontal scan width with
1837 the amount of brightness, extremely common on period TV sets. They
1838 had a single oscillator which generated both the horizontal scan and
1839 (during the horizontal retrace interval) the high voltage for the
1840 electron beam. More brightness meant more load on the oscillator,
1841 which caused an decrease in horizontal deflection. Look for
1844 Also, the A2 did a bad job of generating horizontal sync pulses
1845 during the vertical blanking interval. This, and the fact that the
1846 horizontal frequency was a bit off meant that TVs usually went a bit
1847 out of sync during the vertical retrace, and the top of the screen
1848 would be bent a bit to the left or right. Look for (shiftthisrow).
1850 We also add a teeny bit of left overscan, just enough to be
1851 annoying, but you can still read the left column of text.
1853 We also simulate compression & brightening on the right side of the
1854 screen. Most TVs do this, but you don't notice because they overscan
1855 so it's off the right edge of the CRT. But the A2 video system used
1856 so much of the horizontal scan line that you had to crank the
1857 horizontal width down in order to not lose the right few characters,
1858 and you'd see the compression on the right edge. Associated with
1859 compression is brightening; since the electron beam was scanning
1860 slower, the same drive signal hit the phosphor harder. Look for
1861 (squishright_i) and (squishdiv).
1865 /* This used to be an int, I suspect by mistake. - Dave */
1872 frac = signal_offset & (ANALOGTV_SUBTOTAL_LEN - 1);
1873 p = it->rx_signal + (signal_offset & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1874 for (i=0; i != frac; i++) {
1878 end0 = (signal_offset + ANALOGTV_PIC_LEN);
1880 end1 = end0 / ANALOGTV_SUBTOTAL_LEN;
1881 for (i=signal_offset / ANALOGTV_SUBTOTAL_LEN; i<end1; i++) {
1882 totsignal += it->signal_subtotals[i];
1885 frac = end0 & (ANALOGTV_SUBTOTAL_LEN - 1);
1886 p = it->rx_signal + (end0 & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1887 for (i=0; i != frac; i++) {
1891 totsignal *= it->agclevel;
1892 ncl = 0.95f * it->crtload[lineno-1] +
1894 (totsignal-30000)/100000.0f +
1895 (slineno>184 ? (slineno-184)*(lineno-184)*0.001f * it->squeezebottom
1897 /*diff=ncl - it->crtload[lineno];*/
1898 /*bigloadchange = (diff>0.01 || diff<-0.01);*/
1899 it->crtload[lineno]=ncl;
1903 threadpool_run(&it->threads, analogtv_thread_draw_lines);
1904 threadpool_wait(&it->threads);
1907 /* poor attempt at visible retrace */
1908 for (i=0; i<15; i++) {
1909 int ytop=(int)((i*it->useheight/15 -
1910 it->useheight/2)*puheight) + it->useheight/2;
1911 int ybot=(int)(((i+1)*it->useheight/15 -
1912 it->useheight/2)*puheight) + it->useheight/2;
1913 int div=it->usewidth*3/2;
1915 for (x=0; x<it->usewidth; x++) {
1916 y = ytop + (ybot-ytop)*x / div;
1917 if (y<0 || y>=it->useheight) continue;
1918 XPutPixel(it->image, x, y, 0xffffff);
1923 if (it->need_clear) {
1924 XClearWindow(it->dpy, it->window);
1929 Subtle change: overall_bot was the bottom of the last scan line. Now it's
1930 the top of the next-after-the-last scan line. This is the same until
1931 the y-dimension is > 2400, note ANALOGTV_MAX_LINEHEIGHT.
1934 overall_top=(int)(it->useheight*(1-it->puheight)/2);
1935 overall_bot=(int)(it->useheight*(1+it->puheight)/2);
1937 if (overall_top<0) overall_top=0;
1938 if (overall_bot>it->useheight) overall_bot=it->useheight;
1940 if (overall_top>0) {
1941 XClearArea(it->dpy, it->window,
1942 it->screen_xo, it->screen_yo,
1943 it->usewidth, overall_top, 0);
1945 if (it->useheight > overall_bot) {
1946 XClearArea(it->dpy, it->window,
1947 it->screen_xo, it->screen_yo+overall_bot,
1948 it->usewidth, it->useheight-overall_bot, 0);
1951 if (overall_bot > overall_top) {
1953 #ifdef HAVE_XSHM_EXTENSION
1954 XShmPutImage(it->dpy, it->window, it->gc, it->image,
1956 it->screen_xo, it->screen_yo+overall_top,
1957 it->usewidth, overall_bot - overall_top,
1961 XPutImage(it->dpy, it->window, it->gc, it->image,
1963 it->screen_xo, it->screen_yo+overall_top,
1964 it->usewidth, overall_bot - overall_top);
1973 gettimeofday(&tv,NULL);
1975 fps=1.0/((tv.tv_sec - it->last_display_time.tv_sec)
1976 + 0.000001*(tv.tv_usec - it->last_display_time.tv_usec));
1977 sprintf(buf, "FPS=%0.1f",fps);
1978 XDrawString(it->dpy, it->window, it->gc, 50, it->useheight*2/3,
1981 it->last_display_time=tv;
1987 analogtv_input_allocate()
1989 analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input));
1995 This takes a screen image and encodes it as a video camera would,
1996 including all the bandlimiting and YIQ modulation.
1997 This isn't especially tuned for speed.
2001 analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
2008 XColor col1[ANALOGTV_PIC_LEN];
2009 XColor col2[ANALOGTV_PIC_LEN];
2010 int multiq[ANALOGTV_PIC_LEN+4];
2011 int y_overscan=5; /* overscan this much top and bottom */
2012 int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
2014 img_w=pic_im->width;
2015 img_h=pic_im->height;
2017 for (i=0; i<ANALOGTV_PIC_LEN+4; i++) {
2018 double phase=90.0-90.0*i;
2020 multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
2023 for (y=0; y<y_scanlength; y++) {
2024 int picy1=(y*img_h)/y_scanlength;
2025 int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
2027 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
2028 int picx=(x*img_w)/ANALOGTV_PIC_LEN;
2029 col1[x].pixel=XGetPixel(pic_im, picx, picy1);
2030 col2[x].pixel=XGetPixel(pic_im, picx, picy2);
2032 XQueryColors(it->dpy, it->colormap, col1, ANALOGTV_PIC_LEN);
2033 XQueryColors(it->dpy, it->colormap, col2, ANALOGTV_PIC_LEN);
2035 for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
2036 for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
2038 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
2040 int filty,filti,filtq;
2043 y=0.30 r + 0.59 g + 0.11 b
2044 i=0.60 r - 0.28 g - 0.32 b
2045 q=0.21 r - 0.52 g + 0.31 b
2046 The coefficients below are in .4 format */
2048 rawy=( 5*col1[x].red + 11*col1[x].green + 2*col1[x].blue +
2049 5*col2[x].red + 11*col2[x].green + 2*col2[x].blue)>>7;
2050 rawi=(10*col1[x].red - 4*col1[x].green - 5*col1[x].blue +
2051 10*col2[x].red - 4*col2[x].green - 5*col2[x].blue)>>7;
2052 rawq=( 3*col1[x].red - 8*col1[x].green + 5*col1[x].blue +
2053 3*col2[x].red - 8*col2[x].green + 5*col2[x].blue)>>7;
2055 /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz
2056 with an extra zero at 3.5 MHz, from
2057 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l */
2059 fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3];
2060 fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6];
2061 fyx[6] = (rawy * 1897) >> 16;
2062 fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3];
2063 fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6];
2064 fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
2065 + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
2068 /* Filter I at 1.5 MHz. 3 pole Butterworth from
2069 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */
2071 fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
2072 fix[3] = (rawi * 1413) >> 16;
2073 fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
2074 fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
2075 + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
2078 /* Filter Q at 0.5 MHz. 3 pole Butterworth from
2079 mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */
2081 fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
2082 fqx[3] = (rawq * 75) >> 16;
2083 fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
2084 fqy[3] = (fqx[0]+fqx[3]) + 3 * (fqx[1]+fqx[2])
2085 + ((2612*fqy[0] - 9007*fqy[1] + 10453 * fqy[2]) >> 12);
2089 composite = filty + ((multiq[x] * filti + multiq[x+3] * filtq)>>12);
2090 composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
2091 if (composite>125) composite=125;
2092 if (composite<0) composite=0;
2093 input->signal[y-y_overscan+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite;
2101 void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
2104 int change=random()%ANALOGTV_V;
2105 unsigned int fastrnd=random();
2106 double hso=(int)(random()%1000)-500;
2107 int yofs=random()%ANALOGTV_V;
2110 for (y=change; y<ANALOGTV_V; y++) {
2111 int s2y=(y+yofs)%ANALOGTV_V;
2113 int noiselevel=60000 / (y-change+100);
2115 it->line_hsync[y] = s2->line_hsync[y] + (int)hso;
2117 for (x=0; x<ANALOGTV_H; x++) {
2119 filt+= (-filt/16) + (int)(fastrnd&0xfff)-0x800;
2120 noise=(filt*noiselevel)>>16;
2121 newsig=s2->signal[s2y][x] + noise;
2122 if (newsig>120) newsig=120;
2123 if (newsig<0) newsig=0;
2124 it->signal[y][x]=newsig;
2134 if (it->hashnoise_times[lineno]) {
2135 int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno];
2137 if (hnt>=0 && hnt<ANALOGTV_PIC_LEN) {
2139 double cur=frand(150.0)-20.0;
2140 int len=random()%15+3;
2141 if (len > ANALOGTV_PIC_LEN-hnt) len=ANALOGTV_PIC_LEN-hnt;
2142 for (i=0; i<len; i++) {
2143 double sig=signal[hnt];
2146 cur += frand(5.0)-5.0;
2147 maxampl = maxampl*0.9;
2158 analogtv_reception_update(analogtv_reception *rec)
2162 if (rec->multipath > 0.0) {
2163 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2164 rec->ghostfir2[i] +=
2165 -(rec->ghostfir2[i]/16.0) + rec->multipath * (frand(0.02)-0.01);
2167 if (random()%20==0) {
2168 rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)]
2169 = rec->multipath * (frand(0.08)-0.04);
2171 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2172 rec->ghostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i];
2176 rec->hfloss2 += -(rec->hfloss2/16.0) + rec->multipath * (frand(0.08)-0.04);
2177 rec->hfloss = 0.5*rec->hfloss + 0.5*rec->hfloss2;
2181 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2182 rec->ghostfir[i] = (i>=ANALOGTV_GHOSTFIR_LEN/2) ? ((i&1) ? +0.04 : -0.08) /ANALOGTV_GHOSTFIR_LEN
2189 /* jwz: since MacOS doesn't have "6x10", I dumped this font to an XBM...
2192 #include "images/6x10font.xbm"
2195 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
2196 int w, int h, char *fontname)
2203 XWindowAttributes xgwa;
2208 XGetWindowAttributes (dpy, window, &xgwa);
2210 if (fontname && !strcmp (fontname, "6x10")) {
2212 text_pm = XCreatePixmapFromBitmapData (dpy, window,
2213 (char *) font6x10_bits,
2217 f->text_im = XGetImage(dpy, text_pm, 0, 0, font6x10_width, font6x10_height,
2219 XFreePixmap(dpy, text_pm);
2221 } else if (fontname) {
2223 font = XLoadQueryFont (dpy, fontname);
2225 fprintf(stderr, "analogtv: can't load font %s\n", fontname);
2229 text_pm=XCreatePixmap(dpy, window, 256*f->char_w, f->char_h, 1);
2231 memset(&gcv, 0, sizeof(gcv));
2235 gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
2237 XSetForeground(dpy, gc, 0);
2238 XFillRectangle(dpy, text_pm, gc, 0, 0, 256*f->char_w, f->char_h);
2239 XSetForeground(dpy, gc, 1);
2240 for (i=0; i<256; i++) {
2242 int x=f->char_w*i+1;
2243 int y=f->char_h*8/10;
2244 XDrawString(dpy, text_pm, gc, x, y, &c, 1);
2246 f->text_im = XGetImage(dpy, text_pm, 0, 0, 256*f->char_w, f->char_h,
2249 XWriteBitmapFile(dpy, "/tmp/tvfont.xbm", text_pm,
2250 256*f->char_w, f->char_h, -1, -1);
2253 XFreePixmap(dpy, text_pm);
2255 f->text_im = XCreateImage(dpy, xgwa.visual, 1, XYPixmap, 0, 0,
2256 256*f->char_w, f->char_h, 8, 0);
2257 f->text_im->data = (char *)calloc(f->text_im->height,
2258 f->text_im->bytes_per_line);
2266 analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
2268 if (x<0 || x>=f->char_w) return 0;
2269 if (y<0 || y>=f->char_h) return 0;
2270 if (c<0 || c>=256) return 0;
2271 return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
2275 analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
2277 if (x<0 || x>=f->char_w) return;
2278 if (y<0 || y>=f->char_h) return;
2279 if (c<0 || c>=256) return;
2281 XPutPixel(f->text_im, c*f->char_w + x, y, value);
2285 analogtv_font_set_char(analogtv_font *f, int c, char *s)
2289 if (c<0 || c>=256) return;
2291 for (y=0; y<f->char_h; y++) {
2292 for (x=0; x<f->char_w; x++) {
2294 value=(*s==' ') ? 0 : 1;
2295 analogtv_font_set_pixel(f, c, x, y, value);
2302 analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
2305 for (i=0; i<4; i++) {
2306 double w=90.0*i + phase;
2307 double val=luma + chroma * (cos(3.1415926/180.0*w));
2308 if (val<0.0) val=0.0;
2309 if (val>127.0) val=127.0;
2315 analogtv_draw_solid(analogtv_input *input,
2316 int left, int right, int top, int bot,
2321 if (right-left<4) right=left+4;
2322 if (bot-top<1) bot=top+1;
2324 for (y=top; y<bot; y++) {
2325 for (x=left; x<right; x++) {
2326 input->signal[y][x] = ntsc[x&3];
2333 analogtv_draw_solid_rel_lcp(analogtv_input *input,
2334 double left, double right, double top, double bot,
2335 double luma, double chroma, double phase)
2339 int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top);
2340 int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot);
2341 int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left);
2342 int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right);
2344 analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
2345 analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
2350 analogtv_draw_char(analogtv_input *input, analogtv_font *f,
2351 int c, int x, int y, int ntsc[4])
2353 int yc,xc,ys,xs,pix;
2355 for (yc=0; yc<f->char_h; yc++) {
2356 for (ys=y + yc*f->y_mult; ys<y + (yc+1)*f->y_mult; ys++) {
2357 if (ys<0 || ys>=ANALOGTV_V) continue;
2359 for (xc=0; xc<f->char_w; xc++) {
2360 pix=analogtv_font_pixel(f, c, xc, yc);
2362 for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
2363 if (xs<0 || xs>=ANALOGTV_H) continue;
2365 input->signal[ys][xs] = ntsc[xs&3];
2374 analogtv_draw_string(analogtv_input *input, analogtv_font *f,
2375 char *s, int x, int y, int ntsc[4])
2378 analogtv_draw_char(input, f, *s, x, y, ntsc);
2385 analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
2386 char *s, int x, int y, int ntsc[4])
2388 int width=strlen(s) * f->char_w * 4;
2391 analogtv_draw_string(input, f, s, x, y, ntsc);
2395 static const char hextonib[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2396 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2397 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2398 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
2399 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2400 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2401 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2402 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2405 Much of this function was adapted from logo.c
2408 analogtv_draw_xpm(analogtv *tv, analogtv_input *input,
2409 const char * const *xpm, int left, int top)
2414 int ncolors, nbytes;
2417 int r; int g; int b;
2421 if (4 != sscanf ((const char *) *xpm,
2423 &xpmw, &xpmh, &ncolors, &nbytes, &dummyc))
2425 if (ncolors < 1 || ncolors > 255)
2427 if (nbytes != 1) /* a serious limitation */
2431 for (i = 0; i < ncolors; i++) {
2432 const char *line = *xpm;
2433 int colori = ((unsigned char)*line++)&0xff;
2438 while (*line == ' ' || *line == '\t')
2441 if (which != 'c' && which != 'm')
2443 while (*line == ' ' || *line == '\t')
2445 if (!strncasecmp(line, "None", 4))
2454 r = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2456 g = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2458 b = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2473 for (y=0; y<xpmh; y++) {
2474 const char *line = *xpm++;
2476 if (tvy<ANALOGTV_TOP || tvy>=ANALOGTV_BOT) continue;
2478 for (x=0; x<xpmw; x++) {
2479 int cbyte=((unsigned char)line[x])&0xff;
2482 if (tvx<ANALOGTV_PIC_START || tvx+4>ANALOGTV_PIC_END) continue;
2484 rawy=( 5*cmap[cbyte].r + 11*cmap[cbyte].g + 2*cmap[cbyte].b) / 64;
2485 rawi=(10*cmap[cbyte].r - 4*cmap[cbyte].g - 5*cmap[cbyte].b) / 64;
2486 rawq=( 3*cmap[cbyte].r - 8*cmap[cbyte].g + 5*cmap[cbyte].b) / 64;
2493 for (i=0; i<4; i++) {
2494 if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
2495 if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
2498 input->signal[tvy][tvx+0]= ntsc[(tvx+0)&3];
2499 input->signal[tvy][tvx+1]= ntsc[(tvx+1)&3];
2500 input->signal[tvy][tvx+2]= ntsc[(tvx+2)&3];
2501 input->signal[tvy][tvx+3]= ntsc[(tvx+3)&3];