1 /* analogtv, Copyright (c) 2003-2018 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"
88 #include "font-retry.h"
89 #include "ximage-loader.h"
93 #if defined(DEBUG) && (defined(__linux) || defined(__FreeBSD__))
94 /* only works on linux + freebsd */
95 #include <machine/cpufunc.h>
97 #define DTIME_DECL u_int64_t dtimes[100]; int n_dtimes
98 #define DTIME_START do {n_dtimes=0; dtimes[n_dtimes++]=rdtsc(); } while (0)
99 #define DTIME dtimes[n_dtimes++]=rdtsc()
100 #define DTIME_SHOW(DIV) \
102 double _dtime_div=(DIV); \
103 printf("time/%.1f: ",_dtime_div); \
104 for (i=1; i<n_dtimes; i++) \
105 printf(" %0.9f",(dtimes[i]-dtimes[i-1])* 1e-9 / _dtime_div); \
112 #define DTIME_START do { } while (0)
113 #define DTIME do { } while (0)
114 #define DTIME_SHOW(DIV) do { } while (0)
119 #define FASTRND_A 1103515245
120 #define FASTRND_C 12345
121 #define FASTRND (fastrnd = fastrnd*FASTRND_A+FASTRND_C)
123 static void analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
124 int start, int end, struct analogtv_yiq_s *it_yiq);
126 static float puramp(const analogtv *it, float tc, float start, float over)
128 float pt=it->powerup-start;
130 if (pt<0.0f) return 0.0f;
131 if (pt>900.0f || pt/tc>8.0f) return 1.0f;
133 ret=(1.0f-expf(-pt/tc))*over;
134 if (ret>1.0f) return 1.0f;
139 There are actual standards for TV signals: NTSC and RS-170A describe the
140 system used in the US and Japan. Europe has slightly different systems, but
141 not different enough to make substantially different screensaver displays.
142 Sadly, the standards bodies don't do anything so useful as publish the spec on
143 the web. Best bets are:
145 http://www.ee.washington.edu/conselec/CE/kuhn/ntsc/95x4.htm
146 http://www.ntsc-tv.com/ntsc-index-02.htm
148 In DirectColor or TrueColor modes, it generates pixel values directly from RGB
149 values it calculates across each scan line. In PseudoColor mode, it consider
150 each possible pattern of 5 preceding bit values in each possible position
151 modulo 4 and allocates a color for each. A few things, like the brightening on
152 the right side as the horizontal trace slows down, aren't done in PseudoColor.
154 I'd like to add a bit of visible retrace, but it conflicts with being able to
155 bitcopy the image when fast scrolling. After another couple of CPU
156 generations, we could probably regenerate the whole image from scratch every
157 time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling
161 /* localbyteorder is MSBFirst or LSBFirst */
162 static int localbyteorder;
163 static const double float_low8_ofs=8388608.0;
164 static int float_extraction_works;
176 unsigned int localbyteorder_loc = (MSBFirst<<24) | (LSBFirst<<0);
177 localbyteorder=*(char *)&localbyteorder_loc;
184 float_extraction_works=1;
185 for (i=0; i<256*4; i++) {
186 fe.f=float_low8_ofs+(double)i;
190 printf("Float extraction failed for %d => %d\n",i,ans);
192 float_extraction_works=0;
201 analogtv_set_defaults(analogtv *it, char *prefix)
205 sprintf(buf,"%sTVTint",prefix);
206 it->tint_control = get_float_resource(it->dpy, buf,"TVTint");
207 sprintf(buf,"%sTVColor",prefix);
208 it->color_control = get_float_resource(it->dpy, buf,"TVColor")/100.0;
209 sprintf(buf,"%sTVBrightness",prefix);
210 it->brightness_control = get_float_resource(it->dpy, buf,"TVBrightness") / 100.0;
211 sprintf(buf,"%sTVContrast",prefix);
212 it->contrast_control = get_float_resource(it->dpy, buf,"TVContrast") / 100.0;
213 it->height_control = 1.0;
214 it->width_control = 1.0;
215 it->squish_control = 0.0;
220 it->hashnoise_enable=1;
222 it->horiz_desync=frand(10.0)-5.0;
223 it->squeezebottom=frand(5.0)-1.0;
226 printf("analogtv: prefix=%s\n",prefix);
227 printf(" use: cmap=%d color=%d\n",
228 it->use_cmap,it->use_color);
229 printf(" controls: tint=%g color=%g brightness=%g contrast=%g\n",
230 it->tint_control, it->color_control, it->brightness_control,
231 it->contrast_control);
232 /* printf(" freq_error %g: %g %d\n",
233 it->freq_error, it->freq_error_inc, it->flutter_tint); */
234 printf(" desync: %g %d\n",
235 it->horiz_desync, it->flutter_horiz_desync);
236 printf(" hashnoise rpm: %g\n",
238 printf(" vis: %d %d\n",
239 it->visclass, it->visdepth);
240 printf(" shift: %d-%d %d-%d %d-%d\n",
241 it->red_invprec,it->red_shift,
242 it->green_invprec,it->green_shift,
243 it->blue_invprec,it->blue_shift);
244 printf(" size: %d %d %d %d xrepl=%d\n",
245 it->usewidth, it->useheight,
246 it->screen_xo, it->screen_yo, it->xrepl);
248 printf(" ANALOGTV_V=%d\n",ANALOGTV_V);
249 printf(" ANALOGTV_TOP=%d\n",ANALOGTV_TOP);
250 printf(" ANALOGTV_VISLINES=%d\n",ANALOGTV_VISLINES);
251 printf(" ANALOGTV_BOT=%d\n",ANALOGTV_BOT);
252 printf(" ANALOGTV_H=%d\n",ANALOGTV_H);
253 printf(" ANALOGTV_SYNC_START=%d\n",ANALOGTV_SYNC_START);
254 printf(" ANALOGTV_BP_START=%d\n",ANALOGTV_BP_START);
255 printf(" ANALOGTV_CB_START=%d\n",ANALOGTV_CB_START);
256 printf(" ANALOGTV_PIC_START=%d\n",ANALOGTV_PIC_START);
257 printf(" ANALOGTV_PIC_LEN=%d\n",ANALOGTV_PIC_LEN);
258 printf(" ANALOGTV_FP_START=%d\n",ANALOGTV_FP_START);
259 printf(" ANALOGTV_PIC_END=%d\n",ANALOGTV_PIC_END);
260 printf(" ANALOGTV_HASHNOISE_LEN=%d\n",ANALOGTV_HASHNOISE_LEN);
266 extern Bool mono_p; /* shoot me */
269 analogtv_free_image(analogtv *it)
272 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
278 analogtv_alloc_image(analogtv *it)
280 /* On failure, it->image is NULL. */
282 unsigned bits_per_pixel = visual_pixmap_depth(it->screen, it->xgwa.visual);
283 unsigned align = thread_memory_alignment(it->dpy) * 8 - 1;
284 /* Width is in bits. */
285 unsigned width = (it->usewidth * bits_per_pixel + align) & ~align;
287 it->image=create_xshm_image(it->dpy, it->xgwa.visual, it->xgwa.depth,
288 ZPixmap, &it->shm_info,
289 width / bits_per_pixel, it->useheight);
292 memset (it->image->data, 0, it->image->height * it->image->bytes_per_line);
294 /* Not enough memory. Maybe try a smaller window. */
295 fprintf(stderr, "analogtv: %s\n", strerror(ENOMEM));
301 analogtv_configure(analogtv *it)
303 int oldwidth=it->usewidth;
304 int oldheight=it->useheight;
305 int wlim,hlim,height_diff;
307 /* If the window is very small, don't let the image we draw get lower
308 than the actual TV resolution (266x200.)
310 If the aspect ratio of the window is close to a 4:3 or 16:9 ratio --
311 or if it is a completely weird aspect ratio --
312 then scale the image to exactly fill the window.
314 Otherwise, center the image either horizontally or vertically,
315 letterboxing or pillarboxing (but not both).
317 If it's very close (2.5%) to a multiple of VISLINES, make it exact
318 For example, it maps 1024 => 1000.
320 float percent = 0.15;
321 float min_ratio = 4.0 / 3.0 * (1 - percent);
322 float max_ratio = 16.0 / 9.0 * (1 + percent);
323 float crazy_min_ratio = 10;
324 float crazy_max_ratio = 1/crazy_min_ratio;
326 float height_snap=0.025;
328 hlim = it->xgwa.height;
329 wlim = it->xgwa.width;
330 ratio = wlim / (float) hlim;
333 /* Fill the whole iPhone screen, even though that distorts the image. */
338 if (wlim < 266 || hlim < 200)
344 "size: minimal: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
345 wlim, hlim, it->xgwa.width, it->xgwa.height,
346 min_ratio, ratio, max_ratio);
349 else if (ratio > min_ratio && ratio < max_ratio)
353 "size: close enough: %dx%d (%.3f < %.3f < %.3f)\n",
354 wlim, hlim, min_ratio, ratio, max_ratio);
357 else if (ratio >= max_ratio)
359 wlim = hlim*max_ratio;
362 "size: center H: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
363 wlim, hlim, it->xgwa.width, it->xgwa.height,
364 min_ratio, ratio, max_ratio);
367 else /* ratio <= min_ratio */
369 hlim = wlim/min_ratio;
372 "size: center V: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
373 wlim, hlim, it->xgwa.width, it->xgwa.height,
374 min_ratio, ratio, max_ratio);
378 if (ratio < crazy_min_ratio || ratio > crazy_max_ratio)
380 if (ratio < crazy_min_ratio)
381 hlim = it->xgwa.height;
383 wlim = it->xgwa.width;
386 "size: aspect: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
387 wlim, hlim, it->xgwa.width, it->xgwa.height,
388 min_ratio, ratio, max_ratio);
393 height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
394 if (height_diff != 0 && abs(height_diff) < hlim * height_snap)
400 /* Most times this doesn't change */
401 if (wlim != oldwidth || hlim != oldheight) {
406 it->xrepl=1+it->usewidth/640;
407 if (it->xrepl>2) it->xrepl=2;
408 it->subwidth=it->usewidth/it->xrepl;
410 analogtv_free_image(it);
411 analogtv_alloc_image(it);
414 it->screen_xo = (it->xgwa.width-it->usewidth)/2;
415 it->screen_yo = (it->xgwa.height-it->useheight)/2;
420 analogtv_reconfigure(analogtv *it)
422 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
423 analogtv_configure(it);
426 /* Can be any power-of-two <= 32. 16 a slightly better choice for 2-3 threads. */
427 #define ANALOGTV_SUBTOTAL_LEN 32
429 typedef struct analogtv_thread_s
433 size_t signal_start, signal_end;
436 #define SIGNAL_OFFSET(thread_id) \
437 ((ANALOGTV_SIGNAL_LEN * (thread_id) / threads->count) & align)
439 static int analogtv_thread_create(void *thread_raw, struct threadpool *threads,
442 analogtv_thread *thread = (analogtv_thread *)thread_raw;
445 thread->it = GET_PARENT_OBJ(analogtv, threads, threads);
446 thread->thread_id = thread_id;
448 align = thread_memory_alignment(thread->it->dpy) /
449 sizeof(thread->it->signal_subtotals[0]);
452 align = ~(align * ANALOGTV_SUBTOTAL_LEN - 1);
454 thread->signal_start = SIGNAL_OFFSET(thread_id);
455 thread->signal_end = thread_id + 1 == threads->count ?
456 ANALOGTV_SIGNAL_LEN :
457 SIGNAL_OFFSET(thread_id + 1);
462 static void analogtv_thread_destroy(void *thread_raw)
467 analogtv_allocate(Display *dpy, Window window)
469 static const struct threadpool_class cls = {
470 sizeof(analogtv_thread),
471 analogtv_thread_create,
472 analogtv_thread_destroy
478 const size_t rx_signal_len = ANALOGTV_SIGNAL_LEN + 2*ANALOGTV_H;
482 it=(analogtv *)calloc(1,sizeof(analogtv));
486 it->signal_subtotals=NULL;
491 if (thread_malloc((void **)&it->rx_signal, dpy,
492 sizeof(it->rx_signal[0]) * rx_signal_len))
495 assert(!(ANALOGTV_SIGNAL_LEN % ANALOGTV_SUBTOTAL_LEN));
496 if (thread_malloc((void **)&it->signal_subtotals, dpy,
497 sizeof(it->signal_subtotals[0]) *
498 (rx_signal_len / ANALOGTV_SUBTOTAL_LEN)))
501 if (threadpool_create(&it->threads, &cls, dpy, hardware_concurrency(dpy)))
504 assert(it->threads.count);
510 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
512 it->screen=it->xgwa.screen;
513 it->colormap=it->xgwa.colormap;
514 it->visclass=visual_class(it->xgwa.screen, it->xgwa.visual);
515 it->visdepth=it->xgwa.depth;
516 if (it->visclass == TrueColor || it->visclass == DirectColor) {
517 if (get_integer_resource (it->dpy, "use_cmap", "Integer")) {
522 it->use_color=!mono_p;
524 else if (it->visclass == PseudoColor || it->visclass == StaticColor) {
526 it->use_color=!mono_p;
533 visual_rgb_masks (it->xgwa.screen, it->xgwa.visual,
534 &it->red_mask, &it->green_mask, &it->blue_mask);
535 it->red_shift=it->red_invprec=-1;
536 it->green_shift=it->green_invprec=-1;
537 it->blue_shift=it->blue_invprec=-1;
539 /* Is there a standard way to do this? Does this handle all cases? */
541 for (shift=0; shift<32; shift++) {
542 for (prec=1; prec<16 && prec<40-shift; prec++) {
543 unsigned long mask=(0xffffUL>>(16-prec)) << shift;
544 if (it->red_shift<0 && mask==it->red_mask)
545 it->red_shift=shift, it->red_invprec=16-prec;
546 if (it->green_shift<0 && mask==it->green_mask)
547 it->green_shift=shift, it->green_invprec=16-prec;
548 if (it->blue_shift<0 && mask==it->blue_mask)
549 it->blue_shift=shift, it->blue_invprec=16-prec;
552 if (it->red_shift<0 || it->green_shift<0 || it->blue_shift<0) {
553 if (0) fprintf(stderr,"Can't figure out color space\n");
557 for (i=0; i<ANALOGTV_CV_MAX; i++) {
558 int intensity=pow(i/256.0, 0.8)*65535.0; /* gamma correction */
559 if (intensity>65535) intensity=65535;
560 it->red_values[i]=((intensity>>it->red_invprec)<<it->red_shift);
561 it->green_values[i]=((intensity>>it->green_invprec)<<it->green_shift);
562 it->blue_values[i]=((intensity>>it->blue_invprec)<<it->blue_shift);
567 gcv.background=get_pixel_resource(it->dpy, it->colormap,
568 "background", "Background");
570 it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
571 XSetWindowBackground(it->dpy, it->window, gcv.background);
572 XClearWindow(dpy,window);
574 analogtv_configure(it);
580 if(it->threads.count)
581 threadpool_destroy(&it->threads);
582 thread_free(it->signal_subtotals);
583 thread_free(it->rx_signal);
590 analogtv_release(analogtv *it)
593 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
596 if (it->gc) XFreeGC(it->dpy, it->gc);
598 if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
600 threadpool_destroy(&it->threads);
601 thread_free(it->rx_signal);
602 thread_free(it->signal_subtotals);
608 First generate the I and Q reference signals, which we'll multiply
609 the input signal by to accomplish the demodulation. Normally they
610 are shifted 33 degrees from the colorburst. I think this was convenient
611 for inductor-capacitor-vacuum tube implementation.
613 The tint control, FWIW, just adds a phase shift to the chroma signal,
614 and the color control controls the amplitude.
616 In text modes (colormode==0) the system disabled the color burst, and no
617 color was detected by the monitor.
619 freq_error gives a mismatch between the built-in oscillator and the
620 TV's colorbust. Some II Plus machines seemed to occasionally get
621 instability problems -- the crystal oscillator was a single
622 transistor if I remember correctly -- and the frequency would vary
623 enough that the tint would change across the width of the screen.
624 The left side would be in correct tint because it had just gotten
625 resynchronized with the color burst.
627 If we're using a colormap, set it up.
630 analogtv_set_demod(analogtv *it)
632 int y_levels=10,i_levels=5,q_levels=5;
635 In principle, we might be able to figure out how to adjust the
636 color map frame-by-frame to get some nice color bummage. But I'm
637 terrified of changing the color map because we'll get flashing.
639 I can hardly believe we still have to deal with colormaps. They're
640 like having NEAR PTRs: an enormous hassle for the programmer just
641 to save on memory. They should have been deprecated by 1995 or
645 if (it->use_cmap && !it->n_colors) {
648 XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
654 for (yli=0; yli<y_levels; yli++) {
655 for (ili=0; ili<i_levels; ili++) {
656 for (qli=0; qli<q_levels; qli++) {
657 double interpy,interpi,interpq;
658 double levelmult=700.0;
662 interpy=100.0 * ((double)yli/y_levels);
663 interpi=50.0 * (((double)ili-(0.5*i_levels))/(double)i_levels);
664 interpq=50.0 * (((double)qli-(0.5*q_levels))/(double)q_levels);
666 r=(int)((interpy + 1.04*interpi + 0.624*interpq)*levelmult);
667 g=(int)((interpy - 0.276*interpi - 0.639*interpq)*levelmult);
668 b=(int)((interpy - 1.105*interpi + 1.729*interpq)*levelmult);
670 if (r>65535) r=65535;
672 if (g>65535) g=65535;
674 if (b>65535) b=65535;
677 printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n",
678 interpy, interpi, interpq,
686 if (!XAllocColor(it->dpy, it->colormap, &col)) {
687 if (q_levels > y_levels*4/12)
689 else if (i_levels > y_levels*5/12)
698 it->colors[it->n_colors++]=col.pixel;
703 it->cmap_y_levels=y_levels;
704 it->cmap_i_levels=i_levels;
705 it->cmap_q_levels=q_levels;
714 analogtv_line_signature(analogtv_input *input, int lineno)
717 char *origsignal=&input->signal[(lineno+input->vsync)
718 %ANALOGTV_V][input->line_hsync[lineno]];
722 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
724 hash = hash + (hash<<17) + c;
727 hash += input->line_hsync[lineno];
730 hash += input->hashnoise_times[lineno];
739 /* Here we model the analog circuitry of an NTSC television.
740 Basically, it splits the signal into 3 signals: Y, I and Q. Y
741 corresponds to luminance, and you get it by low-pass filtering the
742 input signal to below 3.57 MHz.
744 I and Q are the in-phase and quadrature components of the 3.57 MHz
745 subcarrier. We get them by multiplying by cos(3.57 MHz*t) and
746 sin(3.57 MHz*t), and low-pass filtering. Because the eye has less
747 resolution in some colors than others, the I component gets
748 low-pass filtered at 1.5 MHz and the Q at 0.5 MHz. The I component
749 is approximately orange-blue, and Q is roughly purple-green. See
750 http://www.ntsc-tv.com for details.
752 We actually do an awful lot to the signal here. I suspect it would
753 make sense to wrap them all up together by calculating impulse
754 response and doing FFT convolutions.
759 analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
760 int start, int end, struct analogtv_yiq_s *it_yiq)
765 int phasecorr=(signal-it->rx_signal)&3;
766 struct analogtv_yiq_s *yiq;
768 float agclevel=it->agclevel;
769 float brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
770 float delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
775 double cb_i=(it->line_cb_phase[lineno][(2+phasecorr)&3]-
776 it->line_cb_phase[lineno][(0+phasecorr)&3])/16.0;
777 double cb_q=(it->line_cb_phase[lineno][(3+phasecorr)&3]-
778 it->line_cb_phase[lineno][(1+phasecorr)&3])/16.0;
780 colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
783 multiq2[0] = (cb_i*it->tint_i - cb_q*it->tint_q) * it->color_control;
784 multiq2[1] = (cb_q*it->tint_i + cb_i*it->tint_q) * it->color_control;
785 multiq2[2]=-multiq2[0];
786 multiq2[3]=-multiq2[1];
792 printf("multiq = [%0.3f %0.3f %0.3f %0.3f] ",
793 it->multiq[60],it->multiq[61],it->multiq[62],it->multiq[63]);
794 printf("it->line_cb_phase = [%0.3f %0.3f %0.3f %0.3f]\n",
795 it->line_cb_phase[lineno][0],it->line_cb_phase[lineno][1],
796 it->line_cb_phase[lineno][2],it->line_cb_phase[lineno][3]);
797 printf("multiq2 = [%0.3f %0.3f %0.3f %0.3f]\n",
798 multiq2[0],multiq2[1],multiq2[2],multiq2[3]);
802 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
803 for (i=0; i<5; i++) dp[i]=0.0f;
806 assert(end < ANALOGTV_PIC_LEN+10);
808 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
809 for (i=0; i<24; i++) dp[i]=0.0;
810 for (i=start, yiq=it_yiq+start, sp=signal+start;
812 i++, dp--, yiq++, sp++) {
814 /* Now filter them. These are infinite impulse response filters
815 calculated by the script at
816 http://www-users.cs.york.ac.uk/~fisher/mkfilter. This is
817 fixed-point integer DSP, son. No place for wimps. We do it in
818 integer because you can count on integer being faster on most
819 CPUs. We care about speed because we need to recalculate every
820 time we blink text, and when we spew random bytes into screen
821 memory. This is roughly 16.16 fixed point arithmetic, but we
822 scale some filter values up by a few bits to avoid some nasty
825 /* Filter Y with a 4-pole low-pass Butterworth filter at 3.5 MHz
826 with an extra zero at 3.5 MHz, from
827 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
830 dp[0] = sp[0] * 0.0469904257251935f * agclevel;
831 dp[8] = (+1.0f*(dp[6]+dp[0])
837 yiq->y = dp[8] + brightadd;
841 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
842 for (i=0; i<27; i++) dp[i]=0.0;
844 for (i=start, yiq=it_yiq+start, sp=signal+start;
846 i++, dp--, yiq++, sp++) {
849 /* Filter I and Q with a 3-pole low-pass Butterworth filter at
850 1.5 MHz with an extra zero at 3.5 MHz, from
851 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 -Z 2.5000000000e-01 -l
855 dp[0] = sig*multiq2[i&3] * 0.0833333333333f;
856 yiq->i=dp[8] = (dp[5] + dp[0]
857 +3.0f*(dp[4] + dp[1])
858 +4.0f*(dp[3] + dp[2])
859 -0.3333333333f * dp[10]);
861 dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333f;
862 yiq->q=dp[24] = (dp[16+5] + dp[16+0]
863 +3.0f*(dp[16+4] + dp[16+1])
864 +4.0f*(dp[16+3] + dp[16+2])
865 -0.3333333333f * dp[24+2]);
868 for (i=start, yiq=it_yiq+start; i<end; i++, yiq++) {
869 yiq->i = yiq->q = 0.0f;
875 analogtv_setup_teletext(analogtv_input *input)
878 int teletext=ANALOGTV_BLACK_LEVEL;
880 /* Teletext goes in line 21. But I suspect there are other things
881 in the vertical retrace interval */
883 for (y=19; y<22; y++) {
884 for (x=ANALOGTV_PIC_START; x<ANALOGTV_PIC_END; x++) {
886 teletext=(random()&1) ? ANALOGTV_WHITE_LEVEL : ANALOGTV_BLACK_LEVEL;
888 input->signal[y][x]=teletext;
894 analogtv_setup_frame(analogtv *it)
900 if (it->flutter_horiz_desync) {
901 /* Horizontal sync during vertical sync instability. */
902 it->horiz_desync += -0.10*(it->horiz_desync-3.0) +
903 ((int)(random()&0xff)-0x80) *
904 ((int)(random()&0xff)-0x80) *
905 ((int)(random()&0xff)-0x80) * 0.000001;
909 for (i=0; i<ANALOGTV_V; i++) {
910 it->hashnoise_times[i]=0;
914 /* let's leave it to process shrinkpulse */
915 if (it->hashnoise_enable && !it->hashnoise_on) {
916 if (random()%10000==0) {
918 it->shrinkpulse=random()%ANALOGTV_V;
921 if (random()%1000==0) {
925 #if 0 /* never used */
926 if (it->hashnoise_on) {
927 it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
928 ((int)(random()%2000)-1000)*0.1;
930 it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
931 if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
933 if (it->hashnoise_rpm > 0.0) {
936 int hnc=it->hashnoise_counter; /* in 24.8 format */
938 /* Convert rpm of a 16-pole motor into dots in 24.8 format */
939 hni_double = ANALOGTV_V * ANALOGTV_H * 256.0 /
940 (it->hashnoise_rpm * 16.0 / 60.0 / 60.0);
941 hni = (hni_double <= INT_MAX) ? (int)hni_double : INT_MAX;
943 while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
944 y=(hnc>>8)/ANALOGTV_H;
945 x=(hnc>>8)%ANALOGTV_H;
947 if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
948 it->hashnoise_times[y]=x;
950 /* hnc += hni + (int)(random()%65536)-32768; */
952 hnc += (int)(random()%65536)-32768;
953 if ((hnc >= 0) && (INT_MAX - hnc < hni)) break;
960 /* hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;*/
963 if (it->rx_signal_level != 0.0)
964 it->agclevel = 1.0/it->rx_signal_level;
969 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
970 printf(" %0.3f",it->ghostfir[i]);
972 printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel);
977 analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
982 int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
984 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
985 vsync=lineno>=3 && lineno<7;
987 sig=input->signal[lineno];
989 i=ANALOGTV_SYNC_START;
991 while (i<ANALOGTV_BP_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
992 while (i<ANALOGTV_H) sig[i++]=synclevel;
994 while (i<ANALOGTV_BP_START) sig[i++]=synclevel;
995 while (i<ANALOGTV_PIC_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
996 while (i<ANALOGTV_FP_START) sig[i++]=ANALOGTV_BLACK_LEVEL;
998 while (i<ANALOGTV_H) sig[i++]=ANALOGTV_BLANK_LEVEL;
1001 /* 9 cycles of colorburst */
1002 for (i=ANALOGTV_CB_START; i<ANALOGTV_CB_START+36; i+=4) {
1003 sig[i+1] += ANALOGTV_CB_LEVEL;
1004 sig[i+3] -= ANALOGTV_CB_LEVEL;
1011 analogtv_sync(analogtv *it)
1013 int cur_hsync=it->cur_hsync;
1014 int cur_vsync=it->cur_vsync;
1019 float cbfc=1.0f/128.0f;
1021 /* sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;*/
1022 for (i=-32; i<32; i++) {
1023 lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1024 sp = it->rx_signal + lineno*ANALOGTV_H;
1026 for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/16) {
1029 filt *= it->agclevel;
1031 osc = (float)(ANALOGTV_V+i)/(float)ANALOGTV_V;
1033 if (osc >= 1.05f+0.0002f * filt) break;
1035 cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1037 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
1039 if (lineno>5 && lineno<ANALOGTV_V-3) { /* ignore vsync interval */
1040 unsigned lineno2 = (lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V;
1041 if (!lineno2) lineno2 = ANALOGTV_V;
1042 sp = it->rx_signal + lineno2*ANALOGTV_H + cur_hsync;
1043 for (i=-8; i<8; i++) {
1044 osc = (float)(ANALOGTV_H+i)/(float)ANALOGTV_H;
1045 filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
1047 if (osc >= 1.005f + 0.0001f*filt) break;
1049 cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
1052 it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START +
1053 ANALOGTV_H) % ANALOGTV_H;
1055 /* Now look for the colorburst, which is a few cycles after the H
1056 sync pulse, and store its phase.
1057 The colorburst is 9 cycles long, and we look at the middle 5
1062 sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
1063 for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; i++) {
1064 it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0f-cbfc) +
1065 sp[i]*it->agclevel*cbfc;
1073 for (i=0; i<4; i++) {
1074 tot += it->cb_phase[i]*it->cb_phase[i];
1076 cbgain = 32.0f/sqrtf(tot);
1078 for (i=0; i<4; i++) {
1079 it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
1084 if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n",
1086 it->cb_phase[0], it->cb_phase[1],
1087 it->cb_phase[2], it->cb_phase[3]);
1090 /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */
1093 it->cur_hsync = cur_hsync;
1094 it->cur_vsync = cur_vsync;
1098 analogtv_levelmult(const analogtv *it, int level)
1100 static const double levelfac[3]={-7.5, 5.5, 24.5};
1101 return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
1105 analogtv_level(const analogtv *it, int y, int ytop, int ybot)
1109 if (y==ytop || y==ybot-1) level=0;
1110 else if (y==ytop+1 || y==ybot-2) level=1;
1113 else if (ybot-ytop>=5) {
1114 if (y==ytop || y==ybot-1) level=0;
1117 else if (ybot-ytop>=3) {
1118 if (y==ytop) level=0;
1128 The point of this stuff is to ensure that when useheight is not a
1129 multiple of VISLINES so that TV scan lines map to different numbers
1130 of vertical screen pixels, the total brightness of each scan line
1132 ANALOGTV_MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
1133 it interpolates extra black lines.
1137 analogtv_setup_levels(analogtv *it, double avgheight)
1140 static const double levelfac[3]={-7.5, 5.5, 24.5};
1142 for (height=0; height<avgheight+2.0 && height<=ANALOGTV_MAX_LINEHEIGHT; height++) {
1144 for (i=0; i<height; i++) {
1145 it->leveltable[height][i].index = 2;
1149 it->leveltable[height][0].index=0;
1152 if (height >= 1) it->leveltable[height][height-1].index=0;
1155 it->leveltable[height][1].index=1;
1156 if (height >= 2) it->leveltable[height][height-2].index=1;
1159 for (i=0; i<height; i++) {
1160 it->leveltable[height][i].value =
1161 (40.0 + levelfac[it->leveltable[height][i].index]*puramp(it, 3.0, 6.0, 1.0)) / 256.0;
1167 static void rnd_combine(unsigned *a0, unsigned *c0, unsigned a1, unsigned c1)
1169 *a0 = (*a0 * a1) & 0xffffffffu;
1170 *c0 = (c1 + a1 * *c0) & 0xffffffffu;
1173 static void rnd_seek_ac(unsigned *a, unsigned *c, unsigned dist)
1175 unsigned int a1 = *a, c1 = *c;
1181 rnd_combine(a, c, a1, c1);
1183 rnd_combine(&a1, &c1, a1, c1);
1187 static unsigned int rnd_seek(unsigned a, unsigned c, unsigned rnd, unsigned dist)
1189 rnd_seek_ac(&a, &c, dist);
1193 static void analogtv_init_signal(const analogtv *it, double noiselevel, unsigned start, unsigned end)
1195 float *ps=it->rx_signal + start;
1196 float *pe=it->rx_signal + end;
1198 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random0, start);
1199 unsigned int fastrnd_offset;
1201 float noisemul = sqrt(noiselevel*150)/(float)0x7fffffff;
1203 fastrnd_offset = fastrnd - 0x7fffffff;
1204 nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
1207 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1208 fastrnd_offset = fastrnd - 0x7fffffff;
1209 nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
1214 static void analogtv_add_signal(const analogtv *it, const analogtv_reception *rec, unsigned start, unsigned end, int ec)
1216 analogtv_input *inp=rec->input;
1217 float *ps=it->rx_signal + start;
1218 float *pe=it->rx_signal + end;
1220 signed char *ss=&inp->signal[0][0];
1221 signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
1222 signed char *s=ss + ((start + (unsigned)rec->ofs) % ANALOGTV_SIGNAL_LEN);
1225 float level=rec->level;
1226 float hfloss=rec->hfloss;
1227 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random1, start);
1230 const float noise_decay = 0.99995f;
1231 float noise_ampl = 1.3f * powf(noise_decay, start);
1236 /* assert((se-ss)%4==0 && (se-s)%4==0); */
1238 for (i = start; i < ec; i++) { /* Sometimes start > ec. */
1240 /* Do a big noisy transition. We can make the transition noise of
1241 high constant strength regardless of signal strength.
1243 There are two separate state machines. here, One is the noise
1244 process and the other is the
1246 We don't bother with the FIR filter here
1249 float sig0=(float)s[0];
1250 unsigned int fastrnd_offset = fastrnd - 0x7fffffff;
1251 float noise = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * (50.0f/(float)0x7fffffff);
1252 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1254 p[0] += sig0 * level * (1.0f - noise_ampl) + noise * noise_ampl;
1256 noise_ampl *= noise_decay;
1265 for (i=1; i<5; i++) {
1268 s2 += ANALOGTV_SIGNAL_LEN;
1269 dp[i] = (float)((int)s2[0]+(int)s2[1]+(int)s2[2]+(int)s2[3]);
1273 assert(!((pe - p) % 4));
1275 float sig0,sig1,sig2,sig3,sigr;
1282 dp[0]=sig0+sig1+sig2+sig3;
1284 /* Get the video out signal, and add some ghosting, typical of RF
1285 monitor cables. This corresponds to a pretty long cable, but
1289 sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] +
1290 dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]);
1291 dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0];
1293 p[0] += (sig0+sigr + sig2*hfloss) * level;
1294 p[1] += (sig1+sigr + sig3*hfloss) * level;
1295 p[2] += (sig2+sigr + sig0*hfloss) * level;
1296 p[3] += (sig3+sigr + sig1*hfloss) * level;
1300 if (s>=se) s = ss + (s-se);
1306 static void analogtv_thread_add_signals(void *thread_raw)
1308 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1309 const analogtv *it = thread->it;
1311 unsigned subtotal_end;
1313 unsigned start = thread->signal_start;
1314 while(start != thread->signal_end)
1318 /* Work on 8 KB blocks; these should fit in L1. */
1319 /* (Though it doesn't seem to help much on my system.) */
1320 unsigned end = start + 2048;
1321 if(end > thread->signal_end)
1322 end = thread->signal_end;
1324 analogtv_init_signal (it, it->noiselevel, start, end);
1326 for (i = 0; i != it->rec_count; ++i) {
1327 analogtv_add_signal (it, it->recs[i], start, end,
1328 !i ? it->channel_change_cycles : 0);
1331 assert (!(start % ANALOGTV_SUBTOTAL_LEN));
1332 assert (!(end % ANALOGTV_SUBTOTAL_LEN));
1334 p = it->rx_signal + start;
1335 subtotal_end = end / ANALOGTV_SUBTOTAL_LEN;
1336 for (i = start / ANALOGTV_SUBTOTAL_LEN; i != subtotal_end; ++i) {
1338 for (j = 1; j != ANALOGTV_SUBTOTAL_LEN; ++j)
1340 it->signal_subtotals[i] = sum;
1341 p += ANALOGTV_SUBTOTAL_LEN;
1348 static int analogtv_get_line(const analogtv *it, int lineno, int *slineno,
1349 int *ytop, int *ybot, unsigned *signal_offset)
1351 *slineno=lineno-ANALOGTV_TOP;
1352 *ytop=(int)((*slineno*it->useheight/ANALOGTV_VISLINES -
1353 it->useheight/2)*it->puheight) + it->useheight/2;
1354 *ybot=(int)(((*slineno+1)*it->useheight/ANALOGTV_VISLINES -
1355 it->useheight/2)*it->puheight) + it->useheight/2;
1357 int linesig=analogtv_line_signature(input,lineno)
1358 + it->hashnoise_times[lineno];
1360 *signal_offset = ((lineno+it->cur_vsync+ANALOGTV_V) % ANALOGTV_V) * ANALOGTV_H +
1361 it->line_hsync[lineno];
1363 if (*ytop==*ybot) return 0;
1364 if (*ybot<0 || *ytop>it->useheight) return 0;
1365 if (*ytop<0) *ytop=0;
1366 if (*ybot>it->useheight) *ybot=it->useheight;
1368 if (*ybot > *ytop+ANALOGTV_MAX_LINEHEIGHT) *ybot=*ytop+ANALOGTV_MAX_LINEHEIGHT;
1373 analogtv_blast_imagerow(const analogtv *it,
1374 float *rgbf, float *rgbf_end,
1379 char *level_copyfrom[3];
1380 int xrepl=it->xrepl;
1381 unsigned lineheight = ybot - ytop;
1382 if (lineheight > ANALOGTV_MAX_LINEHEIGHT) lineheight = ANALOGTV_MAX_LINEHEIGHT;
1383 for (i=0; i<3; i++) level_copyfrom[i]=NULL;
1385 for (y=ytop; y<ybot; y++) {
1386 char *rowdata=it->image->data + y*it->image->bytes_per_line;
1387 unsigned line = y-ytop;
1389 int level=it->leveltable[lineheight][line].index;
1390 float levelmult=it->leveltable[lineheight][line].value;
1392 /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
1393 why standard graphics sw has to be fast, or else people will have to
1394 work around it and risk incompatibility. The quickdraw folks
1395 understood this. The other answer would be for X11 to have fewer
1396 formats for bitm.. oh, never mind. If neither of these cases work
1397 (they probably cover 99% of setups) it falls back on the Xlib
1400 if (level_copyfrom[level]) {
1401 memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line);
1404 level_copyfrom[level] = rowdata;
1408 else if (it->image->format==ZPixmap &&
1409 it->image->bits_per_pixel==32 &&
1410 sizeof(unsigned int)==4 &&
1411 it->image->byte_order==localbyteorder) {
1412 /* int is more likely to be 32 bits than long */
1413 unsigned int *pixelptr=(unsigned int *)rowdata;
1416 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1417 int ntscri=rpf[0]*levelmult;
1418 int ntscgi=rpf[1]*levelmult;
1419 int ntscbi=rpf[2]*levelmult;
1420 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1421 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1422 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1423 pix = (it->red_values[ntscri] |
1424 it->green_values[ntscgi] |
1425 it->blue_values[ntscbi]);
1429 if (xrepl>=3) pixelptr[2] = pix;
1434 else if (it->image->format==ZPixmap &&
1435 it->image->bits_per_pixel==16 &&
1436 sizeof(unsigned short)==2 &&
1437 float_extraction_works &&
1438 it->image->byte_order==localbyteorder) {
1439 unsigned short *pixelptr=(unsigned short *)rowdata;
1441 float_extract_t r1,g1,b1;
1444 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1445 r2=rpf[0]; g2=rpf[1]; b2=rpf[2];
1446 r1.f=r2 * levelmult+float_low8_ofs;
1447 g1.f=g2 * levelmult+float_low8_ofs;
1448 b1.f=b2 * levelmult+float_low8_ofs;
1449 pix = (it->red_values[r1.i & 0x3ff] |
1450 it->green_values[g1.i & 0x3ff] |
1451 it->blue_values[b1.i & 0x3ff]);
1455 if (xrepl>=3) pixelptr[2] = pix;
1460 else if (it->image->format==ZPixmap &&
1461 it->image->bits_per_pixel==16 &&
1462 sizeof(unsigned short)==2 &&
1463 it->image->byte_order==localbyteorder) {
1464 unsigned short *pixelptr=(unsigned short *)rowdata;
1467 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1468 int r1=rpf[0] * levelmult;
1469 int g1=rpf[1] * levelmult;
1470 int b1=rpf[2] * levelmult;
1471 if (r1>=ANALOGTV_CV_MAX) r1=ANALOGTV_CV_MAX-1;
1472 if (g1>=ANALOGTV_CV_MAX) g1=ANALOGTV_CV_MAX-1;
1473 if (b1>=ANALOGTV_CV_MAX) b1=ANALOGTV_CV_MAX-1;
1474 pix = it->red_values[r1] | it->green_values[g1] | it->blue_values[b1];
1478 if (xrepl>=3) pixelptr[2] = pix;
1484 for (x=0, rpf=rgbf; rpf!=rgbf_end ; x++, rpf+=3) {
1485 int ntscri=rpf[0]*levelmult;
1486 int ntscgi=rpf[1]*levelmult;
1487 int ntscbi=rpf[2]*levelmult;
1488 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1489 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1490 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1491 for (j=0; j<xrepl; j++) {
1492 XPutPixel(it->image, x*xrepl + j, y,
1493 it->red_values[ntscri] | it->green_values[ntscgi] |
1494 it->blue_values[ntscbi]);
1502 static void analogtv_thread_draw_lines(void *thread_raw)
1504 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1505 const analogtv *it = thread->it;
1509 float *raw_rgb_start;
1511 raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
1513 if (! raw_rgb_start) return;
1515 raw_rgb_end=raw_rgb_start+3*it->subwidth;
1517 for (lineno=ANALOGTV_TOP + thread->thread_id;
1518 lineno<ANALOGTV_BOT;
1519 lineno += it->threads.count) {
1522 int slineno, ytop, ybot;
1523 unsigned signal_offset;
1525 const float *signal;
1527 int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
1528 float *rgb_start, *rgb_end;
1534 struct analogtv_yiq_s yiq[ANALOGTV_PIC_LEN+10];
1536 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot,
1540 signal = it->rx_signal + signal_offset;
1544 float bloomthisrow,shiftthisrow;
1545 float viswidth,middle;
1549 bloomthisrow = -10.0f * it->crtload[lineno];
1550 if (bloomthisrow<-10.0f) bloomthisrow=-10.0f;
1551 if (bloomthisrow>2.0f) bloomthisrow=2.0f;
1553 shiftthisrow=it->horiz_desync * (expf(-0.17f*slineno) *
1554 (0.7f+cosf(slineno*0.6f)));
1559 viswidth=ANALOGTV_PIC_LEN * 0.79f - 5.0f*bloomthisrow;
1560 middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
1562 scanwidth=it->width_control * puramp(it, 0.5f, 0.3f, 1.0f);
1564 scw=it->subwidth*scanwidth;
1565 if (scw>it->subwidth) scw=it->usewidth;
1566 scl=it->subwidth/2 - scw/2;
1567 scr=it->subwidth/2 + scw/2;
1569 pixrate=(int)((viswidth*65536.0f*1.0f)/it->subwidth)/scanwidth;
1570 scanstart_i=(int)((middle-viswidth*0.5f)*65536.0f);
1571 scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
1572 squishright_i=(int)((middle+viswidth*(0.25f + 0.25f*puramp(it, 2.0f, 0.0f, 1.1f)
1573 - it->squish_control)) *65536.0f);
1574 squishdiv=it->subwidth/15;
1576 rgb_start=raw_rgb_start+scl*3;
1577 rgb_end=raw_rgb_start+scr*3;
1579 assert(scanstart_i>=0);
1582 if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
1584 scanstart_i/65536.0f,
1585 squishright_i/65536.0f,
1592 for (y=ytop; y<ybot; y++) {
1593 int level=analogtv_level(it, y, ytop, ybot);
1594 float levelmult=analogtv_levelmult(it, level);
1595 float levelmult_y = levelmult * it->contrast_control
1596 * puramp(it, 1.0f, 0.0f, 1.0f) / (0.5f+0.5f*it->puheight) * 0.070f;
1597 float levelmult_iq = levelmult * 0.090f;
1599 analogtv_ntsc_to_yiq(it, lineno, signal,
1600 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1605 while (i<0 && x<it->usewidth) {
1606 XPutPixel(it->image, x, y, it->colors[0]);
1611 while (i<scanend_i && x<it->usewidth) {
1612 float pixfrac=(i&0xffff)/65536.0f;
1613 float invpixfrac=(1.0f-pixfrac);
1615 int yli,ili,qli,cmi;
1617 float interpy=(yiq[pati].y*invpixfrac
1618 + yiq[pati+1].y*pixfrac) * levelmult_y;
1619 float interpi=(yiq[pati].i*invpixfrac
1620 + yiq[pati+1].i*pixfrac) * levelmult_iq;
1621 float interpq=(yiq[pati].q*invpixfrac
1622 + yiq[pati+1].q*pixfrac) * levelmult_iq;
1624 yli = (int)(interpy * it->cmap_y_levels);
1625 ili = (int)((interpi+0.5f) * it->cmap_i_levels);
1626 qli = (int)((interpq+0.5f) * it->cmap_q_levels);
1628 if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
1630 if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1;
1632 if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1;
1634 cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli);
1637 if ((random()%65536)==0) {
1638 printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n",
1639 interpy, interpi, interpq,
1645 for (j=0; j<it->xrepl; j++) {
1646 XPutPixel(it->image, x, y,
1650 if (i >= squishright_i) {
1651 pixmultinc += pixmultinc/squishdiv;
1655 while (x<it->usewidth) {
1656 XPutPixel(it->image, x, y, it->colors[0]);
1662 analogtv_ntsc_to_yiq(it, lineno, signal,
1663 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1665 pixbright=it->contrast_control * puramp(it, 1.0f, 0.0f, 1.0f)
1666 / (0.5f+0.5f*it->puheight) * 1024.0f/100.0f;
1668 i=scanstart_i; rrp=rgb_start;
1669 while (i<0 && rrp!=rgb_end) {
1670 rrp[0]=rrp[1]=rrp[2]=0;
1674 while (i<scanend_i && rrp!=rgb_end) {
1675 float pixfrac=(i&0xffff)/65536.0f;
1676 float invpixfrac=1.0f-pixfrac;
1680 float interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
1681 float interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
1682 float interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
1685 According to the NTSC spec, Y,I,Q are generated as:
1687 y=0.30 r + 0.59 g + 0.11 b
1688 i=0.60 r - 0.28 g - 0.32 b
1689 q=0.21 r - 0.52 g + 0.31 b
1691 So if you invert the implied 3x3 matrix you get what standard
1692 televisions implement with a bunch of resistors (or directly in the
1695 r = y + 0.948 i + 0.624 q
1696 g = y - 0.276 i - 0.639 q
1697 b = y - 1.105 i + 1.729 q
1700 r=(interpy + 0.948f*interpi + 0.624f*interpq) * pixbright;
1701 g=(interpy - 0.276f*interpi - 0.639f*interpq) * pixbright;
1702 b=(interpy - 1.105f*interpi + 1.729f*interpq) * pixbright;
1710 if (i>=squishright_i) {
1711 pixmultinc += pixmultinc/squishdiv;
1712 pixbright += pixbright/squishdiv/2;
1717 while (rrp != rgb_end) {
1718 rrp[0]=rrp[1]=rrp[2]=0.0f;
1722 analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end,
1727 free(raw_rgb_start);
1731 analogtv_draw(analogtv *it, double noiselevel,
1732 const analogtv_reception *const *recs, unsigned rec_count)
1735 /* int bigloadchange,drawcount;*/
1737 int overall_top, overall_bot;
1739 /* AnalogTV isn't very interesting if there isn't enough RAM. */
1743 it->rx_signal_level = noiselevel;
1744 for (i = 0; i != rec_count; ++i) {
1745 const analogtv_reception *rec = recs[i];
1746 double level = rec->level;
1747 analogtv_input *inp=rec->input;
1749 it->rx_signal_level =
1750 sqrt(it->rx_signal_level * it->rx_signal_level +
1751 (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] +
1752 rec->ghostfir[2] + rec->ghostfir[3]))));
1754 /* duplicate the first line into the Nth line to ease wraparound computation */
1755 memcpy(inp->signal[ANALOGTV_V], inp->signal[0],
1756 ANALOGTV_H * sizeof(inp->signal[0][0]));
1759 analogtv_setup_frame(it);
1760 analogtv_set_demod(it);
1762 it->random0 = random();
1763 it->random1 = random();
1764 it->noiselevel = noiselevel;
1766 it->rec_count = rec_count;
1767 threadpool_run(&it->threads, analogtv_thread_add_signals);
1768 threadpool_wait(&it->threads);
1770 it->channel_change_cycles=0;
1772 /* rx_signal has an extra 2 lines at the end, where we copy the
1773 first 2 lines so we can index into it while only worrying about
1774 wraparound on a per-line level */
1775 memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN],
1777 2*ANALOGTV_H*sizeof(it->rx_signal[0]));
1779 /* Repeat for signal_subtotals. */
1781 memcpy(&it->signal_subtotals[ANALOGTV_SIGNAL_LEN / ANALOGTV_SUBTOTAL_LEN],
1782 &it->signal_subtotals[0],
1783 (2*ANALOGTV_H/ANALOGTV_SUBTOTAL_LEN)*sizeof(it->signal_subtotals[0]));
1785 analogtv_sync(it); /* Requires the add_signals be complete. */
1788 /* if (it->hashnoise_on) baseload=0.5; */
1792 it->crtload[ANALOGTV_TOP-1]=baseload;
1793 it->puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
1794 (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
1796 analogtv_setup_levels(it, it->puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
1798 /* calculate tint once per frame */
1799 it->tint_i = -cos((103 + it->tint_control)*3.1415926/180);
1800 it->tint_q = sin((103 + it->tint_control)*3.1415926/180);
1802 for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
1803 int slineno, ytop, ybot;
1804 unsigned signal_offset;
1805 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot, &signal_offset))
1808 if (lineno==it->shrinkpulse) {
1810 /*bigloadchange=1;*/
1815 if (it->hashnoise_rpm>0.0 &&
1818 (slineno<20 && it->flutter_horiz_desync) ||
1819 it->gaussiannoise_level>30 ||
1820 ((it->gaussiannoise_level>2.0 ||
1821 it->multipath) && random()%4) ||
1822 linesig != it->onscreen_signature[lineno])) {
1825 it->onscreen_signature[lineno] = linesig;
1830 Interpolate the 600-dotclock line into however many horizontal
1831 screen pixels we're using, and convert to RGB.
1833 We add some 'bloom', variations in the horizontal scan width with
1834 the amount of brightness, extremely common on period TV sets. They
1835 had a single oscillator which generated both the horizontal scan and
1836 (during the horizontal retrace interval) the high voltage for the
1837 electron beam. More brightness meant more load on the oscillator,
1838 which caused an decrease in horizontal deflection. Look for
1841 Also, the A2 did a bad job of generating horizontal sync pulses
1842 during the vertical blanking interval. This, and the fact that the
1843 horizontal frequency was a bit off meant that TVs usually went a bit
1844 out of sync during the vertical retrace, and the top of the screen
1845 would be bent a bit to the left or right. Look for (shiftthisrow).
1847 We also add a teeny bit of left overscan, just enough to be
1848 annoying, but you can still read the left column of text.
1850 We also simulate compression & brightening on the right side of the
1851 screen. Most TVs do this, but you don't notice because they overscan
1852 so it's off the right edge of the CRT. But the A2 video system used
1853 so much of the horizontal scan line that you had to crank the
1854 horizontal width down in order to not lose the right few characters,
1855 and you'd see the compression on the right edge. Associated with
1856 compression is brightening; since the electron beam was scanning
1857 slower, the same drive signal hit the phosphor harder. Look for
1858 (squishright_i) and (squishdiv).
1862 /* This used to be an int, I suspect by mistake. - Dave */
1869 frac = signal_offset & (ANALOGTV_SUBTOTAL_LEN - 1);
1870 p = it->rx_signal + (signal_offset & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1871 for (i=0; i != frac; i++) {
1875 end0 = (signal_offset + ANALOGTV_PIC_LEN);
1877 end1 = end0 / ANALOGTV_SUBTOTAL_LEN;
1878 for (i=signal_offset / ANALOGTV_SUBTOTAL_LEN; i<end1; i++) {
1879 totsignal += it->signal_subtotals[i];
1882 frac = end0 & (ANALOGTV_SUBTOTAL_LEN - 1);
1883 p = it->rx_signal + (end0 & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1884 for (i=0; i != frac; i++) {
1888 totsignal *= it->agclevel;
1889 ncl = 0.95f * it->crtload[lineno-1] +
1891 (totsignal-30000)/100000.0f +
1892 (slineno>184 ? (slineno-184)*(lineno-184)*0.001f * it->squeezebottom
1894 /*diff=ncl - it->crtload[lineno];*/
1895 /*bigloadchange = (diff>0.01 || diff<-0.01);*/
1896 it->crtload[lineno]=ncl;
1900 threadpool_run(&it->threads, analogtv_thread_draw_lines);
1901 threadpool_wait(&it->threads);
1904 /* poor attempt at visible retrace */
1905 for (i=0; i<15; i++) {
1906 int ytop=(int)((i*it->useheight/15 -
1907 it->useheight/2)*puheight) + it->useheight/2;
1908 int ybot=(int)(((i+1)*it->useheight/15 -
1909 it->useheight/2)*puheight) + it->useheight/2;
1910 int div=it->usewidth*3/2;
1912 for (x=0; x<it->usewidth; x++) {
1913 y = ytop + (ybot-ytop)*x / div;
1914 if (y<0 || y>=it->useheight) continue;
1915 XPutPixel(it->image, x, y, 0xffffff);
1920 if (it->need_clear) {
1921 XClearWindow(it->dpy, it->window);
1926 Subtle change: overall_bot was the bottom of the last scan line. Now it's
1927 the top of the next-after-the-last scan line. This is the same until
1928 the y-dimension is > 2400, note ANALOGTV_MAX_LINEHEIGHT.
1931 overall_top=(int)(it->useheight*(1-it->puheight)/2);
1932 overall_bot=(int)(it->useheight*(1+it->puheight)/2);
1934 if (overall_top<0) overall_top=0;
1935 if (overall_bot>it->useheight) overall_bot=it->useheight;
1937 if (overall_top>0) {
1938 XClearArea(it->dpy, it->window,
1939 it->screen_xo, it->screen_yo,
1940 it->usewidth, overall_top, 0);
1942 if (it->useheight > overall_bot) {
1943 XClearArea(it->dpy, it->window,
1944 it->screen_xo, it->screen_yo+overall_bot,
1945 it->usewidth, it->useheight-overall_bot, 0);
1948 if (overall_bot > overall_top) {
1949 put_xshm_image(it->dpy, it->window, it->gc, it->image,
1951 it->screen_xo, it->screen_yo+overall_top,
1952 it->usewidth, overall_bot - overall_top,
1961 gettimeofday(&tv,NULL);
1963 fps=1.0/((tv.tv_sec - it->last_display_time.tv_sec)
1964 + 0.000001*(tv.tv_usec - it->last_display_time.tv_usec));
1965 sprintf(buf, "FPS=%0.1f",fps);
1966 XDrawString(it->dpy, it->window, it->gc, 50, it->useheight*2/3,
1969 it->last_display_time=tv;
1975 analogtv_input_allocate()
1977 analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input));
1983 This takes a screen image and encodes it as a video camera would,
1984 including all the bandlimiting and YIQ modulation.
1985 This isn't especially tuned for speed.
1989 analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
1996 XColor col1[ANALOGTV_PIC_LEN];
1997 XColor col2[ANALOGTV_PIC_LEN];
1998 int multiq[ANALOGTV_PIC_LEN+4];
1999 int y_overscan=5; /* overscan this much top and bottom */
2000 int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
2002 img_w=pic_im->width;
2003 img_h=pic_im->height;
2005 for (i=0; i<ANALOGTV_PIC_LEN+4; i++) {
2006 double phase=90.0-90.0*i;
2008 multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
2011 for (y=0; y<y_scanlength; y++) {
2012 int picy1=(y*img_h)/y_scanlength;
2013 int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
2015 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
2016 int picx=(x*img_w)/ANALOGTV_PIC_LEN;
2017 col1[x].pixel=XGetPixel(pic_im, picx, picy1);
2018 col2[x].pixel=XGetPixel(pic_im, picx, picy2);
2020 XQueryColors(it->dpy, it->colormap, col1, ANALOGTV_PIC_LEN);
2021 XQueryColors(it->dpy, it->colormap, col2, ANALOGTV_PIC_LEN);
2023 for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
2024 for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
2026 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
2028 int filty,filti,filtq;
2031 y=0.30 r + 0.59 g + 0.11 b
2032 i=0.60 r - 0.28 g - 0.32 b
2033 q=0.21 r - 0.52 g + 0.31 b
2034 The coefficients below are in .4 format */
2036 rawy=( 5*col1[x].red + 11*col1[x].green + 2*col1[x].blue +
2037 5*col2[x].red + 11*col2[x].green + 2*col2[x].blue)>>7;
2038 rawi=(10*col1[x].red - 4*col1[x].green - 5*col1[x].blue +
2039 10*col2[x].red - 4*col2[x].green - 5*col2[x].blue)>>7;
2040 rawq=( 3*col1[x].red - 8*col1[x].green + 5*col1[x].blue +
2041 3*col2[x].red - 8*col2[x].green + 5*col2[x].blue)>>7;
2043 /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz
2044 with an extra zero at 3.5 MHz, from
2045 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l */
2047 fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3];
2048 fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6];
2049 fyx[6] = (rawy * 1897) >> 16;
2050 fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3];
2051 fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6];
2052 fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
2053 + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
2056 /* Filter I at 1.5 MHz. 3 pole Butterworth from
2057 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */
2059 fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
2060 fix[3] = (rawi * 1413) >> 16;
2061 fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
2062 fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
2063 + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
2066 /* Filter Q at 0.5 MHz. 3 pole Butterworth from
2067 mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */
2069 fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
2070 fqx[3] = (rawq * 75) >> 16;
2071 fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
2072 fqy[3] = (fqx[0]+fqx[3]) + 3 * (fqx[1]+fqx[2])
2073 + ((2612*fqy[0] - 9007*fqy[1] + 10453 * fqy[2]) >> 12);
2077 composite = filty + ((multiq[x] * filti + multiq[x+3] * filtq)>>12);
2078 composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
2079 if (composite>125) composite=125;
2080 if (composite<0) composite=0;
2081 input->signal[y-y_overscan+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite;
2089 void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
2092 int change=random()%ANALOGTV_V;
2093 unsigned int fastrnd=random();
2094 double hso=(int)(random()%1000)-500;
2095 int yofs=random()%ANALOGTV_V;
2098 for (y=change; y<ANALOGTV_V; y++) {
2099 int s2y=(y+yofs)%ANALOGTV_V;
2101 int noiselevel=60000 / (y-change+100);
2103 it->line_hsync[y] = s2->line_hsync[y] + (int)hso;
2105 for (x=0; x<ANALOGTV_H; x++) {
2107 filt+= (-filt/16) + (int)(fastrnd&0xfff)-0x800;
2108 noise=(filt*noiselevel)>>16;
2109 newsig=s2->signal[s2y][x] + noise;
2110 if (newsig>120) newsig=120;
2111 if (newsig<0) newsig=0;
2112 it->signal[y][x]=newsig;
2122 if (it->hashnoise_times[lineno]) {
2123 int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno];
2125 if (hnt>=0 && hnt<ANALOGTV_PIC_LEN) {
2127 double cur=frand(150.0)-20.0;
2128 int len=random()%15+3;
2129 if (len > ANALOGTV_PIC_LEN-hnt) len=ANALOGTV_PIC_LEN-hnt;
2130 for (i=0; i<len; i++) {
2131 double sig=signal[hnt];
2134 cur += frand(5.0)-5.0;
2135 maxampl = maxampl*0.9;
2146 analogtv_reception_update(analogtv_reception *rec)
2150 if (rec->multipath > 0.0) {
2151 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2152 rec->ghostfir2[i] +=
2153 -(rec->ghostfir2[i]/16.0) + rec->multipath * (frand(0.02)-0.01);
2155 if (random()%20==0) {
2156 rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)]
2157 = rec->multipath * (frand(0.08)-0.04);
2159 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2160 rec->ghostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i];
2164 rec->hfloss2 += -(rec->hfloss2/16.0) + rec->multipath * (frand(0.08)-0.04);
2165 rec->hfloss = 0.5*rec->hfloss + 0.5*rec->hfloss2;
2169 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2170 rec->ghostfir[i] = (i>=ANALOGTV_GHOSTFIR_LEN/2) ? ((i&1) ? +0.04 : -0.08) /ANALOGTV_GHOSTFIR_LEN
2177 /* jwz: since MacOS doesn't have "6x10", I dumped this font to a PNG...
2180 #include "images/gen/6x10font_png.h"
2183 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
2184 int w, int h, char *fontname)
2191 XWindowAttributes xgwa;
2196 XGetWindowAttributes (dpy, window, &xgwa);
2198 if (fontname && !strcmp (fontname, "6x10")) {
2201 XWindowAttributes xgwa;
2203 Pixmap p = image_data_to_pixmap (dpy, window,
2204 _6x10font_png, sizeof(_6x10font_png),
2205 &pix_w, &pix_h, &m);
2206 XImage *im = XGetImage (dpy, p, 0, 0, pix_w, pix_h, ~0L, ZPixmap);
2207 XImage *mm = XGetImage (dpy, m, 0, 0, pix_w, pix_h, 1, XYPixmap);
2208 unsigned long black = BlackPixelOfScreen (DefaultScreenOfDisplay (dpy));
2211 XFreePixmap (dpy, p);
2212 XFreePixmap (dpy, m);
2213 if (pix_w != 256*7) abort();
2214 if (pix_h != 10) abort();
2216 XGetWindowAttributes (dpy, window, &xgwa);
2217 f->text_im = XCreateImage (dpy, xgwa.visual, 1, XYBitmap, 0, 0,
2218 pix_w, pix_h, 8, 0);
2219 f->text_im->data = malloc (f->text_im->bytes_per_line * f->text_im->height);
2221 /* Convert deep image to 1 bit */
2222 for (y = 0; y < pix_h; y++)
2223 for (x = 0; x < pix_w; x++)
2224 XPutPixel (f->text_im, x, y,
2225 (XGetPixel (mm, x, y)
2226 ? XGetPixel (im, x, y) == black
2231 } else if (fontname) {
2233 font = load_font_retry (dpy, fontname);
2235 fprintf(stderr, "analogtv: can't load font %s\n", fontname);
2239 text_pm=XCreatePixmap(dpy, window, 256*f->char_w, f->char_h, 1);
2241 memset(&gcv, 0, sizeof(gcv));
2245 gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
2247 XSetForeground(dpy, gc, 0);
2248 XFillRectangle(dpy, text_pm, gc, 0, 0, 256*f->char_w, f->char_h);
2249 XSetForeground(dpy, gc, 1);
2250 for (i=0; i<256; i++) {
2252 int x=f->char_w*i+1;
2253 int y=f->char_h*8/10;
2254 XDrawString(dpy, text_pm, gc, x, y, &c, 1);
2256 f->text_im = XGetImage(dpy, text_pm, 0, 0, 256*f->char_w, f->char_h,
2259 XWriteBitmapFile(dpy, "/tmp/tvfont.xbm", text_pm,
2260 256*f->char_w, f->char_h, -1, -1);
2263 XFreePixmap(dpy, text_pm);
2265 f->text_im = XCreateImage(dpy, xgwa.visual, 1, XYPixmap, 0, 0,
2266 256*f->char_w, f->char_h, 8, 0);
2267 f->text_im->data = (char *)calloc(f->text_im->height,
2268 f->text_im->bytes_per_line);
2276 analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
2278 if (x<0 || x>=f->char_w) return 0;
2279 if (y<0 || y>=f->char_h) return 0;
2280 if (c<0 || c>=256) return 0;
2281 return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
2285 analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
2287 if (x<0 || x>=f->char_w) return;
2288 if (y<0 || y>=f->char_h) return;
2289 if (c<0 || c>=256) return;
2291 XPutPixel(f->text_im, c*f->char_w + x, y, value);
2295 analogtv_font_set_char(analogtv_font *f, int c, char *s)
2299 if (c<0 || c>=256) return;
2301 for (y=0; y<f->char_h; y++) {
2302 for (x=0; x<f->char_w; x++) {
2304 value=(*s==' ') ? 0 : 1;
2305 analogtv_font_set_pixel(f, c, x, y, value);
2312 analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
2315 for (i=0; i<4; i++) {
2316 double w=90.0*i + phase;
2317 double val=luma + chroma * (cos(3.1415926/180.0*w));
2318 if (val<0.0) val=0.0;
2319 if (val>127.0) val=127.0;
2325 analogtv_draw_solid(analogtv_input *input,
2326 int left, int right, int top, int bot,
2331 if (right-left<4) right=left+4;
2332 if (bot-top<1) bot=top+1;
2334 for (y=top; y<bot; y++) {
2335 for (x=left; x<right; x++) {
2336 input->signal[y][x] = ntsc[x&3];
2343 analogtv_draw_solid_rel_lcp(analogtv_input *input,
2344 double left, double right, double top, double bot,
2345 double luma, double chroma, double phase)
2349 int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top);
2350 int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot);
2351 int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left);
2352 int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right);
2354 analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
2355 analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
2360 analogtv_draw_char(analogtv_input *input, analogtv_font *f,
2361 int c, int x, int y, int ntsc[4])
2363 int yc,xc,ys,xs,pix;
2365 for (yc=0; yc<f->char_h; yc++) {
2366 for (ys=y + yc*f->y_mult; ys<y + (yc+1)*f->y_mult; ys++) {
2367 if (ys<0 || ys>=ANALOGTV_V) continue;
2369 for (xc=0; xc<f->char_w; xc++) {
2370 pix=analogtv_font_pixel(f, c, xc, yc);
2372 for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
2373 if (xs<0 || xs>=ANALOGTV_H) continue;
2375 input->signal[ys][xs] = ntsc[xs&3];
2384 analogtv_draw_string(analogtv_input *input, analogtv_font *f,
2385 char *s, int x, int y, int ntsc[4])
2388 analogtv_draw_char(input, f, *s, x, y, ntsc);
2395 analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
2396 char *s, int x, int y, int ntsc[4])
2398 int width=strlen(s) * f->char_w * 4;
2401 analogtv_draw_string(input, f, s, x, y, ntsc);
2405 static const char hextonib[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2406 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2407 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2408 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
2409 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2410 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2411 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2412 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2415 Much of this function was adapted from logo.c
2418 analogtv_draw_xpm(analogtv *tv, analogtv_input *input,
2419 const char * const *xpm, int left, int top)
2424 int ncolors, nbytes;
2427 int r; int g; int b;
2431 if (4 != sscanf ((const char *) *xpm,
2433 &xpmw, &xpmh, &ncolors, &nbytes, &dummyc))
2435 if (ncolors < 1 || ncolors > 255)
2437 if (nbytes != 1) /* a serious limitation */
2441 for (i = 0; i < ncolors; i++) {
2442 const char *line = *xpm;
2443 int colori = ((unsigned char)*line++)&0xff;
2448 while (*line == ' ' || *line == '\t')
2451 if (which != 'c' && which != 'm')
2453 while (*line == ' ' || *line == '\t')
2455 if (!strncasecmp(line, "None", 4))
2464 r = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2466 g = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2468 b = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2483 for (y=0; y<xpmh; y++) {
2484 const char *line = *xpm++;
2486 if (tvy<ANALOGTV_TOP || tvy>=ANALOGTV_BOT) continue;
2488 for (x=0; x<xpmw; x++) {
2489 int cbyte=((unsigned char)line[x])&0xff;
2492 if (tvx<ANALOGTV_PIC_START || tvx+4>ANALOGTV_PIC_END) continue;
2494 rawy=( 5*cmap[cbyte].r + 11*cmap[cbyte].g + 2*cmap[cbyte].b) / 64;
2495 rawi=(10*cmap[cbyte].r - 4*cmap[cbyte].g - 5*cmap[cbyte].b) / 64;
2496 rawq=( 3*cmap[cbyte].r - 8*cmap[cbyte].g + 5*cmap[cbyte].b) / 64;
2503 for (i=0; i<4; i++) {
2504 if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
2505 if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
2508 input->signal[tvy][tvx+0]= ntsc[(tvx+0)&3];
2509 input->signal[tvy][tvx+1]= ntsc[(tvx+1)&3];
2510 input->signal[tvy][tvx+2]= ntsc[(tvx+2)&3];
2511 input->signal[tvy][tvx+3]= ntsc[(tvx+3)&3];