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.
62 /* 2015-02-27, Tomasz Sulej <tomeksul@gmail.com>:
63 - tint_control variable is used now
64 - removed unusable hashnoise code
68 2016-10-09, Dave Odell <dmo2118@gmail.com>:
69 Updated for new xshm.c.
74 #else /* !HAVE_JWXYZ */
75 # include <X11/Xlib.h>
76 # include <X11/Xutil.h>
83 #include "resources.h"
86 #include "grabscreen.h"
91 #if defined(DEBUG) && (defined(__linux) || defined(__FreeBSD__))
92 /* only works on linux + freebsd */
93 #include <machine/cpufunc.h>
95 #define DTIME_DECL u_int64_t dtimes[100]; int n_dtimes
96 #define DTIME_START do {n_dtimes=0; dtimes[n_dtimes++]=rdtsc(); } while (0)
97 #define DTIME dtimes[n_dtimes++]=rdtsc()
98 #define DTIME_SHOW(DIV) \
100 double _dtime_div=(DIV); \
101 printf("time/%.1f: ",_dtime_div); \
102 for (i=1; i<n_dtimes; i++) \
103 printf(" %0.9f",(dtimes[i]-dtimes[i-1])* 1e-9 / _dtime_div); \
110 #define DTIME_START do { } while (0)
111 #define DTIME do { } while (0)
112 #define DTIME_SHOW(DIV) do { } while (0)
117 #define FASTRND_A 1103515245
118 #define FASTRND_C 12345
119 #define FASTRND (fastrnd = fastrnd*FASTRND_A+FASTRND_C)
121 static void analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
122 int start, int end, struct analogtv_yiq_s *it_yiq);
124 static float puramp(const analogtv *it, float tc, float start, float over)
126 float pt=it->powerup-start;
128 if (pt<0.0f) return 0.0f;
129 if (pt>900.0f || pt/tc>8.0f) return 1.0f;
131 ret=(1.0f-expf(-pt/tc))*over;
132 if (ret>1.0f) return 1.0f;
137 There are actual standards for TV signals: NTSC and RS-170A describe the
138 system used in the US and Japan. Europe has slightly different systems, but
139 not different enough to make substantially different screensaver displays.
140 Sadly, the standards bodies don't do anything so useful as publish the spec on
141 the web. Best bets are:
143 http://www.ee.washington.edu/conselec/CE/kuhn/ntsc/95x4.htm
144 http://www.ntsc-tv.com/ntsc-index-02.htm
146 In DirectColor or TrueColor modes, it generates pixel values directly from RGB
147 values it calculates across each scan line. In PseudoColor mode, it consider
148 each possible pattern of 5 preceding bit values in each possible position
149 modulo 4 and allocates a color for each. A few things, like the brightening on
150 the right side as the horizontal trace slows down, aren't done in PseudoColor.
152 I'd like to add a bit of visible retrace, but it conflicts with being able to
153 bitcopy the image when fast scrolling. After another couple of CPU
154 generations, we could probably regenerate the whole image from scratch every
155 time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling
159 /* localbyteorder is MSBFirst or LSBFirst */
160 static int localbyteorder;
161 static const double float_low8_ofs=8388608.0;
162 static int float_extraction_works;
174 unsigned int localbyteorder_loc = (MSBFirst<<24) | (LSBFirst<<0);
175 localbyteorder=*(char *)&localbyteorder_loc;
182 float_extraction_works=1;
183 for (i=0; i<256*4; i++) {
184 fe.f=float_low8_ofs+(double)i;
188 printf("Float extraction failed for %d => %d\n",i,ans);
190 float_extraction_works=0;
199 analogtv_set_defaults(analogtv *it, char *prefix)
203 sprintf(buf,"%sTVTint",prefix);
204 it->tint_control = get_float_resource(it->dpy, buf,"TVTint");
205 sprintf(buf,"%sTVColor",prefix);
206 it->color_control = get_float_resource(it->dpy, buf,"TVColor")/100.0;
207 sprintf(buf,"%sTVBrightness",prefix);
208 it->brightness_control = get_float_resource(it->dpy, buf,"TVBrightness") / 100.0;
209 sprintf(buf,"%sTVContrast",prefix);
210 it->contrast_control = get_float_resource(it->dpy, buf,"TVContrast") / 100.0;
211 it->height_control = 1.0;
212 it->width_control = 1.0;
213 it->squish_control = 0.0;
218 it->hashnoise_enable=1;
220 it->horiz_desync=frand(10.0)-5.0;
221 it->squeezebottom=frand(5.0)-1.0;
224 printf("analogtv: prefix=%s\n",prefix);
225 printf(" use: cmap=%d color=%d\n",
226 it->use_cmap,it->use_color);
227 printf(" controls: tint=%g color=%g brightness=%g contrast=%g\n",
228 it->tint_control, it->color_control, it->brightness_control,
229 it->contrast_control);
230 /* printf(" freq_error %g: %g %d\n",
231 it->freq_error, it->freq_error_inc, it->flutter_tint); */
232 printf(" desync: %g %d\n",
233 it->horiz_desync, it->flutter_horiz_desync);
234 printf(" hashnoise rpm: %g\n",
236 printf(" vis: %d %d\n",
237 it->visclass, it->visdepth);
238 printf(" shift: %d-%d %d-%d %d-%d\n",
239 it->red_invprec,it->red_shift,
240 it->green_invprec,it->green_shift,
241 it->blue_invprec,it->blue_shift);
242 printf(" size: %d %d %d %d xrepl=%d\n",
243 it->usewidth, it->useheight,
244 it->screen_xo, it->screen_yo, it->xrepl);
246 printf(" ANALOGTV_V=%d\n",ANALOGTV_V);
247 printf(" ANALOGTV_TOP=%d\n",ANALOGTV_TOP);
248 printf(" ANALOGTV_VISLINES=%d\n",ANALOGTV_VISLINES);
249 printf(" ANALOGTV_BOT=%d\n",ANALOGTV_BOT);
250 printf(" ANALOGTV_H=%d\n",ANALOGTV_H);
251 printf(" ANALOGTV_SYNC_START=%d\n",ANALOGTV_SYNC_START);
252 printf(" ANALOGTV_BP_START=%d\n",ANALOGTV_BP_START);
253 printf(" ANALOGTV_CB_START=%d\n",ANALOGTV_CB_START);
254 printf(" ANALOGTV_PIC_START=%d\n",ANALOGTV_PIC_START);
255 printf(" ANALOGTV_PIC_LEN=%d\n",ANALOGTV_PIC_LEN);
256 printf(" ANALOGTV_FP_START=%d\n",ANALOGTV_FP_START);
257 printf(" ANALOGTV_PIC_END=%d\n",ANALOGTV_PIC_END);
258 printf(" ANALOGTV_HASHNOISE_LEN=%d\n",ANALOGTV_HASHNOISE_LEN);
264 extern Bool mono_p; /* shoot me */
267 analogtv_free_image(analogtv *it)
270 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
276 analogtv_alloc_image(analogtv *it)
278 /* On failure, it->image is NULL. */
280 unsigned bits_per_pixel = visual_pixmap_depth(it->screen, it->xgwa.visual);
281 unsigned align = thread_memory_alignment(it->dpy) * 8 - 1;
282 /* Width is in bits. */
283 unsigned width = (it->usewidth * bits_per_pixel + align) & ~align;
285 it->image=create_xshm_image(it->dpy, it->xgwa.visual, it->xgwa.depth,
286 ZPixmap, &it->shm_info,
287 width / bits_per_pixel, it->useheight);
290 memset (it->image->data, 0, it->image->height * it->image->bytes_per_line);
292 /* Not enough memory. Maybe try a smaller window. */
293 fprintf(stderr, "analogtv: %s\n", strerror(ENOMEM));
299 analogtv_configure(analogtv *it)
301 int oldwidth=it->usewidth;
302 int oldheight=it->useheight;
303 int wlim,hlim,height_diff;
305 /* If the window is very small, don't let the image we draw get lower
306 than the actual TV resolution (266x200.)
308 If the aspect ratio of the window is close to a 4:3 or 16:9 ratio,
309 then scale the image to exactly fill the window.
311 Otherwise, center the image either horizontally or vertically,
312 letterboxing or pillarboxing (but not both).
314 If it's very close (2.5%) to a multiple of VISLINES, make it exact
315 For example, it maps 1024 => 1000.
317 float percent = 0.15;
318 float min_ratio = 4.0 / 3.0 * (1 - percent);
319 float max_ratio = 16.0 / 9.0 * (1 + percent);
321 float height_snap=0.025;
323 hlim = it->xgwa.height;
324 wlim = it->xgwa.width;
325 ratio = wlim / (float) hlim;
328 /* Fill the whole iPhone screen, even though that distorts the image. */
333 if (wlim < 266 || hlim < 200)
339 "size: minimal: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
340 wlim, hlim, it->xgwa.width, it->xgwa.height,
341 min_ratio, ratio, max_ratio);
344 else if (ratio > min_ratio && ratio < max_ratio)
348 "size: close enough: %dx%d (%.3f < %.3f < %.3f)\n",
349 wlim, hlim, min_ratio, ratio, max_ratio);
352 else if (ratio >= max_ratio)
354 wlim = hlim*max_ratio;
357 "size: center H: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
358 wlim, hlim, it->xgwa.width, it->xgwa.height,
359 min_ratio, ratio, max_ratio);
362 else /* ratio <= min_ratio */
364 hlim = wlim/min_ratio;
367 "size: center V: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
368 wlim, hlim, it->xgwa.width, it->xgwa.height,
369 min_ratio, ratio, max_ratio);
374 height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
375 if (height_diff != 0 && abs(height_diff) < hlim * height_snap)
381 /* Most times this doesn't change */
382 if (wlim != oldwidth || hlim != oldheight) {
387 it->xrepl=1+it->usewidth/640;
388 if (it->xrepl>2) it->xrepl=2;
389 it->subwidth=it->usewidth/it->xrepl;
391 analogtv_free_image(it);
392 analogtv_alloc_image(it);
395 it->screen_xo = (it->xgwa.width-it->usewidth)/2;
396 it->screen_yo = (it->xgwa.height-it->useheight)/2;
401 analogtv_reconfigure(analogtv *it)
403 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
404 analogtv_configure(it);
407 /* Can be any power-of-two <= 32. 16 a slightly better choice for 2-3 threads. */
408 #define ANALOGTV_SUBTOTAL_LEN 32
410 typedef struct analogtv_thread_s
414 size_t signal_start, signal_end;
417 #define SIGNAL_OFFSET(thread_id) \
418 ((ANALOGTV_SIGNAL_LEN * (thread_id) / threads->count) & align)
420 static int analogtv_thread_create(void *thread_raw, struct threadpool *threads,
423 analogtv_thread *thread = (analogtv_thread *)thread_raw;
426 thread->it = GET_PARENT_OBJ(analogtv, threads, threads);
427 thread->thread_id = thread_id;
429 align = thread_memory_alignment(thread->it->dpy) /
430 sizeof(thread->it->signal_subtotals[0]);
433 align = ~(align * ANALOGTV_SUBTOTAL_LEN - 1);
435 thread->signal_start = SIGNAL_OFFSET(thread_id);
436 thread->signal_end = thread_id + 1 == threads->count ?
437 ANALOGTV_SIGNAL_LEN :
438 SIGNAL_OFFSET(thread_id + 1);
443 static void analogtv_thread_destroy(void *thread_raw)
448 analogtv_allocate(Display *dpy, Window window)
450 static const struct threadpool_class cls = {
451 sizeof(analogtv_thread),
452 analogtv_thread_create,
453 analogtv_thread_destroy
459 const size_t rx_signal_len = ANALOGTV_SIGNAL_LEN + 2*ANALOGTV_H;
463 it=(analogtv *)calloc(1,sizeof(analogtv));
467 it->signal_subtotals=NULL;
472 if (thread_malloc((void **)&it->rx_signal, dpy,
473 sizeof(it->rx_signal[0]) * rx_signal_len))
476 assert(!(ANALOGTV_SIGNAL_LEN % ANALOGTV_SUBTOTAL_LEN));
477 if (thread_malloc((void **)&it->signal_subtotals, dpy,
478 sizeof(it->signal_subtotals[0]) *
479 (rx_signal_len / ANALOGTV_SUBTOTAL_LEN)))
482 if (threadpool_create(&it->threads, &cls, dpy, hardware_concurrency(dpy)))
485 assert(it->threads.count);
491 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
493 it->screen=it->xgwa.screen;
494 it->colormap=it->xgwa.colormap;
495 it->visclass=visual_class(it->xgwa.screen, it->xgwa.visual);
496 it->visdepth=it->xgwa.depth;
497 if (it->visclass == TrueColor || it->visclass == DirectColor) {
498 if (get_integer_resource (it->dpy, "use_cmap", "Integer")) {
503 it->use_color=!mono_p;
505 else if (it->visclass == PseudoColor || it->visclass == StaticColor) {
507 it->use_color=!mono_p;
514 visual_rgb_masks (it->xgwa.screen, it->xgwa.visual,
515 &it->red_mask, &it->green_mask, &it->blue_mask);
516 it->red_shift=it->red_invprec=-1;
517 it->green_shift=it->green_invprec=-1;
518 it->blue_shift=it->blue_invprec=-1;
520 /* Is there a standard way to do this? Does this handle all cases? */
522 for (shift=0; shift<32; shift++) {
523 for (prec=1; prec<16 && prec<40-shift; prec++) {
524 unsigned long mask=(0xffffUL>>(16-prec)) << shift;
525 if (it->red_shift<0 && mask==it->red_mask)
526 it->red_shift=shift, it->red_invprec=16-prec;
527 if (it->green_shift<0 && mask==it->green_mask)
528 it->green_shift=shift, it->green_invprec=16-prec;
529 if (it->blue_shift<0 && mask==it->blue_mask)
530 it->blue_shift=shift, it->blue_invprec=16-prec;
533 if (it->red_shift<0 || it->green_shift<0 || it->blue_shift<0) {
534 if (0) fprintf(stderr,"Can't figure out color space\n");
538 for (i=0; i<ANALOGTV_CV_MAX; i++) {
539 int intensity=pow(i/256.0, 0.8)*65535.0; /* gamma correction */
540 if (intensity>65535) intensity=65535;
541 it->red_values[i]=((intensity>>it->red_invprec)<<it->red_shift);
542 it->green_values[i]=((intensity>>it->green_invprec)<<it->green_shift);
543 it->blue_values[i]=((intensity>>it->blue_invprec)<<it->blue_shift);
548 gcv.background=get_pixel_resource(it->dpy, it->colormap,
549 "background", "Background");
551 it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
552 XSetWindowBackground(it->dpy, it->window, gcv.background);
553 XClearWindow(dpy,window);
555 analogtv_configure(it);
561 if(it->threads.count)
562 threadpool_destroy(&it->threads);
563 thread_free(it->signal_subtotals);
564 thread_free(it->rx_signal);
571 analogtv_release(analogtv *it)
574 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
577 if (it->gc) XFreeGC(it->dpy, it->gc);
579 if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
581 threadpool_destroy(&it->threads);
582 thread_free(it->rx_signal);
583 thread_free(it->signal_subtotals);
589 First generate the I and Q reference signals, which we'll multiply
590 the input signal by to accomplish the demodulation. Normally they
591 are shifted 33 degrees from the colorburst. I think this was convenient
592 for inductor-capacitor-vacuum tube implementation.
594 The tint control, FWIW, just adds a phase shift to the chroma signal,
595 and the color control controls the amplitude.
597 In text modes (colormode==0) the system disabled the color burst, and no
598 color was detected by the monitor.
600 freq_error gives a mismatch between the built-in oscillator and the
601 TV's colorbust. Some II Plus machines seemed to occasionally get
602 instability problems -- the crystal oscillator was a single
603 transistor if I remember correctly -- and the frequency would vary
604 enough that the tint would change across the width of the screen.
605 The left side would be in correct tint because it had just gotten
606 resynchronized with the color burst.
608 If we're using a colormap, set it up.
611 analogtv_set_demod(analogtv *it)
613 int y_levels=10,i_levels=5,q_levels=5;
616 In principle, we might be able to figure out how to adjust the
617 color map frame-by-frame to get some nice color bummage. But I'm
618 terrified of changing the color map because we'll get flashing.
620 I can hardly believe we still have to deal with colormaps. They're
621 like having NEAR PTRs: an enormous hassle for the programmer just
622 to save on memory. They should have been deprecated by 1995 or
626 if (it->use_cmap && !it->n_colors) {
629 XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
635 for (yli=0; yli<y_levels; yli++) {
636 for (ili=0; ili<i_levels; ili++) {
637 for (qli=0; qli<q_levels; qli++) {
638 double interpy,interpi,interpq;
639 double levelmult=700.0;
643 interpy=100.0 * ((double)yli/y_levels);
644 interpi=50.0 * (((double)ili-(0.5*i_levels))/(double)i_levels);
645 interpq=50.0 * (((double)qli-(0.5*q_levels))/(double)q_levels);
647 r=(int)((interpy + 1.04*interpi + 0.624*interpq)*levelmult);
648 g=(int)((interpy - 0.276*interpi - 0.639*interpq)*levelmult);
649 b=(int)((interpy - 1.105*interpi + 1.729*interpq)*levelmult);
651 if (r>65535) r=65535;
653 if (g>65535) g=65535;
655 if (b>65535) b=65535;
658 printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n",
659 interpy, interpi, interpq,
667 if (!XAllocColor(it->dpy, it->colormap, &col)) {
668 if (q_levels > y_levels*4/12)
670 else if (i_levels > y_levels*5/12)
679 it->colors[it->n_colors++]=col.pixel;
684 it->cmap_y_levels=y_levels;
685 it->cmap_i_levels=i_levels;
686 it->cmap_q_levels=q_levels;
695 analogtv_line_signature(analogtv_input *input, int lineno)
698 char *origsignal=&input->signal[(lineno+input->vsync)
699 %ANALOGTV_V][input->line_hsync[lineno]];
703 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
705 hash = hash + (hash<<17) + c;
708 hash += input->line_hsync[lineno];
711 hash += input->hashnoise_times[lineno];
720 /* Here we model the analog circuitry of an NTSC television.
721 Basically, it splits the signal into 3 signals: Y, I and Q. Y
722 corresponds to luminance, and you get it by low-pass filtering the
723 input signal to below 3.57 MHz.
725 I and Q are the in-phase and quadrature components of the 3.57 MHz
726 subcarrier. We get them by multiplying by cos(3.57 MHz*t) and
727 sin(3.57 MHz*t), and low-pass filtering. Because the eye has less
728 resolution in some colors than others, the I component gets
729 low-pass filtered at 1.5 MHz and the Q at 0.5 MHz. The I component
730 is approximately orange-blue, and Q is roughly purple-green. See
731 http://www.ntsc-tv.com for details.
733 We actually do an awful lot to the signal here. I suspect it would
734 make sense to wrap them all up together by calculating impulse
735 response and doing FFT convolutions.
740 analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
741 int start, int end, struct analogtv_yiq_s *it_yiq)
746 int phasecorr=(signal-it->rx_signal)&3;
747 struct analogtv_yiq_s *yiq;
749 float agclevel=it->agclevel;
750 float brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
751 float delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
756 double cb_i=(it->line_cb_phase[lineno][(2+phasecorr)&3]-
757 it->line_cb_phase[lineno][(0+phasecorr)&3])/16.0;
758 double cb_q=(it->line_cb_phase[lineno][(3+phasecorr)&3]-
759 it->line_cb_phase[lineno][(1+phasecorr)&3])/16.0;
761 colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
764 multiq2[0] = (cb_i*it->tint_i - cb_q*it->tint_q) * it->color_control;
765 multiq2[1] = (cb_q*it->tint_i + cb_i*it->tint_q) * it->color_control;
766 multiq2[2]=-multiq2[0];
767 multiq2[3]=-multiq2[1];
773 printf("multiq = [%0.3f %0.3f %0.3f %0.3f] ",
774 it->multiq[60],it->multiq[61],it->multiq[62],it->multiq[63]);
775 printf("it->line_cb_phase = [%0.3f %0.3f %0.3f %0.3f]\n",
776 it->line_cb_phase[lineno][0],it->line_cb_phase[lineno][1],
777 it->line_cb_phase[lineno][2],it->line_cb_phase[lineno][3]);
778 printf("multiq2 = [%0.3f %0.3f %0.3f %0.3f]\n",
779 multiq2[0],multiq2[1],multiq2[2],multiq2[3]);
783 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
784 for (i=0; i<5; i++) dp[i]=0.0f;
787 assert(end < ANALOGTV_PIC_LEN+10);
789 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
790 for (i=0; i<24; i++) dp[i]=0.0;
791 for (i=start, yiq=it_yiq+start, sp=signal+start;
793 i++, dp--, yiq++, sp++) {
795 /* Now filter them. These are infinite impulse response filters
796 calculated by the script at
797 http://www-users.cs.york.ac.uk/~fisher/mkfilter. This is
798 fixed-point integer DSP, son. No place for wimps. We do it in
799 integer because you can count on integer being faster on most
800 CPUs. We care about speed because we need to recalculate every
801 time we blink text, and when we spew random bytes into screen
802 memory. This is roughly 16.16 fixed point arithmetic, but we
803 scale some filter values up by a few bits to avoid some nasty
806 /* Filter Y with a 4-pole low-pass Butterworth filter at 3.5 MHz
807 with an extra zero at 3.5 MHz, from
808 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
811 dp[0] = sp[0] * 0.0469904257251935f * agclevel;
812 dp[8] = (+1.0f*(dp[6]+dp[0])
818 yiq->y = dp[8] + brightadd;
822 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
823 for (i=0; i<27; i++) dp[i]=0.0;
825 for (i=start, yiq=it_yiq+start, sp=signal+start;
827 i++, dp--, yiq++, sp++) {
830 /* Filter I and Q with a 3-pole low-pass Butterworth filter at
831 1.5 MHz with an extra zero at 3.5 MHz, from
832 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 -Z 2.5000000000e-01 -l
836 dp[0] = sig*multiq2[i&3] * 0.0833333333333f;
837 yiq->i=dp[8] = (dp[5] + dp[0]
838 +3.0f*(dp[4] + dp[1])
839 +4.0f*(dp[3] + dp[2])
840 -0.3333333333f * dp[10]);
842 dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333f;
843 yiq->q=dp[24] = (dp[16+5] + dp[16+0]
844 +3.0f*(dp[16+4] + dp[16+1])
845 +4.0f*(dp[16+3] + dp[16+2])
846 -0.3333333333f * dp[24+2]);
849 for (i=start, yiq=it_yiq+start; i<end; i++, yiq++) {
850 yiq->i = yiq->q = 0.0f;
856 analogtv_setup_teletext(analogtv_input *input)
859 int teletext=ANALOGTV_BLACK_LEVEL;
861 /* Teletext goes in line 21. But I suspect there are other things
862 in the vertical retrace interval */
864 for (y=19; y<22; y++) {
865 for (x=ANALOGTV_PIC_START; x<ANALOGTV_PIC_END; x++) {
867 teletext=(random()&1) ? ANALOGTV_WHITE_LEVEL : ANALOGTV_BLACK_LEVEL;
869 input->signal[y][x]=teletext;
875 analogtv_setup_frame(analogtv *it)
881 if (it->flutter_horiz_desync) {
882 /* Horizontal sync during vertical sync instability. */
883 it->horiz_desync += -0.10*(it->horiz_desync-3.0) +
884 ((int)(random()&0xff)-0x80) *
885 ((int)(random()&0xff)-0x80) *
886 ((int)(random()&0xff)-0x80) * 0.000001;
890 for (i=0; i<ANALOGTV_V; i++) {
891 it->hashnoise_times[i]=0;
895 /* let's leave it to process shrinkpulse */
896 if (it->hashnoise_enable && !it->hashnoise_on) {
897 if (random()%10000==0) {
899 it->shrinkpulse=random()%ANALOGTV_V;
902 if (random()%1000==0) {
906 #if 0 /* never used */
907 if (it->hashnoise_on) {
908 it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
909 ((int)(random()%2000)-1000)*0.1;
911 it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
912 if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
914 if (it->hashnoise_rpm > 0.0) {
917 int hnc=it->hashnoise_counter; /* in 24.8 format */
919 /* Convert rpm of a 16-pole motor into dots in 24.8 format */
920 hni_double = ANALOGTV_V * ANALOGTV_H * 256.0 /
921 (it->hashnoise_rpm * 16.0 / 60.0 / 60.0);
922 hni = (hni_double <= INT_MAX) ? (int)hni_double : INT_MAX;
924 while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
925 y=(hnc>>8)/ANALOGTV_H;
926 x=(hnc>>8)%ANALOGTV_H;
928 if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
929 it->hashnoise_times[y]=x;
931 /* hnc += hni + (int)(random()%65536)-32768; */
933 hnc += (int)(random()%65536)-32768;
934 if ((hnc >= 0) && (INT_MAX - hnc < hni)) break;
941 /* hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;*/
944 if (it->rx_signal_level != 0.0)
945 it->agclevel = 1.0/it->rx_signal_level;
950 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
951 printf(" %0.3f",it->ghostfir[i]);
953 printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel);
958 analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
963 int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
965 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
966 vsync=lineno>=3 && lineno<7;
968 sig=input->signal[lineno];
970 i=ANALOGTV_SYNC_START;
972 while (i<ANALOGTV_BP_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
973 while (i<ANALOGTV_H) sig[i++]=synclevel;
975 while (i<ANALOGTV_BP_START) sig[i++]=synclevel;
976 while (i<ANALOGTV_PIC_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
977 while (i<ANALOGTV_FP_START) sig[i++]=ANALOGTV_BLACK_LEVEL;
979 while (i<ANALOGTV_H) sig[i++]=ANALOGTV_BLANK_LEVEL;
982 /* 9 cycles of colorburst */
983 for (i=ANALOGTV_CB_START; i<ANALOGTV_CB_START+36; i+=4) {
984 sig[i+1] += ANALOGTV_CB_LEVEL;
985 sig[i+3] -= ANALOGTV_CB_LEVEL;
992 analogtv_sync(analogtv *it)
994 int cur_hsync=it->cur_hsync;
995 int cur_vsync=it->cur_vsync;
1000 float cbfc=1.0f/128.0f;
1002 /* sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;*/
1003 for (i=-32; i<32; i++) {
1004 lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1005 sp = it->rx_signal + lineno*ANALOGTV_H;
1007 for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/16) {
1010 filt *= it->agclevel;
1012 osc = (float)(ANALOGTV_V+i)/(float)ANALOGTV_V;
1014 if (osc >= 1.05f+0.0002f * filt) break;
1016 cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1018 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
1020 if (lineno>5 && lineno<ANALOGTV_V-3) { /* ignore vsync interval */
1021 unsigned lineno2 = (lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V;
1022 if (!lineno2) lineno2 = ANALOGTV_V;
1023 sp = it->rx_signal + lineno2*ANALOGTV_H + cur_hsync;
1024 for (i=-8; i<8; i++) {
1025 osc = (float)(ANALOGTV_H+i)/(float)ANALOGTV_H;
1026 filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
1028 if (osc >= 1.005f + 0.0001f*filt) break;
1030 cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
1033 it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START +
1034 ANALOGTV_H) % ANALOGTV_H;
1036 /* Now look for the colorburst, which is a few cycles after the H
1037 sync pulse, and store its phase.
1038 The colorburst is 9 cycles long, and we look at the middle 5
1043 sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
1044 for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; i++) {
1045 it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0f-cbfc) +
1046 sp[i]*it->agclevel*cbfc;
1054 for (i=0; i<4; i++) {
1055 tot += it->cb_phase[i]*it->cb_phase[i];
1057 cbgain = 32.0f/sqrtf(tot);
1059 for (i=0; i<4; i++) {
1060 it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
1065 if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n",
1067 it->cb_phase[0], it->cb_phase[1],
1068 it->cb_phase[2], it->cb_phase[3]);
1071 /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */
1074 it->cur_hsync = cur_hsync;
1075 it->cur_vsync = cur_vsync;
1079 analogtv_levelmult(const analogtv *it, int level)
1081 static const double levelfac[3]={-7.5, 5.5, 24.5};
1082 return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
1086 analogtv_level(const analogtv *it, int y, int ytop, int ybot)
1090 if (y==ytop || y==ybot-1) level=0;
1091 else if (y==ytop+1 || y==ybot-2) level=1;
1094 else if (ybot-ytop>=5) {
1095 if (y==ytop || y==ybot-1) level=0;
1098 else if (ybot-ytop>=3) {
1099 if (y==ytop) level=0;
1109 The point of this stuff is to ensure that when useheight is not a
1110 multiple of VISLINES so that TV scan lines map to different numbers
1111 of vertical screen pixels, the total brightness of each scan line
1113 ANALOGTV_MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
1114 it interpolates extra black lines.
1118 analogtv_setup_levels(analogtv *it, double avgheight)
1121 static const double levelfac[3]={-7.5, 5.5, 24.5};
1123 for (height=0; height<avgheight+2.0 && height<=ANALOGTV_MAX_LINEHEIGHT; height++) {
1125 for (i=0; i<height; i++) {
1126 it->leveltable[height][i].index = 2;
1130 it->leveltable[height][0].index=0;
1133 if (height >= 1) it->leveltable[height][height-1].index=0;
1136 it->leveltable[height][1].index=1;
1137 if (height >= 2) it->leveltable[height][height-2].index=1;
1140 for (i=0; i<height; i++) {
1141 it->leveltable[height][i].value =
1142 (40.0 + levelfac[it->leveltable[height][i].index]*puramp(it, 3.0, 6.0, 1.0)) / 256.0;
1148 static void rnd_combine(unsigned *a0, unsigned *c0, unsigned a1, unsigned c1)
1150 *a0 = (*a0 * a1) & 0xffffffffu;
1151 *c0 = (c1 + a1 * *c0) & 0xffffffffu;
1154 static void rnd_seek_ac(unsigned *a, unsigned *c, unsigned dist)
1156 unsigned int a1 = *a, c1 = *c;
1162 rnd_combine(a, c, a1, c1);
1164 rnd_combine(&a1, &c1, a1, c1);
1168 static unsigned int rnd_seek(unsigned a, unsigned c, unsigned rnd, unsigned dist)
1170 rnd_seek_ac(&a, &c, dist);
1174 static void analogtv_init_signal(const analogtv *it, double noiselevel, unsigned start, unsigned end)
1176 float *ps=it->rx_signal + start;
1177 float *pe=it->rx_signal + end;
1179 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random0, start);
1180 unsigned int fastrnd_offset;
1182 float noisemul = sqrt(noiselevel*150)/(float)0x7fffffff;
1184 fastrnd_offset = fastrnd - 0x7fffffff;
1185 nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
1188 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1189 fastrnd_offset = fastrnd - 0x7fffffff;
1190 nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
1195 static void analogtv_add_signal(const analogtv *it, const analogtv_reception *rec, unsigned start, unsigned end, int ec)
1197 analogtv_input *inp=rec->input;
1198 float *ps=it->rx_signal + start;
1199 float *pe=it->rx_signal + end;
1201 signed char *ss=&inp->signal[0][0];
1202 signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
1203 signed char *s=ss + ((start + (unsigned)rec->ofs) % ANALOGTV_SIGNAL_LEN);
1206 float level=rec->level;
1207 float hfloss=rec->hfloss;
1208 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random1, start);
1211 const float noise_decay = 0.99995f;
1212 float noise_ampl = 1.3f * powf(noise_decay, start);
1217 /* assert((se-ss)%4==0 && (se-s)%4==0); */
1219 for (i = start; i < ec; i++) { /* Sometimes start > ec. */
1221 /* Do a big noisy transition. We can make the transition noise of
1222 high constant strength regardless of signal strength.
1224 There are two separate state machines. here, One is the noise
1225 process and the other is the
1227 We don't bother with the FIR filter here
1230 float sig0=(float)s[0];
1231 unsigned int fastrnd_offset = fastrnd - 0x7fffffff;
1232 float noise = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * (50.0f/(float)0x7fffffff);
1233 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1235 p[0] += sig0 * level * (1.0f - noise_ampl) + noise * noise_ampl;
1237 noise_ampl *= noise_decay;
1246 for (i=1; i<5; i++) {
1249 s2 += ANALOGTV_SIGNAL_LEN;
1250 dp[i] = (float)((int)s2[0]+(int)s2[1]+(int)s2[2]+(int)s2[3]);
1254 assert(!((pe - p) % 4));
1256 float sig0,sig1,sig2,sig3,sigr;
1263 dp[0]=sig0+sig1+sig2+sig3;
1265 /* Get the video out signal, and add some ghosting, typical of RF
1266 monitor cables. This corresponds to a pretty long cable, but
1270 sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] +
1271 dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]);
1272 dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0];
1274 p[0] += (sig0+sigr + sig2*hfloss) * level;
1275 p[1] += (sig1+sigr + sig3*hfloss) * level;
1276 p[2] += (sig2+sigr + sig0*hfloss) * level;
1277 p[3] += (sig3+sigr + sig1*hfloss) * level;
1281 if (s>=se) s = ss + (s-se);
1287 static void analogtv_thread_add_signals(void *thread_raw)
1289 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1290 const analogtv *it = thread->it;
1292 unsigned subtotal_end;
1294 unsigned start = thread->signal_start;
1295 while(start != thread->signal_end)
1299 /* Work on 8 KB blocks; these should fit in L1. */
1300 /* (Though it doesn't seem to help much on my system.) */
1301 unsigned end = start + 2048;
1302 if(end > thread->signal_end)
1303 end = thread->signal_end;
1305 analogtv_init_signal (it, it->noiselevel, start, end);
1307 for (i = 0; i != it->rec_count; ++i) {
1308 analogtv_add_signal (it, it->recs[i], start, end,
1309 !i ? it->channel_change_cycles : 0);
1312 assert (!(start % ANALOGTV_SUBTOTAL_LEN));
1313 assert (!(end % ANALOGTV_SUBTOTAL_LEN));
1315 p = it->rx_signal + start;
1316 subtotal_end = end / ANALOGTV_SUBTOTAL_LEN;
1317 for (i = start / ANALOGTV_SUBTOTAL_LEN; i != subtotal_end; ++i) {
1319 for (j = 1; j != ANALOGTV_SUBTOTAL_LEN; ++j)
1321 it->signal_subtotals[i] = sum;
1322 p += ANALOGTV_SUBTOTAL_LEN;
1329 static int analogtv_get_line(const analogtv *it, int lineno, int *slineno,
1330 int *ytop, int *ybot, unsigned *signal_offset)
1332 *slineno=lineno-ANALOGTV_TOP;
1333 *ytop=(int)((*slineno*it->useheight/ANALOGTV_VISLINES -
1334 it->useheight/2)*it->puheight) + it->useheight/2;
1335 *ybot=(int)(((*slineno+1)*it->useheight/ANALOGTV_VISLINES -
1336 it->useheight/2)*it->puheight) + it->useheight/2;
1338 int linesig=analogtv_line_signature(input,lineno)
1339 + it->hashnoise_times[lineno];
1341 *signal_offset = ((lineno+it->cur_vsync+ANALOGTV_V) % ANALOGTV_V) * ANALOGTV_H +
1342 it->line_hsync[lineno];
1344 if (*ytop==*ybot) return 0;
1345 if (*ybot<0 || *ytop>it->useheight) return 0;
1346 if (*ytop<0) *ytop=0;
1347 if (*ybot>it->useheight) *ybot=it->useheight;
1349 if (*ybot > *ytop+ANALOGTV_MAX_LINEHEIGHT) *ybot=*ytop+ANALOGTV_MAX_LINEHEIGHT;
1354 analogtv_blast_imagerow(const analogtv *it,
1355 float *rgbf, float *rgbf_end,
1360 char *level_copyfrom[3];
1361 int xrepl=it->xrepl;
1362 unsigned lineheight = ybot - ytop;
1363 if (lineheight > ANALOGTV_MAX_LINEHEIGHT) lineheight = ANALOGTV_MAX_LINEHEIGHT;
1364 for (i=0; i<3; i++) level_copyfrom[i]=NULL;
1366 for (y=ytop; y<ybot; y++) {
1367 char *rowdata=it->image->data + y*it->image->bytes_per_line;
1368 unsigned line = y-ytop;
1370 int level=it->leveltable[lineheight][line].index;
1371 float levelmult=it->leveltable[lineheight][line].value;
1373 /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
1374 why standard graphics sw has to be fast, or else people will have to
1375 work around it and risk incompatibility. The quickdraw folks
1376 understood this. The other answer would be for X11 to have fewer
1377 formats for bitm.. oh, never mind. If neither of these cases work
1378 (they probably cover 99% of setups) it falls back on the Xlib
1381 if (level_copyfrom[level]) {
1382 memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line);
1385 level_copyfrom[level] = rowdata;
1389 else if (it->image->format==ZPixmap &&
1390 it->image->bits_per_pixel==32 &&
1391 sizeof(unsigned int)==4 &&
1392 it->image->byte_order==localbyteorder) {
1393 /* int is more likely to be 32 bits than long */
1394 unsigned int *pixelptr=(unsigned int *)rowdata;
1397 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1398 int ntscri=rpf[0]*levelmult;
1399 int ntscgi=rpf[1]*levelmult;
1400 int ntscbi=rpf[2]*levelmult;
1401 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1402 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1403 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1404 pix = (it->red_values[ntscri] |
1405 it->green_values[ntscgi] |
1406 it->blue_values[ntscbi]);
1410 if (xrepl>=3) pixelptr[2] = pix;
1415 else if (it->image->format==ZPixmap &&
1416 it->image->bits_per_pixel==16 &&
1417 sizeof(unsigned short)==2 &&
1418 float_extraction_works &&
1419 it->image->byte_order==localbyteorder) {
1420 unsigned short *pixelptr=(unsigned short *)rowdata;
1422 float_extract_t r1,g1,b1;
1425 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1426 r2=rpf[0]; g2=rpf[1]; b2=rpf[2];
1427 r1.f=r2 * levelmult+float_low8_ofs;
1428 g1.f=g2 * levelmult+float_low8_ofs;
1429 b1.f=b2 * levelmult+float_low8_ofs;
1430 pix = (it->red_values[r1.i & 0x3ff] |
1431 it->green_values[g1.i & 0x3ff] |
1432 it->blue_values[b1.i & 0x3ff]);
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 it->image->byte_order==localbyteorder) {
1445 unsigned short *pixelptr=(unsigned short *)rowdata;
1448 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1449 int r1=rpf[0] * levelmult;
1450 int g1=rpf[1] * levelmult;
1451 int b1=rpf[2] * levelmult;
1452 if (r1>=ANALOGTV_CV_MAX) r1=ANALOGTV_CV_MAX-1;
1453 if (g1>=ANALOGTV_CV_MAX) g1=ANALOGTV_CV_MAX-1;
1454 if (b1>=ANALOGTV_CV_MAX) b1=ANALOGTV_CV_MAX-1;
1455 pix = it->red_values[r1] | it->green_values[g1] | it->blue_values[b1];
1459 if (xrepl>=3) pixelptr[2] = pix;
1465 for (x=0, rpf=rgbf; rpf!=rgbf_end ; x++, rpf+=3) {
1466 int ntscri=rpf[0]*levelmult;
1467 int ntscgi=rpf[1]*levelmult;
1468 int ntscbi=rpf[2]*levelmult;
1469 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1470 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1471 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1472 for (j=0; j<xrepl; j++) {
1473 XPutPixel(it->image, x*xrepl + j, y,
1474 it->red_values[ntscri] | it->green_values[ntscgi] |
1475 it->blue_values[ntscbi]);
1483 static void analogtv_thread_draw_lines(void *thread_raw)
1485 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1486 const analogtv *it = thread->it;
1490 float *raw_rgb_start;
1492 raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
1494 if (! raw_rgb_start) return;
1496 raw_rgb_end=raw_rgb_start+3*it->subwidth;
1498 for (lineno=ANALOGTV_TOP + thread->thread_id;
1499 lineno<ANALOGTV_BOT;
1500 lineno += it->threads.count) {
1503 int slineno, ytop, ybot;
1504 unsigned signal_offset;
1506 const float *signal;
1508 int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
1509 float *rgb_start, *rgb_end;
1515 struct analogtv_yiq_s yiq[ANALOGTV_PIC_LEN+10];
1517 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot,
1521 signal = it->rx_signal + signal_offset;
1525 float bloomthisrow,shiftthisrow;
1526 float viswidth,middle;
1530 bloomthisrow = -10.0f * it->crtload[lineno];
1531 if (bloomthisrow<-10.0f) bloomthisrow=-10.0f;
1532 if (bloomthisrow>2.0f) bloomthisrow=2.0f;
1534 shiftthisrow=it->horiz_desync * (expf(-0.17f*slineno) *
1535 (0.7f+cosf(slineno*0.6f)));
1540 viswidth=ANALOGTV_PIC_LEN * 0.79f - 5.0f*bloomthisrow;
1541 middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
1543 scanwidth=it->width_control * puramp(it, 0.5f, 0.3f, 1.0f);
1545 scw=it->subwidth*scanwidth;
1546 if (scw>it->subwidth) scw=it->usewidth;
1547 scl=it->subwidth/2 - scw/2;
1548 scr=it->subwidth/2 + scw/2;
1550 pixrate=(int)((viswidth*65536.0f*1.0f)/it->subwidth)/scanwidth;
1551 scanstart_i=(int)((middle-viswidth*0.5f)*65536.0f);
1552 scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
1553 squishright_i=(int)((middle+viswidth*(0.25f + 0.25f*puramp(it, 2.0f, 0.0f, 1.1f)
1554 - it->squish_control)) *65536.0f);
1555 squishdiv=it->subwidth/15;
1557 rgb_start=raw_rgb_start+scl*3;
1558 rgb_end=raw_rgb_start+scr*3;
1560 assert(scanstart_i>=0);
1563 if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
1565 scanstart_i/65536.0f,
1566 squishright_i/65536.0f,
1573 for (y=ytop; y<ybot; y++) {
1574 int level=analogtv_level(it, y, ytop, ybot);
1575 float levelmult=analogtv_levelmult(it, level);
1576 float levelmult_y = levelmult * it->contrast_control
1577 * puramp(it, 1.0f, 0.0f, 1.0f) / (0.5f+0.5f*it->puheight) * 0.070f;
1578 float levelmult_iq = levelmult * 0.090f;
1580 analogtv_ntsc_to_yiq(it, lineno, signal,
1581 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1586 while (i<0 && x<it->usewidth) {
1587 XPutPixel(it->image, x, y, it->colors[0]);
1592 while (i<scanend_i && x<it->usewidth) {
1593 float pixfrac=(i&0xffff)/65536.0f;
1594 float invpixfrac=(1.0f-pixfrac);
1596 int yli,ili,qli,cmi;
1598 float interpy=(yiq[pati].y*invpixfrac
1599 + yiq[pati+1].y*pixfrac) * levelmult_y;
1600 float interpi=(yiq[pati].i*invpixfrac
1601 + yiq[pati+1].i*pixfrac) * levelmult_iq;
1602 float interpq=(yiq[pati].q*invpixfrac
1603 + yiq[pati+1].q*pixfrac) * levelmult_iq;
1605 yli = (int)(interpy * it->cmap_y_levels);
1606 ili = (int)((interpi+0.5f) * it->cmap_i_levels);
1607 qli = (int)((interpq+0.5f) * it->cmap_q_levels);
1609 if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
1611 if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1;
1613 if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1;
1615 cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli);
1618 if ((random()%65536)==0) {
1619 printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n",
1620 interpy, interpi, interpq,
1626 for (j=0; j<it->xrepl; j++) {
1627 XPutPixel(it->image, x, y,
1631 if (i >= squishright_i) {
1632 pixmultinc += pixmultinc/squishdiv;
1636 while (x<it->usewidth) {
1637 XPutPixel(it->image, x, y, it->colors[0]);
1643 analogtv_ntsc_to_yiq(it, lineno, signal,
1644 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1646 pixbright=it->contrast_control * puramp(it, 1.0f, 0.0f, 1.0f)
1647 / (0.5f+0.5f*it->puheight) * 1024.0f/100.0f;
1649 i=scanstart_i; rrp=rgb_start;
1650 while (i<0 && rrp!=rgb_end) {
1651 rrp[0]=rrp[1]=rrp[2]=0;
1655 while (i<scanend_i && rrp!=rgb_end) {
1656 float pixfrac=(i&0xffff)/65536.0f;
1657 float invpixfrac=1.0f-pixfrac;
1661 float interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
1662 float interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
1663 float interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
1666 According to the NTSC spec, Y,I,Q are generated as:
1668 y=0.30 r + 0.59 g + 0.11 b
1669 i=0.60 r - 0.28 g - 0.32 b
1670 q=0.21 r - 0.52 g + 0.31 b
1672 So if you invert the implied 3x3 matrix you get what standard
1673 televisions implement with a bunch of resistors (or directly in the
1676 r = y + 0.948 i + 0.624 q
1677 g = y - 0.276 i - 0.639 q
1678 b = y - 1.105 i + 1.729 q
1681 r=(interpy + 0.948f*interpi + 0.624f*interpq) * pixbright;
1682 g=(interpy - 0.276f*interpi - 0.639f*interpq) * pixbright;
1683 b=(interpy - 1.105f*interpi + 1.729f*interpq) * pixbright;
1691 if (i>=squishright_i) {
1692 pixmultinc += pixmultinc/squishdiv;
1693 pixbright += pixbright/squishdiv/2;
1698 while (rrp != rgb_end) {
1699 rrp[0]=rrp[1]=rrp[2]=0.0f;
1703 analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end,
1708 free(raw_rgb_start);
1712 analogtv_draw(analogtv *it, double noiselevel,
1713 const analogtv_reception *const *recs, unsigned rec_count)
1716 /* int bigloadchange,drawcount;*/
1718 int overall_top, overall_bot;
1720 /* AnalogTV isn't very interesting if there isn't enough RAM. */
1724 it->rx_signal_level = noiselevel;
1725 for (i = 0; i != rec_count; ++i) {
1726 const analogtv_reception *rec = recs[i];
1727 double level = rec->level;
1728 analogtv_input *inp=rec->input;
1730 it->rx_signal_level =
1731 sqrt(it->rx_signal_level * it->rx_signal_level +
1732 (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] +
1733 rec->ghostfir[2] + rec->ghostfir[3]))));
1735 /* duplicate the first line into the Nth line to ease wraparound computation */
1736 memcpy(inp->signal[ANALOGTV_V], inp->signal[0],
1737 ANALOGTV_H * sizeof(inp->signal[0][0]));
1740 analogtv_setup_frame(it);
1741 analogtv_set_demod(it);
1743 it->random0 = random();
1744 it->random1 = random();
1745 it->noiselevel = noiselevel;
1747 it->rec_count = rec_count;
1748 threadpool_run(&it->threads, analogtv_thread_add_signals);
1749 threadpool_wait(&it->threads);
1751 it->channel_change_cycles=0;
1753 /* rx_signal has an extra 2 lines at the end, where we copy the
1754 first 2 lines so we can index into it while only worrying about
1755 wraparound on a per-line level */
1756 memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN],
1758 2*ANALOGTV_H*sizeof(it->rx_signal[0]));
1760 /* Repeat for signal_subtotals. */
1762 memcpy(&it->signal_subtotals[ANALOGTV_SIGNAL_LEN / ANALOGTV_SUBTOTAL_LEN],
1763 &it->signal_subtotals[0],
1764 (2*ANALOGTV_H/ANALOGTV_SUBTOTAL_LEN)*sizeof(it->signal_subtotals[0]));
1766 analogtv_sync(it); /* Requires the add_signals be complete. */
1769 /* if (it->hashnoise_on) baseload=0.5; */
1773 it->crtload[ANALOGTV_TOP-1]=baseload;
1774 it->puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
1775 (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
1777 analogtv_setup_levels(it, it->puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
1779 /* calculate tint once per frame */
1780 it->tint_i = -cos((103 + it->tint_control)*3.1415926/180);
1781 it->tint_q = sin((103 + it->tint_control)*3.1415926/180);
1783 for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
1784 int slineno, ytop, ybot;
1785 unsigned signal_offset;
1786 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot, &signal_offset))
1789 if (lineno==it->shrinkpulse) {
1791 /*bigloadchange=1;*/
1796 if (it->hashnoise_rpm>0.0 &&
1799 (slineno<20 && it->flutter_horiz_desync) ||
1800 it->gaussiannoise_level>30 ||
1801 ((it->gaussiannoise_level>2.0 ||
1802 it->multipath) && random()%4) ||
1803 linesig != it->onscreen_signature[lineno])) {
1806 it->onscreen_signature[lineno] = linesig;
1811 Interpolate the 600-dotclock line into however many horizontal
1812 screen pixels we're using, and convert to RGB.
1814 We add some 'bloom', variations in the horizontal scan width with
1815 the amount of brightness, extremely common on period TV sets. They
1816 had a single oscillator which generated both the horizontal scan and
1817 (during the horizontal retrace interval) the high voltage for the
1818 electron beam. More brightness meant more load on the oscillator,
1819 which caused an decrease in horizontal deflection. Look for
1822 Also, the A2 did a bad job of generating horizontal sync pulses
1823 during the vertical blanking interval. This, and the fact that the
1824 horizontal frequency was a bit off meant that TVs usually went a bit
1825 out of sync during the vertical retrace, and the top of the screen
1826 would be bent a bit to the left or right. Look for (shiftthisrow).
1828 We also add a teeny bit of left overscan, just enough to be
1829 annoying, but you can still read the left column of text.
1831 We also simulate compression & brightening on the right side of the
1832 screen. Most TVs do this, but you don't notice because they overscan
1833 so it's off the right edge of the CRT. But the A2 video system used
1834 so much of the horizontal scan line that you had to crank the
1835 horizontal width down in order to not lose the right few characters,
1836 and you'd see the compression on the right edge. Associated with
1837 compression is brightening; since the electron beam was scanning
1838 slower, the same drive signal hit the phosphor harder. Look for
1839 (squishright_i) and (squishdiv).
1843 /* This used to be an int, I suspect by mistake. - Dave */
1850 frac = signal_offset & (ANALOGTV_SUBTOTAL_LEN - 1);
1851 p = it->rx_signal + (signal_offset & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1852 for (i=0; i != frac; i++) {
1856 end0 = (signal_offset + ANALOGTV_PIC_LEN);
1858 end1 = end0 / ANALOGTV_SUBTOTAL_LEN;
1859 for (i=signal_offset / ANALOGTV_SUBTOTAL_LEN; i<end1; i++) {
1860 totsignal += it->signal_subtotals[i];
1863 frac = end0 & (ANALOGTV_SUBTOTAL_LEN - 1);
1864 p = it->rx_signal + (end0 & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1865 for (i=0; i != frac; i++) {
1869 totsignal *= it->agclevel;
1870 ncl = 0.95f * it->crtload[lineno-1] +
1872 (totsignal-30000)/100000.0f +
1873 (slineno>184 ? (slineno-184)*(lineno-184)*0.001f * it->squeezebottom
1875 /*diff=ncl - it->crtload[lineno];*/
1876 /*bigloadchange = (diff>0.01 || diff<-0.01);*/
1877 it->crtload[lineno]=ncl;
1881 threadpool_run(&it->threads, analogtv_thread_draw_lines);
1882 threadpool_wait(&it->threads);
1885 /* poor attempt at visible retrace */
1886 for (i=0; i<15; i++) {
1887 int ytop=(int)((i*it->useheight/15 -
1888 it->useheight/2)*puheight) + it->useheight/2;
1889 int ybot=(int)(((i+1)*it->useheight/15 -
1890 it->useheight/2)*puheight) + it->useheight/2;
1891 int div=it->usewidth*3/2;
1893 for (x=0; x<it->usewidth; x++) {
1894 y = ytop + (ybot-ytop)*x / div;
1895 if (y<0 || y>=it->useheight) continue;
1896 XPutPixel(it->image, x, y, 0xffffff);
1901 if (it->need_clear) {
1902 XClearWindow(it->dpy, it->window);
1907 Subtle change: overall_bot was the bottom of the last scan line. Now it's
1908 the top of the next-after-the-last scan line. This is the same until
1909 the y-dimension is > 2400, note ANALOGTV_MAX_LINEHEIGHT.
1912 overall_top=(int)(it->useheight*(1-it->puheight)/2);
1913 overall_bot=(int)(it->useheight*(1+it->puheight)/2);
1915 if (overall_top<0) overall_top=0;
1916 if (overall_bot>it->useheight) overall_bot=it->useheight;
1918 if (overall_top>0) {
1919 XClearArea(it->dpy, it->window,
1920 it->screen_xo, it->screen_yo,
1921 it->usewidth, overall_top, 0);
1923 if (it->useheight > overall_bot) {
1924 XClearArea(it->dpy, it->window,
1925 it->screen_xo, it->screen_yo+overall_bot,
1926 it->usewidth, it->useheight-overall_bot, 0);
1929 if (overall_bot > overall_top) {
1930 put_xshm_image(it->dpy, it->window, it->gc, it->image,
1932 it->screen_xo, it->screen_yo+overall_top,
1933 it->usewidth, overall_bot - overall_top,
1942 gettimeofday(&tv,NULL);
1944 fps=1.0/((tv.tv_sec - it->last_display_time.tv_sec)
1945 + 0.000001*(tv.tv_usec - it->last_display_time.tv_usec));
1946 sprintf(buf, "FPS=%0.1f",fps);
1947 XDrawString(it->dpy, it->window, it->gc, 50, it->useheight*2/3,
1950 it->last_display_time=tv;
1956 analogtv_input_allocate()
1958 analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input));
1964 This takes a screen image and encodes it as a video camera would,
1965 including all the bandlimiting and YIQ modulation.
1966 This isn't especially tuned for speed.
1970 analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
1977 XColor col1[ANALOGTV_PIC_LEN];
1978 XColor col2[ANALOGTV_PIC_LEN];
1979 int multiq[ANALOGTV_PIC_LEN+4];
1980 int y_overscan=5; /* overscan this much top and bottom */
1981 int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
1983 img_w=pic_im->width;
1984 img_h=pic_im->height;
1986 for (i=0; i<ANALOGTV_PIC_LEN+4; i++) {
1987 double phase=90.0-90.0*i;
1989 multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
1992 for (y=0; y<y_scanlength; y++) {
1993 int picy1=(y*img_h)/y_scanlength;
1994 int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
1996 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
1997 int picx=(x*img_w)/ANALOGTV_PIC_LEN;
1998 col1[x].pixel=XGetPixel(pic_im, picx, picy1);
1999 col2[x].pixel=XGetPixel(pic_im, picx, picy2);
2001 XQueryColors(it->dpy, it->colormap, col1, ANALOGTV_PIC_LEN);
2002 XQueryColors(it->dpy, it->colormap, col2, ANALOGTV_PIC_LEN);
2004 for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
2005 for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
2007 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
2009 int filty,filti,filtq;
2012 y=0.30 r + 0.59 g + 0.11 b
2013 i=0.60 r - 0.28 g - 0.32 b
2014 q=0.21 r - 0.52 g + 0.31 b
2015 The coefficients below are in .4 format */
2017 rawy=( 5*col1[x].red + 11*col1[x].green + 2*col1[x].blue +
2018 5*col2[x].red + 11*col2[x].green + 2*col2[x].blue)>>7;
2019 rawi=(10*col1[x].red - 4*col1[x].green - 5*col1[x].blue +
2020 10*col2[x].red - 4*col2[x].green - 5*col2[x].blue)>>7;
2021 rawq=( 3*col1[x].red - 8*col1[x].green + 5*col1[x].blue +
2022 3*col2[x].red - 8*col2[x].green + 5*col2[x].blue)>>7;
2024 /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz
2025 with an extra zero at 3.5 MHz, from
2026 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l */
2028 fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3];
2029 fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6];
2030 fyx[6] = (rawy * 1897) >> 16;
2031 fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3];
2032 fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6];
2033 fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
2034 + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
2037 /* Filter I at 1.5 MHz. 3 pole Butterworth from
2038 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */
2040 fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
2041 fix[3] = (rawi * 1413) >> 16;
2042 fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
2043 fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
2044 + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
2047 /* Filter Q at 0.5 MHz. 3 pole Butterworth from
2048 mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */
2050 fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
2051 fqx[3] = (rawq * 75) >> 16;
2052 fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
2053 fqy[3] = (fqx[0]+fqx[3]) + 3 * (fqx[1]+fqx[2])
2054 + ((2612*fqy[0] - 9007*fqy[1] + 10453 * fqy[2]) >> 12);
2058 composite = filty + ((multiq[x] * filti + multiq[x+3] * filtq)>>12);
2059 composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
2060 if (composite>125) composite=125;
2061 if (composite<0) composite=0;
2062 input->signal[y-y_overscan+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite;
2070 void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
2073 int change=random()%ANALOGTV_V;
2074 unsigned int fastrnd=random();
2075 double hso=(int)(random()%1000)-500;
2076 int yofs=random()%ANALOGTV_V;
2079 for (y=change; y<ANALOGTV_V; y++) {
2080 int s2y=(y+yofs)%ANALOGTV_V;
2082 int noiselevel=60000 / (y-change+100);
2084 it->line_hsync[y] = s2->line_hsync[y] + (int)hso;
2086 for (x=0; x<ANALOGTV_H; x++) {
2088 filt+= (-filt/16) + (int)(fastrnd&0xfff)-0x800;
2089 noise=(filt*noiselevel)>>16;
2090 newsig=s2->signal[s2y][x] + noise;
2091 if (newsig>120) newsig=120;
2092 if (newsig<0) newsig=0;
2093 it->signal[y][x]=newsig;
2103 if (it->hashnoise_times[lineno]) {
2104 int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno];
2106 if (hnt>=0 && hnt<ANALOGTV_PIC_LEN) {
2108 double cur=frand(150.0)-20.0;
2109 int len=random()%15+3;
2110 if (len > ANALOGTV_PIC_LEN-hnt) len=ANALOGTV_PIC_LEN-hnt;
2111 for (i=0; i<len; i++) {
2112 double sig=signal[hnt];
2115 cur += frand(5.0)-5.0;
2116 maxampl = maxampl*0.9;
2127 analogtv_reception_update(analogtv_reception *rec)
2131 if (rec->multipath > 0.0) {
2132 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2133 rec->ghostfir2[i] +=
2134 -(rec->ghostfir2[i]/16.0) + rec->multipath * (frand(0.02)-0.01);
2136 if (random()%20==0) {
2137 rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)]
2138 = rec->multipath * (frand(0.08)-0.04);
2140 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2141 rec->ghostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i];
2145 rec->hfloss2 += -(rec->hfloss2/16.0) + rec->multipath * (frand(0.08)-0.04);
2146 rec->hfloss = 0.5*rec->hfloss + 0.5*rec->hfloss2;
2150 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2151 rec->ghostfir[i] = (i>=ANALOGTV_GHOSTFIR_LEN/2) ? ((i&1) ? +0.04 : -0.08) /ANALOGTV_GHOSTFIR_LEN
2158 /* jwz: since MacOS doesn't have "6x10", I dumped this font to an XBM...
2161 #include "images/6x10font.xbm"
2164 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
2165 int w, int h, char *fontname)
2172 XWindowAttributes xgwa;
2177 XGetWindowAttributes (dpy, window, &xgwa);
2179 if (fontname && !strcmp (fontname, "6x10")) {
2181 text_pm = XCreatePixmapFromBitmapData (dpy, window,
2182 (char *) font6x10_bits,
2186 f->text_im = XGetImage(dpy, text_pm, 0, 0, font6x10_width, font6x10_height,
2188 XFreePixmap(dpy, text_pm);
2190 } else if (fontname) {
2192 font = XLoadQueryFont (dpy, fontname);
2194 fprintf(stderr, "analogtv: can't load font %s\n", fontname);
2198 text_pm=XCreatePixmap(dpy, window, 256*f->char_w, f->char_h, 1);
2200 memset(&gcv, 0, sizeof(gcv));
2204 gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
2206 XSetForeground(dpy, gc, 0);
2207 XFillRectangle(dpy, text_pm, gc, 0, 0, 256*f->char_w, f->char_h);
2208 XSetForeground(dpy, gc, 1);
2209 for (i=0; i<256; i++) {
2211 int x=f->char_w*i+1;
2212 int y=f->char_h*8/10;
2213 XDrawString(dpy, text_pm, gc, x, y, &c, 1);
2215 f->text_im = XGetImage(dpy, text_pm, 0, 0, 256*f->char_w, f->char_h,
2218 XWriteBitmapFile(dpy, "/tmp/tvfont.xbm", text_pm,
2219 256*f->char_w, f->char_h, -1, -1);
2222 XFreePixmap(dpy, text_pm);
2224 f->text_im = XCreateImage(dpy, xgwa.visual, 1, XYPixmap, 0, 0,
2225 256*f->char_w, f->char_h, 8, 0);
2226 f->text_im->data = (char *)calloc(f->text_im->height,
2227 f->text_im->bytes_per_line);
2235 analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
2237 if (x<0 || x>=f->char_w) return 0;
2238 if (y<0 || y>=f->char_h) return 0;
2239 if (c<0 || c>=256) return 0;
2240 return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
2244 analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
2246 if (x<0 || x>=f->char_w) return;
2247 if (y<0 || y>=f->char_h) return;
2248 if (c<0 || c>=256) return;
2250 XPutPixel(f->text_im, c*f->char_w + x, y, value);
2254 analogtv_font_set_char(analogtv_font *f, int c, char *s)
2258 if (c<0 || c>=256) return;
2260 for (y=0; y<f->char_h; y++) {
2261 for (x=0; x<f->char_w; x++) {
2263 value=(*s==' ') ? 0 : 1;
2264 analogtv_font_set_pixel(f, c, x, y, value);
2271 analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
2274 for (i=0; i<4; i++) {
2275 double w=90.0*i + phase;
2276 double val=luma + chroma * (cos(3.1415926/180.0*w));
2277 if (val<0.0) val=0.0;
2278 if (val>127.0) val=127.0;
2284 analogtv_draw_solid(analogtv_input *input,
2285 int left, int right, int top, int bot,
2290 if (right-left<4) right=left+4;
2291 if (bot-top<1) bot=top+1;
2293 for (y=top; y<bot; y++) {
2294 for (x=left; x<right; x++) {
2295 input->signal[y][x] = ntsc[x&3];
2302 analogtv_draw_solid_rel_lcp(analogtv_input *input,
2303 double left, double right, double top, double bot,
2304 double luma, double chroma, double phase)
2308 int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top);
2309 int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot);
2310 int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left);
2311 int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right);
2313 analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
2314 analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
2319 analogtv_draw_char(analogtv_input *input, analogtv_font *f,
2320 int c, int x, int y, int ntsc[4])
2322 int yc,xc,ys,xs,pix;
2324 for (yc=0; yc<f->char_h; yc++) {
2325 for (ys=y + yc*f->y_mult; ys<y + (yc+1)*f->y_mult; ys++) {
2326 if (ys<0 || ys>=ANALOGTV_V) continue;
2328 for (xc=0; xc<f->char_w; xc++) {
2329 pix=analogtv_font_pixel(f, c, xc, yc);
2331 for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
2332 if (xs<0 || xs>=ANALOGTV_H) continue;
2334 input->signal[ys][xs] = ntsc[xs&3];
2343 analogtv_draw_string(analogtv_input *input, analogtv_font *f,
2344 char *s, int x, int y, int ntsc[4])
2347 analogtv_draw_char(input, f, *s, x, y, ntsc);
2354 analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
2355 char *s, int x, int y, int ntsc[4])
2357 int width=strlen(s) * f->char_w * 4;
2360 analogtv_draw_string(input, f, s, x, y, ntsc);
2364 static const char hextonib[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2365 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2366 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2367 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
2368 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2369 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2370 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2371 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2374 Much of this function was adapted from logo.c
2377 analogtv_draw_xpm(analogtv *tv, analogtv_input *input,
2378 const char * const *xpm, int left, int top)
2383 int ncolors, nbytes;
2386 int r; int g; int b;
2390 if (4 != sscanf ((const char *) *xpm,
2392 &xpmw, &xpmh, &ncolors, &nbytes, &dummyc))
2394 if (ncolors < 1 || ncolors > 255)
2396 if (nbytes != 1) /* a serious limitation */
2400 for (i = 0; i < ncolors; i++) {
2401 const char *line = *xpm;
2402 int colori = ((unsigned char)*line++)&0xff;
2407 while (*line == ' ' || *line == '\t')
2410 if (which != 'c' && which != 'm')
2412 while (*line == ' ' || *line == '\t')
2414 if (!strncasecmp(line, "None", 4))
2423 r = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2425 g = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2427 b = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2442 for (y=0; y<xpmh; y++) {
2443 const char *line = *xpm++;
2445 if (tvy<ANALOGTV_TOP || tvy>=ANALOGTV_BOT) continue;
2447 for (x=0; x<xpmw; x++) {
2448 int cbyte=((unsigned char)line[x])&0xff;
2451 if (tvx<ANALOGTV_PIC_START || tvx+4>ANALOGTV_PIC_END) continue;
2453 rawy=( 5*cmap[cbyte].r + 11*cmap[cbyte].g + 2*cmap[cbyte].b) / 64;
2454 rawi=(10*cmap[cbyte].r - 4*cmap[cbyte].g - 5*cmap[cbyte].b) / 64;
2455 rawq=( 3*cmap[cbyte].r - 8*cmap[cbyte].g + 5*cmap[cbyte].b) / 64;
2462 for (i=0; i<4; i++) {
2463 if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
2464 if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
2467 input->signal[tvy][tvx+0]= ntsc[(tvx+0)&3];
2468 input->signal[tvy][tvx+1]= ntsc[(tvx+1)&3];
2469 input->signal[tvy][tvx+2]= ntsc[(tvx+2)&3];
2470 input->signal[tvy][tvx+3]= ntsc[(tvx+3)&3];