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);
572 jwxyz_XSetAntiAliasing (it->dpy, it->gc, False);
574 XSetWindowBackground(it->dpy, it->window, gcv.background);
575 XClearWindow(dpy,window);
577 analogtv_configure(it);
583 if(it->threads.count)
584 threadpool_destroy(&it->threads);
585 thread_free(it->signal_subtotals);
586 thread_free(it->rx_signal);
593 analogtv_release(analogtv *it)
596 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
599 if (it->gc) XFreeGC(it->dpy, it->gc);
601 if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
603 threadpool_destroy(&it->threads);
604 thread_free(it->rx_signal);
605 thread_free(it->signal_subtotals);
611 First generate the I and Q reference signals, which we'll multiply
612 the input signal by to accomplish the demodulation. Normally they
613 are shifted 33 degrees from the colorburst. I think this was convenient
614 for inductor-capacitor-vacuum tube implementation.
616 The tint control, FWIW, just adds a phase shift to the chroma signal,
617 and the color control controls the amplitude.
619 In text modes (colormode==0) the system disabled the color burst, and no
620 color was detected by the monitor.
622 freq_error gives a mismatch between the built-in oscillator and the
623 TV's colorbust. Some II Plus machines seemed to occasionally get
624 instability problems -- the crystal oscillator was a single
625 transistor if I remember correctly -- and the frequency would vary
626 enough that the tint would change across the width of the screen.
627 The left side would be in correct tint because it had just gotten
628 resynchronized with the color burst.
630 If we're using a colormap, set it up.
633 analogtv_set_demod(analogtv *it)
635 int y_levels=10,i_levels=5,q_levels=5;
638 In principle, we might be able to figure out how to adjust the
639 color map frame-by-frame to get some nice color bummage. But I'm
640 terrified of changing the color map because we'll get flashing.
642 I can hardly believe we still have to deal with colormaps. They're
643 like having NEAR PTRs: an enormous hassle for the programmer just
644 to save on memory. They should have been deprecated by 1995 or
648 if (it->use_cmap && !it->n_colors) {
651 XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
657 for (yli=0; yli<y_levels; yli++) {
658 for (ili=0; ili<i_levels; ili++) {
659 for (qli=0; qli<q_levels; qli++) {
660 double interpy,interpi,interpq;
661 double levelmult=700.0;
665 interpy=100.0 * ((double)yli/y_levels);
666 interpi=50.0 * (((double)ili-(0.5*i_levels))/(double)i_levels);
667 interpq=50.0 * (((double)qli-(0.5*q_levels))/(double)q_levels);
669 r=(int)((interpy + 1.04*interpi + 0.624*interpq)*levelmult);
670 g=(int)((interpy - 0.276*interpi - 0.639*interpq)*levelmult);
671 b=(int)((interpy - 1.105*interpi + 1.729*interpq)*levelmult);
673 if (r>65535) r=65535;
675 if (g>65535) g=65535;
677 if (b>65535) b=65535;
680 printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n",
681 interpy, interpi, interpq,
689 if (!XAllocColor(it->dpy, it->colormap, &col)) {
690 if (q_levels > y_levels*4/12)
692 else if (i_levels > y_levels*5/12)
701 it->colors[it->n_colors++]=col.pixel;
706 it->cmap_y_levels=y_levels;
707 it->cmap_i_levels=i_levels;
708 it->cmap_q_levels=q_levels;
717 analogtv_line_signature(analogtv_input *input, int lineno)
720 char *origsignal=&input->signal[(lineno+input->vsync)
721 %ANALOGTV_V][input->line_hsync[lineno]];
725 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
727 hash = hash + (hash<<17) + c;
730 hash += input->line_hsync[lineno];
733 hash += input->hashnoise_times[lineno];
742 /* Here we model the analog circuitry of an NTSC television.
743 Basically, it splits the signal into 3 signals: Y, I and Q. Y
744 corresponds to luminance, and you get it by low-pass filtering the
745 input signal to below 3.57 MHz.
747 I and Q are the in-phase and quadrature components of the 3.57 MHz
748 subcarrier. We get them by multiplying by cos(3.57 MHz*t) and
749 sin(3.57 MHz*t), and low-pass filtering. Because the eye has less
750 resolution in some colors than others, the I component gets
751 low-pass filtered at 1.5 MHz and the Q at 0.5 MHz. The I component
752 is approximately orange-blue, and Q is roughly purple-green. See
753 http://www.ntsc-tv.com for details.
755 We actually do an awful lot to the signal here. I suspect it would
756 make sense to wrap them all up together by calculating impulse
757 response and doing FFT convolutions.
762 analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
763 int start, int end, struct analogtv_yiq_s *it_yiq)
768 int phasecorr=(signal-it->rx_signal)&3;
769 struct analogtv_yiq_s *yiq;
771 float agclevel=it->agclevel;
772 float brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
773 float delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
778 double cb_i=(it->line_cb_phase[lineno][(2+phasecorr)&3]-
779 it->line_cb_phase[lineno][(0+phasecorr)&3])/16.0;
780 double cb_q=(it->line_cb_phase[lineno][(3+phasecorr)&3]-
781 it->line_cb_phase[lineno][(1+phasecorr)&3])/16.0;
783 colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
786 multiq2[0] = (cb_i*it->tint_i - cb_q*it->tint_q) * it->color_control;
787 multiq2[1] = (cb_q*it->tint_i + cb_i*it->tint_q) * it->color_control;
788 multiq2[2]=-multiq2[0];
789 multiq2[3]=-multiq2[1];
795 printf("multiq = [%0.3f %0.3f %0.3f %0.3f] ",
796 it->multiq[60],it->multiq[61],it->multiq[62],it->multiq[63]);
797 printf("it->line_cb_phase = [%0.3f %0.3f %0.3f %0.3f]\n",
798 it->line_cb_phase[lineno][0],it->line_cb_phase[lineno][1],
799 it->line_cb_phase[lineno][2],it->line_cb_phase[lineno][3]);
800 printf("multiq2 = [%0.3f %0.3f %0.3f %0.3f]\n",
801 multiq2[0],multiq2[1],multiq2[2],multiq2[3]);
805 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
806 for (i=0; i<5; i++) dp[i]=0.0f;
809 assert(end < ANALOGTV_PIC_LEN+10);
811 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
812 for (i=0; i<24; i++) dp[i]=0.0;
813 for (i=start, yiq=it_yiq+start, sp=signal+start;
815 i++, dp--, yiq++, sp++) {
817 /* Now filter them. These are infinite impulse response filters
818 calculated by the script at
819 http://www-users.cs.york.ac.uk/~fisher/mkfilter. This is
820 fixed-point integer DSP, son. No place for wimps. We do it in
821 integer because you can count on integer being faster on most
822 CPUs. We care about speed because we need to recalculate every
823 time we blink text, and when we spew random bytes into screen
824 memory. This is roughly 16.16 fixed point arithmetic, but we
825 scale some filter values up by a few bits to avoid some nasty
828 /* Filter Y with a 4-pole low-pass Butterworth filter at 3.5 MHz
829 with an extra zero at 3.5 MHz, from
830 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
833 dp[0] = sp[0] * 0.0469904257251935f * agclevel;
834 dp[8] = (+1.0f*(dp[6]+dp[0])
840 yiq->y = dp[8] + brightadd;
844 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
845 for (i=0; i<27; i++) dp[i]=0.0;
847 for (i=start, yiq=it_yiq+start, sp=signal+start;
849 i++, dp--, yiq++, sp++) {
852 /* Filter I and Q with a 3-pole low-pass Butterworth filter at
853 1.5 MHz with an extra zero at 3.5 MHz, from
854 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 -Z 2.5000000000e-01 -l
858 dp[0] = sig*multiq2[i&3] * 0.0833333333333f;
859 yiq->i=dp[8] = (dp[5] + dp[0]
860 +3.0f*(dp[4] + dp[1])
861 +4.0f*(dp[3] + dp[2])
862 -0.3333333333f * dp[10]);
864 dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333f;
865 yiq->q=dp[24] = (dp[16+5] + dp[16+0]
866 +3.0f*(dp[16+4] + dp[16+1])
867 +4.0f*(dp[16+3] + dp[16+2])
868 -0.3333333333f * dp[24+2]);
871 for (i=start, yiq=it_yiq+start; i<end; i++, yiq++) {
872 yiq->i = yiq->q = 0.0f;
878 analogtv_setup_teletext(analogtv_input *input)
881 int teletext=ANALOGTV_BLACK_LEVEL;
883 /* Teletext goes in line 21. But I suspect there are other things
884 in the vertical retrace interval */
886 for (y=19; y<22; y++) {
887 for (x=ANALOGTV_PIC_START; x<ANALOGTV_PIC_END; x++) {
889 teletext=(random()&1) ? ANALOGTV_WHITE_LEVEL : ANALOGTV_BLACK_LEVEL;
891 input->signal[y][x]=teletext;
897 analogtv_setup_frame(analogtv *it)
903 if (it->flutter_horiz_desync) {
904 /* Horizontal sync during vertical sync instability. */
905 it->horiz_desync += -0.10*(it->horiz_desync-3.0) +
906 ((int)(random()&0xff)-0x80) *
907 ((int)(random()&0xff)-0x80) *
908 ((int)(random()&0xff)-0x80) * 0.000001;
912 for (i=0; i<ANALOGTV_V; i++) {
913 it->hashnoise_times[i]=0;
917 /* let's leave it to process shrinkpulse */
918 if (it->hashnoise_enable && !it->hashnoise_on) {
919 if (random()%10000==0) {
921 it->shrinkpulse=random()%ANALOGTV_V;
924 if (random()%1000==0) {
928 #if 0 /* never used */
929 if (it->hashnoise_on) {
930 it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
931 ((int)(random()%2000)-1000)*0.1;
933 it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
934 if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
936 if (it->hashnoise_rpm > 0.0) {
939 int hnc=it->hashnoise_counter; /* in 24.8 format */
941 /* Convert rpm of a 16-pole motor into dots in 24.8 format */
942 hni_double = ANALOGTV_V * ANALOGTV_H * 256.0 /
943 (it->hashnoise_rpm * 16.0 / 60.0 / 60.0);
944 hni = (hni_double <= INT_MAX) ? (int)hni_double : INT_MAX;
946 while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
947 y=(hnc>>8)/ANALOGTV_H;
948 x=(hnc>>8)%ANALOGTV_H;
950 if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
951 it->hashnoise_times[y]=x;
953 /* hnc += hni + (int)(random()%65536)-32768; */
955 hnc += (int)(random()%65536)-32768;
956 if ((hnc >= 0) && (INT_MAX - hnc < hni)) break;
963 /* hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;*/
966 if (it->rx_signal_level != 0.0)
967 it->agclevel = 1.0/it->rx_signal_level;
972 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
973 printf(" %0.3f",it->ghostfir[i]);
975 printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel);
980 analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
985 int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
987 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
988 vsync=lineno>=3 && lineno<7;
990 sig=input->signal[lineno];
992 i=ANALOGTV_SYNC_START;
994 while (i<ANALOGTV_BP_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
995 while (i<ANALOGTV_H) sig[i++]=synclevel;
997 while (i<ANALOGTV_BP_START) sig[i++]=synclevel;
998 while (i<ANALOGTV_PIC_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
999 while (i<ANALOGTV_FP_START) sig[i++]=ANALOGTV_BLACK_LEVEL;
1001 while (i<ANALOGTV_H) sig[i++]=ANALOGTV_BLANK_LEVEL;
1004 /* 9 cycles of colorburst */
1005 for (i=ANALOGTV_CB_START; i<ANALOGTV_CB_START+36; i+=4) {
1006 sig[i+1] += ANALOGTV_CB_LEVEL;
1007 sig[i+3] -= ANALOGTV_CB_LEVEL;
1014 analogtv_sync(analogtv *it)
1016 int cur_hsync=it->cur_hsync;
1017 int cur_vsync=it->cur_vsync;
1022 float cbfc=1.0f/128.0f;
1024 /* sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;*/
1025 for (i=-32; i<32; i++) {
1026 lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1027 sp = it->rx_signal + lineno*ANALOGTV_H;
1029 for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/16) {
1032 filt *= it->agclevel;
1034 osc = (float)(ANALOGTV_V+i)/(float)ANALOGTV_V;
1036 if (osc >= 1.05f+0.0002f * filt) break;
1038 cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1040 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
1042 if (lineno>5 && lineno<ANALOGTV_V-3) { /* ignore vsync interval */
1043 unsigned lineno2 = (lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V;
1044 if (!lineno2) lineno2 = ANALOGTV_V;
1045 sp = it->rx_signal + lineno2*ANALOGTV_H + cur_hsync;
1046 for (i=-8; i<8; i++) {
1047 osc = (float)(ANALOGTV_H+i)/(float)ANALOGTV_H;
1048 filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
1050 if (osc >= 1.005f + 0.0001f*filt) break;
1052 cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
1055 it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START +
1056 ANALOGTV_H) % ANALOGTV_H;
1058 /* Now look for the colorburst, which is a few cycles after the H
1059 sync pulse, and store its phase.
1060 The colorburst is 9 cycles long, and we look at the middle 5
1065 sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
1066 for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; i++) {
1067 it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0f-cbfc) +
1068 sp[i]*it->agclevel*cbfc;
1076 for (i=0; i<4; i++) {
1077 tot += it->cb_phase[i]*it->cb_phase[i];
1079 cbgain = 32.0f/sqrtf(tot);
1081 for (i=0; i<4; i++) {
1082 it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
1087 if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n",
1089 it->cb_phase[0], it->cb_phase[1],
1090 it->cb_phase[2], it->cb_phase[3]);
1093 /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */
1096 it->cur_hsync = cur_hsync;
1097 it->cur_vsync = cur_vsync;
1101 analogtv_levelmult(const analogtv *it, int level)
1103 static const double levelfac[3]={-7.5, 5.5, 24.5};
1104 return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
1108 analogtv_level(const analogtv *it, int y, int ytop, int ybot)
1112 if (y==ytop || y==ybot-1) level=0;
1113 else if (y==ytop+1 || y==ybot-2) level=1;
1116 else if (ybot-ytop>=5) {
1117 if (y==ytop || y==ybot-1) level=0;
1120 else if (ybot-ytop>=3) {
1121 if (y==ytop) level=0;
1131 The point of this stuff is to ensure that when useheight is not a
1132 multiple of VISLINES so that TV scan lines map to different numbers
1133 of vertical screen pixels, the total brightness of each scan line
1135 ANALOGTV_MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
1136 it interpolates extra black lines.
1140 analogtv_setup_levels(analogtv *it, double avgheight)
1143 static const double levelfac[3]={-7.5, 5.5, 24.5};
1145 for (height=0; height<avgheight+2.0 && height<=ANALOGTV_MAX_LINEHEIGHT; height++) {
1147 for (i=0; i<height; i++) {
1148 it->leveltable[height][i].index = 2;
1152 it->leveltable[height][0].index=0;
1155 if (height >= 1) it->leveltable[height][height-1].index=0;
1158 it->leveltable[height][1].index=1;
1159 if (height >= 2) it->leveltable[height][height-2].index=1;
1162 for (i=0; i<height; i++) {
1163 it->leveltable[height][i].value =
1164 (40.0 + levelfac[it->leveltable[height][i].index]*puramp(it, 3.0, 6.0, 1.0)) / 256.0;
1170 static void rnd_combine(unsigned *a0, unsigned *c0, unsigned a1, unsigned c1)
1172 *a0 = (*a0 * a1) & 0xffffffffu;
1173 *c0 = (c1 + a1 * *c0) & 0xffffffffu;
1176 static void rnd_seek_ac(unsigned *a, unsigned *c, unsigned dist)
1178 unsigned int a1 = *a, c1 = *c;
1184 rnd_combine(a, c, a1, c1);
1186 rnd_combine(&a1, &c1, a1, c1);
1190 static unsigned int rnd_seek(unsigned a, unsigned c, unsigned rnd, unsigned dist)
1192 rnd_seek_ac(&a, &c, dist);
1196 static void analogtv_init_signal(const analogtv *it, double noiselevel, unsigned start, unsigned end)
1198 float *ps=it->rx_signal + start;
1199 float *pe=it->rx_signal + end;
1201 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random0, start);
1202 unsigned int fastrnd_offset;
1204 float noisemul = sqrt(noiselevel*150)/(float)0x7fffffff;
1206 fastrnd_offset = fastrnd - 0x7fffffff;
1207 nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
1210 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1211 fastrnd_offset = fastrnd - 0x7fffffff;
1212 nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
1217 static void analogtv_add_signal(const analogtv *it, const analogtv_reception *rec, unsigned start, unsigned end, int ec)
1219 analogtv_input *inp=rec->input;
1220 float *ps=it->rx_signal + start;
1221 float *pe=it->rx_signal + end;
1223 signed char *ss=&inp->signal[0][0];
1224 signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
1225 signed char *s=ss + ((start + (unsigned)rec->ofs) % ANALOGTV_SIGNAL_LEN);
1228 float level=rec->level;
1229 float hfloss=rec->hfloss;
1230 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random1, start);
1233 const float noise_decay = 0.99995f;
1234 float noise_ampl = 1.3f * powf(noise_decay, start);
1239 /* assert((se-ss)%4==0 && (se-s)%4==0); */
1241 for (i = start; i < ec; i++) { /* Sometimes start > ec. */
1243 /* Do a big noisy transition. We can make the transition noise of
1244 high constant strength regardless of signal strength.
1246 There are two separate state machines. here, One is the noise
1247 process and the other is the
1249 We don't bother with the FIR filter here
1252 float sig0=(float)s[0];
1253 unsigned int fastrnd_offset = fastrnd - 0x7fffffff;
1254 float noise = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * (50.0f/(float)0x7fffffff);
1255 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1257 p[0] += sig0 * level * (1.0f - noise_ampl) + noise * noise_ampl;
1259 noise_ampl *= noise_decay;
1268 for (i=1; i<5; i++) {
1271 s2 += ANALOGTV_SIGNAL_LEN;
1272 dp[i] = (float)((int)s2[0]+(int)s2[1]+(int)s2[2]+(int)s2[3]);
1276 assert(!((pe - p) % 4));
1278 float sig0,sig1,sig2,sig3,sigr;
1285 dp[0]=sig0+sig1+sig2+sig3;
1287 /* Get the video out signal, and add some ghosting, typical of RF
1288 monitor cables. This corresponds to a pretty long cable, but
1292 sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] +
1293 dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]);
1294 dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0];
1296 p[0] += (sig0+sigr + sig2*hfloss) * level;
1297 p[1] += (sig1+sigr + sig3*hfloss) * level;
1298 p[2] += (sig2+sigr + sig0*hfloss) * level;
1299 p[3] += (sig3+sigr + sig1*hfloss) * level;
1303 if (s>=se) s = ss + (s-se);
1309 static void analogtv_thread_add_signals(void *thread_raw)
1311 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1312 const analogtv *it = thread->it;
1314 unsigned subtotal_end;
1316 unsigned start = thread->signal_start;
1317 while(start != thread->signal_end)
1321 /* Work on 8 KB blocks; these should fit in L1. */
1322 /* (Though it doesn't seem to help much on my system.) */
1323 unsigned end = start + 2048;
1324 if(end > thread->signal_end)
1325 end = thread->signal_end;
1327 analogtv_init_signal (it, it->noiselevel, start, end);
1329 for (i = 0; i != it->rec_count; ++i) {
1330 analogtv_add_signal (it, it->recs[i], start, end,
1331 !i ? it->channel_change_cycles : 0);
1334 assert (!(start % ANALOGTV_SUBTOTAL_LEN));
1335 assert (!(end % ANALOGTV_SUBTOTAL_LEN));
1337 p = it->rx_signal + start;
1338 subtotal_end = end / ANALOGTV_SUBTOTAL_LEN;
1339 for (i = start / ANALOGTV_SUBTOTAL_LEN; i != subtotal_end; ++i) {
1341 for (j = 1; j != ANALOGTV_SUBTOTAL_LEN; ++j)
1343 it->signal_subtotals[i] = sum;
1344 p += ANALOGTV_SUBTOTAL_LEN;
1351 static int analogtv_get_line(const analogtv *it, int lineno, int *slineno,
1352 int *ytop, int *ybot, unsigned *signal_offset)
1354 *slineno=lineno-ANALOGTV_TOP;
1355 *ytop=(int)((*slineno*it->useheight/ANALOGTV_VISLINES -
1356 it->useheight/2)*it->puheight) + it->useheight/2;
1357 *ybot=(int)(((*slineno+1)*it->useheight/ANALOGTV_VISLINES -
1358 it->useheight/2)*it->puheight) + it->useheight/2;
1360 int linesig=analogtv_line_signature(input,lineno)
1361 + it->hashnoise_times[lineno];
1363 *signal_offset = ((lineno+it->cur_vsync+ANALOGTV_V) % ANALOGTV_V) * ANALOGTV_H +
1364 it->line_hsync[lineno];
1366 if (*ytop==*ybot) return 0;
1367 if (*ybot<0 || *ytop>it->useheight) return 0;
1368 if (*ytop<0) *ytop=0;
1369 if (*ybot>it->useheight) *ybot=it->useheight;
1371 if (*ybot > *ytop+ANALOGTV_MAX_LINEHEIGHT) *ybot=*ytop+ANALOGTV_MAX_LINEHEIGHT;
1376 analogtv_blast_imagerow(const analogtv *it,
1377 float *rgbf, float *rgbf_end,
1382 char *level_copyfrom[3];
1383 int xrepl=it->xrepl;
1384 unsigned lineheight = ybot - ytop;
1385 if (lineheight > ANALOGTV_MAX_LINEHEIGHT) lineheight = ANALOGTV_MAX_LINEHEIGHT;
1386 for (i=0; i<3; i++) level_copyfrom[i]=NULL;
1388 for (y=ytop; y<ybot; y++) {
1389 char *rowdata=it->image->data + y*it->image->bytes_per_line;
1390 unsigned line = y-ytop;
1392 int level=it->leveltable[lineheight][line].index;
1393 float levelmult=it->leveltable[lineheight][line].value;
1395 /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
1396 why standard graphics sw has to be fast, or else people will have to
1397 work around it and risk incompatibility. The quickdraw folks
1398 understood this. The other answer would be for X11 to have fewer
1399 formats for bitm.. oh, never mind. If neither of these cases work
1400 (they probably cover 99% of setups) it falls back on the Xlib
1403 if (level_copyfrom[level]) {
1404 memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line);
1407 level_copyfrom[level] = rowdata;
1411 else if (it->image->format==ZPixmap &&
1412 it->image->bits_per_pixel==32 &&
1413 sizeof(unsigned int)==4 &&
1414 it->image->byte_order==localbyteorder) {
1415 /* int is more likely to be 32 bits than long */
1416 unsigned int *pixelptr=(unsigned int *)rowdata;
1419 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1420 int ntscri=rpf[0]*levelmult;
1421 int ntscgi=rpf[1]*levelmult;
1422 int ntscbi=rpf[2]*levelmult;
1423 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1424 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1425 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1426 pix = (it->red_values[ntscri] |
1427 it->green_values[ntscgi] |
1428 it->blue_values[ntscbi]);
1432 if (xrepl>=3) pixelptr[2] = pix;
1437 else if (it->image->format==ZPixmap &&
1438 it->image->bits_per_pixel==16 &&
1439 sizeof(unsigned short)==2 &&
1440 float_extraction_works &&
1441 it->image->byte_order==localbyteorder) {
1442 unsigned short *pixelptr=(unsigned short *)rowdata;
1444 float_extract_t r1,g1,b1;
1447 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1448 r2=rpf[0]; g2=rpf[1]; b2=rpf[2];
1449 r1.f=r2 * levelmult+float_low8_ofs;
1450 g1.f=g2 * levelmult+float_low8_ofs;
1451 b1.f=b2 * levelmult+float_low8_ofs;
1452 pix = (it->red_values[r1.i & 0x3ff] |
1453 it->green_values[g1.i & 0x3ff] |
1454 it->blue_values[b1.i & 0x3ff]);
1458 if (xrepl>=3) pixelptr[2] = pix;
1463 else if (it->image->format==ZPixmap &&
1464 it->image->bits_per_pixel==16 &&
1465 sizeof(unsigned short)==2 &&
1466 it->image->byte_order==localbyteorder) {
1467 unsigned short *pixelptr=(unsigned short *)rowdata;
1470 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1471 int r1=rpf[0] * levelmult;
1472 int g1=rpf[1] * levelmult;
1473 int b1=rpf[2] * levelmult;
1474 if (r1>=ANALOGTV_CV_MAX) r1=ANALOGTV_CV_MAX-1;
1475 if (g1>=ANALOGTV_CV_MAX) g1=ANALOGTV_CV_MAX-1;
1476 if (b1>=ANALOGTV_CV_MAX) b1=ANALOGTV_CV_MAX-1;
1477 pix = it->red_values[r1] | it->green_values[g1] | it->blue_values[b1];
1481 if (xrepl>=3) pixelptr[2] = pix;
1487 for (x=0, rpf=rgbf; rpf!=rgbf_end ; x++, rpf+=3) {
1488 int ntscri=rpf[0]*levelmult;
1489 int ntscgi=rpf[1]*levelmult;
1490 int ntscbi=rpf[2]*levelmult;
1491 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1492 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1493 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1494 for (j=0; j<xrepl; j++) {
1495 XPutPixel(it->image, x*xrepl + j, y,
1496 it->red_values[ntscri] | it->green_values[ntscgi] |
1497 it->blue_values[ntscbi]);
1505 static void analogtv_thread_draw_lines(void *thread_raw)
1507 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1508 const analogtv *it = thread->it;
1512 float *raw_rgb_start;
1514 raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
1516 if (! raw_rgb_start) return;
1518 raw_rgb_end=raw_rgb_start+3*it->subwidth;
1520 for (lineno=ANALOGTV_TOP + thread->thread_id;
1521 lineno<ANALOGTV_BOT;
1522 lineno += it->threads.count) {
1525 int slineno, ytop, ybot;
1526 unsigned signal_offset;
1528 const float *signal;
1530 int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
1531 float *rgb_start, *rgb_end;
1537 struct analogtv_yiq_s yiq[ANALOGTV_PIC_LEN+10];
1539 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot,
1543 signal = it->rx_signal + signal_offset;
1547 float bloomthisrow,shiftthisrow;
1548 float viswidth,middle;
1552 bloomthisrow = -10.0f * it->crtload[lineno];
1553 if (bloomthisrow<-10.0f) bloomthisrow=-10.0f;
1554 if (bloomthisrow>2.0f) bloomthisrow=2.0f;
1556 shiftthisrow=it->horiz_desync * (expf(-0.17f*slineno) *
1557 (0.7f+cosf(slineno*0.6f)));
1562 viswidth=ANALOGTV_PIC_LEN * 0.79f - 5.0f*bloomthisrow;
1563 middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
1565 scanwidth=it->width_control * puramp(it, 0.5f, 0.3f, 1.0f);
1567 scw=it->subwidth*scanwidth;
1568 if (scw>it->subwidth) scw=it->usewidth;
1569 scl=it->subwidth/2 - scw/2;
1570 scr=it->subwidth/2 + scw/2;
1572 pixrate=(int)((viswidth*65536.0f*1.0f)/it->subwidth)/scanwidth;
1573 scanstart_i=(int)((middle-viswidth*0.5f)*65536.0f);
1574 scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
1575 squishright_i=(int)((middle+viswidth*(0.25f + 0.25f*puramp(it, 2.0f, 0.0f, 1.1f)
1576 - it->squish_control)) *65536.0f);
1577 squishdiv=it->subwidth/15;
1579 rgb_start=raw_rgb_start+scl*3;
1580 rgb_end=raw_rgb_start+scr*3;
1582 assert(scanstart_i>=0);
1585 if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
1587 scanstart_i/65536.0f,
1588 squishright_i/65536.0f,
1595 for (y=ytop; y<ybot; y++) {
1596 int level=analogtv_level(it, y, ytop, ybot);
1597 float levelmult=analogtv_levelmult(it, level);
1598 float levelmult_y = levelmult * it->contrast_control
1599 * puramp(it, 1.0f, 0.0f, 1.0f) / (0.5f+0.5f*it->puheight) * 0.070f;
1600 float levelmult_iq = levelmult * 0.090f;
1602 analogtv_ntsc_to_yiq(it, lineno, signal,
1603 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1608 while (i<0 && x<it->usewidth) {
1609 XPutPixel(it->image, x, y, it->colors[0]);
1614 while (i<scanend_i && x<it->usewidth) {
1615 float pixfrac=(i&0xffff)/65536.0f;
1616 float invpixfrac=(1.0f-pixfrac);
1618 int yli,ili,qli,cmi;
1620 float interpy=(yiq[pati].y*invpixfrac
1621 + yiq[pati+1].y*pixfrac) * levelmult_y;
1622 float interpi=(yiq[pati].i*invpixfrac
1623 + yiq[pati+1].i*pixfrac) * levelmult_iq;
1624 float interpq=(yiq[pati].q*invpixfrac
1625 + yiq[pati+1].q*pixfrac) * levelmult_iq;
1627 yli = (int)(interpy * it->cmap_y_levels);
1628 ili = (int)((interpi+0.5f) * it->cmap_i_levels);
1629 qli = (int)((interpq+0.5f) * it->cmap_q_levels);
1631 if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
1633 if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1;
1635 if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1;
1637 cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli);
1640 if ((random()%65536)==0) {
1641 printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n",
1642 interpy, interpi, interpq,
1648 for (j=0; j<it->xrepl; j++) {
1649 XPutPixel(it->image, x, y,
1653 if (i >= squishright_i) {
1654 pixmultinc += pixmultinc/squishdiv;
1658 while (x<it->usewidth) {
1659 XPutPixel(it->image, x, y, it->colors[0]);
1665 analogtv_ntsc_to_yiq(it, lineno, signal,
1666 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1668 pixbright=it->contrast_control * puramp(it, 1.0f, 0.0f, 1.0f)
1669 / (0.5f+0.5f*it->puheight) * 1024.0f/100.0f;
1671 i=scanstart_i; rrp=rgb_start;
1672 while (i<0 && rrp!=rgb_end) {
1673 rrp[0]=rrp[1]=rrp[2]=0;
1677 while (i<scanend_i && rrp!=rgb_end) {
1678 float pixfrac=(i&0xffff)/65536.0f;
1679 float invpixfrac=1.0f-pixfrac;
1683 float interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
1684 float interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
1685 float interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
1688 According to the NTSC spec, Y,I,Q are generated as:
1690 y=0.30 r + 0.59 g + 0.11 b
1691 i=0.60 r - 0.28 g - 0.32 b
1692 q=0.21 r - 0.52 g + 0.31 b
1694 So if you invert the implied 3x3 matrix you get what standard
1695 televisions implement with a bunch of resistors (or directly in the
1698 r = y + 0.948 i + 0.624 q
1699 g = y - 0.276 i - 0.639 q
1700 b = y - 1.105 i + 1.729 q
1703 r=(interpy + 0.948f*interpi + 0.624f*interpq) * pixbright;
1704 g=(interpy - 0.276f*interpi - 0.639f*interpq) * pixbright;
1705 b=(interpy - 1.105f*interpi + 1.729f*interpq) * pixbright;
1713 if (i>=squishright_i) {
1714 pixmultinc += pixmultinc/squishdiv;
1715 pixbright += pixbright/squishdiv/2;
1720 while (rrp != rgb_end) {
1721 rrp[0]=rrp[1]=rrp[2]=0.0f;
1725 analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end,
1730 free(raw_rgb_start);
1734 analogtv_draw(analogtv *it, double noiselevel,
1735 const analogtv_reception *const *recs, unsigned rec_count)
1738 /* int bigloadchange,drawcount;*/
1740 int overall_top, overall_bot;
1742 /* AnalogTV isn't very interesting if there isn't enough RAM. */
1746 it->rx_signal_level = noiselevel;
1747 for (i = 0; i != rec_count; ++i) {
1748 const analogtv_reception *rec = recs[i];
1749 double level = rec->level;
1750 analogtv_input *inp=rec->input;
1752 it->rx_signal_level =
1753 sqrt(it->rx_signal_level * it->rx_signal_level +
1754 (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] +
1755 rec->ghostfir[2] + rec->ghostfir[3]))));
1757 /* duplicate the first line into the Nth line to ease wraparound computation */
1758 memcpy(inp->signal[ANALOGTV_V], inp->signal[0],
1759 ANALOGTV_H * sizeof(inp->signal[0][0]));
1762 analogtv_setup_frame(it);
1763 analogtv_set_demod(it);
1765 it->random0 = random();
1766 it->random1 = random();
1767 it->noiselevel = noiselevel;
1769 it->rec_count = rec_count;
1770 threadpool_run(&it->threads, analogtv_thread_add_signals);
1771 threadpool_wait(&it->threads);
1773 it->channel_change_cycles=0;
1775 /* rx_signal has an extra 2 lines at the end, where we copy the
1776 first 2 lines so we can index into it while only worrying about
1777 wraparound on a per-line level */
1778 memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN],
1780 2*ANALOGTV_H*sizeof(it->rx_signal[0]));
1782 /* Repeat for signal_subtotals. */
1784 memcpy(&it->signal_subtotals[ANALOGTV_SIGNAL_LEN / ANALOGTV_SUBTOTAL_LEN],
1785 &it->signal_subtotals[0],
1786 (2*ANALOGTV_H/ANALOGTV_SUBTOTAL_LEN)*sizeof(it->signal_subtotals[0]));
1788 analogtv_sync(it); /* Requires the add_signals be complete. */
1791 /* if (it->hashnoise_on) baseload=0.5; */
1795 it->crtload[ANALOGTV_TOP-1]=baseload;
1796 it->puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
1797 (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
1799 analogtv_setup_levels(it, it->puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
1801 /* calculate tint once per frame */
1802 it->tint_i = -cos((103 + it->tint_control)*3.1415926/180);
1803 it->tint_q = sin((103 + it->tint_control)*3.1415926/180);
1805 for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
1806 int slineno, ytop, ybot;
1807 unsigned signal_offset;
1808 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot, &signal_offset))
1811 if (lineno==it->shrinkpulse) {
1813 /*bigloadchange=1;*/
1818 if (it->hashnoise_rpm>0.0 &&
1821 (slineno<20 && it->flutter_horiz_desync) ||
1822 it->gaussiannoise_level>30 ||
1823 ((it->gaussiannoise_level>2.0 ||
1824 it->multipath) && random()%4) ||
1825 linesig != it->onscreen_signature[lineno])) {
1828 it->onscreen_signature[lineno] = linesig;
1833 Interpolate the 600-dotclock line into however many horizontal
1834 screen pixels we're using, and convert to RGB.
1836 We add some 'bloom', variations in the horizontal scan width with
1837 the amount of brightness, extremely common on period TV sets. They
1838 had a single oscillator which generated both the horizontal scan and
1839 (during the horizontal retrace interval) the high voltage for the
1840 electron beam. More brightness meant more load on the oscillator,
1841 which caused an decrease in horizontal deflection. Look for
1844 Also, the A2 did a bad job of generating horizontal sync pulses
1845 during the vertical blanking interval. This, and the fact that the
1846 horizontal frequency was a bit off meant that TVs usually went a bit
1847 out of sync during the vertical retrace, and the top of the screen
1848 would be bent a bit to the left or right. Look for (shiftthisrow).
1850 We also add a teeny bit of left overscan, just enough to be
1851 annoying, but you can still read the left column of text.
1853 We also simulate compression & brightening on the right side of the
1854 screen. Most TVs do this, but you don't notice because they overscan
1855 so it's off the right edge of the CRT. But the A2 video system used
1856 so much of the horizontal scan line that you had to crank the
1857 horizontal width down in order to not lose the right few characters,
1858 and you'd see the compression on the right edge. Associated with
1859 compression is brightening; since the electron beam was scanning
1860 slower, the same drive signal hit the phosphor harder. Look for
1861 (squishright_i) and (squishdiv).
1865 /* This used to be an int, I suspect by mistake. - Dave */
1872 frac = signal_offset & (ANALOGTV_SUBTOTAL_LEN - 1);
1873 p = it->rx_signal + (signal_offset & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1874 for (i=0; i != frac; i++) {
1878 end0 = (signal_offset + ANALOGTV_PIC_LEN);
1880 end1 = end0 / ANALOGTV_SUBTOTAL_LEN;
1881 for (i=signal_offset / ANALOGTV_SUBTOTAL_LEN; i<end1; i++) {
1882 totsignal += it->signal_subtotals[i];
1885 frac = end0 & (ANALOGTV_SUBTOTAL_LEN - 1);
1886 p = it->rx_signal + (end0 & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1887 for (i=0; i != frac; i++) {
1891 totsignal *= it->agclevel;
1892 ncl = 0.95f * it->crtload[lineno-1] +
1894 (totsignal-30000)/100000.0f +
1895 (slineno>184 ? (slineno-184)*(lineno-184)*0.001f * it->squeezebottom
1897 /*diff=ncl - it->crtload[lineno];*/
1898 /*bigloadchange = (diff>0.01 || diff<-0.01);*/
1899 it->crtload[lineno]=ncl;
1903 threadpool_run(&it->threads, analogtv_thread_draw_lines);
1904 threadpool_wait(&it->threads);
1907 /* poor attempt at visible retrace */
1908 for (i=0; i<15; i++) {
1909 int ytop=(int)((i*it->useheight/15 -
1910 it->useheight/2)*puheight) + it->useheight/2;
1911 int ybot=(int)(((i+1)*it->useheight/15 -
1912 it->useheight/2)*puheight) + it->useheight/2;
1913 int div=it->usewidth*3/2;
1915 for (x=0; x<it->usewidth; x++) {
1916 y = ytop + (ybot-ytop)*x / div;
1917 if (y<0 || y>=it->useheight) continue;
1918 XPutPixel(it->image, x, y, 0xffffff);
1923 if (it->need_clear) {
1924 XClearWindow(it->dpy, it->window);
1929 Subtle change: overall_bot was the bottom of the last scan line. Now it's
1930 the top of the next-after-the-last scan line. This is the same until
1931 the y-dimension is > 2400, note ANALOGTV_MAX_LINEHEIGHT.
1934 overall_top=(int)(it->useheight*(1-it->puheight)/2);
1935 overall_bot=(int)(it->useheight*(1+it->puheight)/2);
1937 if (overall_top<0) overall_top=0;
1938 if (overall_bot>it->useheight) overall_bot=it->useheight;
1940 if (overall_top>0) {
1941 XClearArea(it->dpy, it->window,
1942 it->screen_xo, it->screen_yo,
1943 it->usewidth, overall_top, 0);
1945 if (it->useheight > overall_bot) {
1946 XClearArea(it->dpy, it->window,
1947 it->screen_xo, it->screen_yo+overall_bot,
1948 it->usewidth, it->useheight-overall_bot, 0);
1951 if (overall_bot > overall_top) {
1952 put_xshm_image(it->dpy, it->window, it->gc, it->image,
1954 it->screen_xo, it->screen_yo+overall_top,
1955 it->usewidth, overall_bot - overall_top,
1964 gettimeofday(&tv,NULL);
1966 fps=1.0/((tv.tv_sec - it->last_display_time.tv_sec)
1967 + 0.000001*(tv.tv_usec - it->last_display_time.tv_usec));
1968 sprintf(buf, "FPS=%0.1f",fps);
1969 XDrawString(it->dpy, it->window, it->gc, 50, it->useheight*2/3,
1972 it->last_display_time=tv;
1978 analogtv_input_allocate()
1980 analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input));
1986 This takes a screen image and encodes it as a video camera would,
1987 including all the bandlimiting and YIQ modulation.
1988 This isn't especially tuned for speed.
1990 xoff, yoff: top left corner of rendered image, in window pixels.
1991 w, h: scaled size of rendered image, in window pixels.
1992 mask: BlackPixel means don't render (it's not full alpha)
1996 analogtv_load_ximage(analogtv *it, analogtv_input *input,
1997 XImage *pic_im, XImage *mask_im,
1998 int xoff, int yoff, int target_w, int target_h)
2005 XColor col1[ANALOGTV_PIC_LEN];
2006 XColor col2[ANALOGTV_PIC_LEN];
2007 char mask[ANALOGTV_PIC_LEN];
2008 int multiq[ANALOGTV_PIC_LEN+4];
2009 unsigned long black = 0; /* not BlackPixelOfScreen (it->xgwa.screen); */
2011 int x_length=ANALOGTV_PIC_LEN;
2012 int y_overscan=5; /* overscan this much top and bottom */
2013 int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
2015 if (target_w > 0) x_length = x_length * target_w / it->xgwa.width;
2016 if (target_h > 0) y_scanlength = y_scanlength * target_h / it->xgwa.height;
2018 img_w = pic_im->width;
2019 img_h = pic_im->height;
2021 xoff = ANALOGTV_PIC_LEN * xoff / it->xgwa.width;
2022 yoff = ANALOGTV_VISLINES * yoff / it->xgwa.height;
2024 for (i=0; i<x_length+4; i++) {
2025 double phase=90.0-90.0*i;
2027 multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
2030 for (y=0; y<y_scanlength; y++) {
2031 int picy1=(y*img_h)/y_scanlength;
2032 int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
2034 for (x=0; x<x_length; x++) {
2035 int picx=(x*img_w)/x_length;
2036 col1[x].pixel=XGetPixel(pic_im, picx, picy1);
2037 col2[x].pixel=XGetPixel(pic_im, picx, picy2);
2039 mask[x] = (XGetPixel(mask_im, picx, picy1) != black);
2043 XQueryColors(it->dpy, it->colormap, col1, x_length);
2044 XQueryColors(it->dpy, it->colormap, col2, x_length);
2045 for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
2046 for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
2048 for (x=0; x<x_length; x++) {
2050 int filty,filti,filtq;
2053 if (!mask[x]) continue;
2056 y=0.30 r + 0.59 g + 0.11 b
2057 i=0.60 r - 0.28 g - 0.32 b
2058 q=0.21 r - 0.52 g + 0.31 b
2059 The coefficients below are in .4 format */
2061 rawy=( 5*col1[x].red + 11*col1[x].green + 2*col1[x].blue +
2062 5*col2[x].red + 11*col2[x].green + 2*col2[x].blue)>>7;
2063 rawi=(10*col1[x].red - 4*col1[x].green - 5*col1[x].blue +
2064 10*col2[x].red - 4*col2[x].green - 5*col2[x].blue)>>7;
2065 rawq=( 3*col1[x].red - 8*col1[x].green + 5*col1[x].blue +
2066 3*col2[x].red - 8*col2[x].green + 5*col2[x].blue)>>7;
2068 /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz
2069 with an extra zero at 3.5 MHz, from
2070 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l */
2072 fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3];
2073 fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6];
2074 fyx[6] = (rawy * 1897) >> 16;
2075 fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3];
2076 fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6];
2077 fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
2078 + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
2081 /* Filter I at 1.5 MHz. 3 pole Butterworth from
2082 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */
2084 fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
2085 fix[3] = (rawi * 1413) >> 16;
2086 fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
2087 fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
2088 + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
2091 /* Filter Q at 0.5 MHz. 3 pole Butterworth from
2092 mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */
2094 fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
2095 fqx[3] = (rawq * 75) >> 16;
2096 fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
2097 fqy[3] = (fqx[0]+fqx[3]) + 3 * (fqx[1]+fqx[2])
2098 + ((2612*fqy[0] - 9007*fqy[1] + 10453 * fqy[2]) >> 12);
2102 composite = filty + ((multiq[x] * filti + multiq[x+3] * filtq)>>12);
2103 composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
2104 if (composite>125) composite=125;
2105 if (composite<0) composite=0;
2107 input->signal[y-y_overscan+ANALOGTV_TOP+yoff][x+ANALOGTV_PIC_START+xoff] = composite;
2115 void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
2118 int change=random()%ANALOGTV_V;
2119 unsigned int fastrnd=random();
2120 double hso=(int)(random()%1000)-500;
2121 int yofs=random()%ANALOGTV_V;
2124 for (y=change; y<ANALOGTV_V; y++) {
2125 int s2y=(y+yofs)%ANALOGTV_V;
2127 int noiselevel=60000 / (y-change+100);
2129 it->line_hsync[y] = s2->line_hsync[y] + (int)hso;
2131 for (x=0; x<ANALOGTV_H; x++) {
2133 filt+= (-filt/16) + (int)(fastrnd&0xfff)-0x800;
2134 noise=(filt*noiselevel)>>16;
2135 newsig=s2->signal[s2y][x] + noise;
2136 if (newsig>120) newsig=120;
2137 if (newsig<0) newsig=0;
2138 it->signal[y][x]=newsig;
2148 if (it->hashnoise_times[lineno]) {
2149 int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno];
2151 if (hnt>=0 && hnt<ANALOGTV_PIC_LEN) {
2153 double cur=frand(150.0)-20.0;
2154 int len=random()%15+3;
2155 if (len > ANALOGTV_PIC_LEN-hnt) len=ANALOGTV_PIC_LEN-hnt;
2156 for (i=0; i<len; i++) {
2157 double sig=signal[hnt];
2160 cur += frand(5.0)-5.0;
2161 maxampl = maxampl*0.9;
2172 analogtv_reception_update(analogtv_reception *rec)
2176 if (rec->multipath > 0.0) {
2177 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2178 rec->ghostfir2[i] +=
2179 -(rec->ghostfir2[i]/16.0) + rec->multipath * (frand(0.02)-0.01);
2181 if (random()%20==0) {
2182 rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)]
2183 = rec->multipath * (frand(0.08)-0.04);
2185 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2186 rec->ghostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i];
2190 rec->hfloss2 += -(rec->hfloss2/16.0) + rec->multipath * (frand(0.08)-0.04);
2191 rec->hfloss = 0.5*rec->hfloss + 0.5*rec->hfloss2;
2195 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2196 rec->ghostfir[i] = (i>=ANALOGTV_GHOSTFIR_LEN/2) ? ((i&1) ? +0.04 : -0.08) /ANALOGTV_GHOSTFIR_LEN
2203 /* jwz: since MacOS doesn't have "6x10", I dumped this font to a PNG...
2206 #include "images/gen/6x10font_png.h"
2209 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
2210 int w, int h, char *fontname)
2217 XWindowAttributes xgwa;
2222 XGetWindowAttributes (dpy, window, &xgwa);
2224 if (fontname && !strcmp (fontname, "6x10")) {
2227 XWindowAttributes xgwa;
2229 Pixmap p = image_data_to_pixmap (dpy, window,
2230 _6x10font_png, sizeof(_6x10font_png),
2231 &pix_w, &pix_h, &m);
2232 XImage *im = XGetImage (dpy, p, 0, 0, pix_w, pix_h, ~0L, ZPixmap);
2233 XImage *mm = XGetImage (dpy, m, 0, 0, pix_w, pix_h, 1, XYPixmap);
2234 unsigned long black = BlackPixelOfScreen (DefaultScreenOfDisplay (dpy));
2237 XFreePixmap (dpy, p);
2238 XFreePixmap (dpy, m);
2239 if (pix_w != 256*7) abort();
2240 if (pix_h != 10) abort();
2242 XGetWindowAttributes (dpy, window, &xgwa);
2243 f->text_im = XCreateImage (dpy, xgwa.visual, 1, XYBitmap, 0, 0,
2244 pix_w, pix_h, 8, 0);
2245 f->text_im->data = malloc (f->text_im->bytes_per_line * f->text_im->height);
2247 /* Convert deep image to 1 bit */
2248 for (y = 0; y < pix_h; y++)
2249 for (x = 0; x < pix_w; x++)
2250 XPutPixel (f->text_im, x, y,
2251 (XGetPixel (mm, x, y)
2252 ? XGetPixel (im, x, y) == black
2257 } else if (fontname) {
2259 font = load_font_retry (dpy, fontname);
2261 fprintf(stderr, "analogtv: can't load font %s\n", fontname);
2265 text_pm=XCreatePixmap(dpy, window, 256*f->char_w, f->char_h, 1);
2267 memset(&gcv, 0, sizeof(gcv));
2271 gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
2273 jwxyz_XSetAntiAliasing (dpy, gc, False);
2276 XSetForeground(dpy, gc, 0);
2277 XFillRectangle(dpy, text_pm, gc, 0, 0, 256*f->char_w, f->char_h);
2278 XSetForeground(dpy, gc, 1);
2279 for (i=0; i<256; i++) {
2281 int x=f->char_w*i+1;
2282 int y=f->char_h*8/10;
2283 XDrawString(dpy, text_pm, gc, x, y, &c, 1);
2285 f->text_im = XGetImage(dpy, text_pm, 0, 0, 256*f->char_w, f->char_h,
2288 XWriteBitmapFile(dpy, "/tmp/tvfont.xbm", text_pm,
2289 256*f->char_w, f->char_h, -1, -1);
2292 XFreePixmap(dpy, text_pm);
2294 f->text_im = XCreateImage(dpy, xgwa.visual, 1, XYPixmap, 0, 0,
2295 256*f->char_w, f->char_h, 8, 0);
2296 f->text_im->data = (char *)calloc(f->text_im->height,
2297 f->text_im->bytes_per_line);
2305 analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
2307 if (x<0 || x>=f->char_w) return 0;
2308 if (y<0 || y>=f->char_h) return 0;
2309 if (c<0 || c>=256) return 0;
2310 return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
2314 analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
2316 if (x<0 || x>=f->char_w) return;
2317 if (y<0 || y>=f->char_h) return;
2318 if (c<0 || c>=256) return;
2320 XPutPixel(f->text_im, c*f->char_w + x, y, value);
2324 analogtv_font_set_char(analogtv_font *f, int c, char *s)
2328 if (c<0 || c>=256) return;
2330 for (y=0; y<f->char_h; y++) {
2331 for (x=0; x<f->char_w; x++) {
2333 value=(*s==' ') ? 0 : 1;
2334 analogtv_font_set_pixel(f, c, x, y, value);
2341 analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
2344 for (i=0; i<4; i++) {
2345 double w=90.0*i + phase;
2346 double val=luma + chroma * (cos(3.1415926/180.0*w));
2347 if (val<0.0) val=0.0;
2348 if (val>127.0) val=127.0;
2354 analogtv_draw_solid(analogtv_input *input,
2355 int left, int right, int top, int bot,
2360 if (right-left<4) right=left+4;
2361 if (bot-top<1) bot=top+1;
2363 for (y=top; y<bot; y++) {
2364 for (x=left; x<right; x++) {
2365 input->signal[y][x] = ntsc[x&3];
2372 analogtv_draw_solid_rel_lcp(analogtv_input *input,
2373 double left, double right, double top, double bot,
2374 double luma, double chroma, double phase)
2378 int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top);
2379 int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot);
2380 int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left);
2381 int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right);
2383 analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
2384 analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
2389 analogtv_draw_char(analogtv_input *input, analogtv_font *f,
2390 int c, int x, int y, int ntsc[4])
2392 int yc,xc,ys,xs,pix;
2394 for (yc=0; yc<f->char_h; yc++) {
2395 for (ys=y + yc*f->y_mult; ys<y + (yc+1)*f->y_mult; ys++) {
2396 if (ys<0 || ys>=ANALOGTV_V) continue;
2398 for (xc=0; xc<f->char_w; xc++) {
2399 pix=analogtv_font_pixel(f, c, xc, yc);
2401 for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
2402 if (xs<0 || xs>=ANALOGTV_H) continue;
2404 input->signal[ys][xs] = ntsc[xs&3];
2413 analogtv_draw_string(analogtv_input *input, analogtv_font *f,
2414 char *s, int x, int y, int ntsc[4])
2417 analogtv_draw_char(input, f, *s, x, y, ntsc);
2424 analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
2425 char *s, int x, int y, int ntsc[4])
2427 int width=strlen(s) * f->char_w * 4;
2430 analogtv_draw_string(input, f, s, x, y, ntsc);