1 /* analogtv, Copyright (c) 2003-2018 Trevor Blackwell <tlb@tlb.org>
2 *
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
9 * implied warranty.
10 */
11
12 /*
13
14 This is the code for implementing something that looks like a conventional
15 analog TV set. It simulates the following characteristics of standard
16 televisions:
17
18 - Realistic rendering of a composite video signal
19 - Compression & brightening on the right, as the scan gets truncated
20 because of saturation in the flyback transformer
21 - Blooming of the picture dependent on brightness
22 - Overscan, cutting off a few pixels on the left side.
23 - Colored text in mixed graphics/text modes
24
25 It's amazing how much it makes your high-end monitor look like at large
26 late-70s TV. All you need is to put a big "Solid State" logo in curly script
27 on it and you'd be set.
28
29 In DirectColor or TrueColor modes, it generates pixel values
30 directly from RGB values it calculates across each scan line. In
31 PseudoColor mode, it consider each possible pattern of 5 preceding
32 bit values in each possible position modulo 4 and allocates a color
33 for each. A few things, like the brightening on the right side as
34 the horizontal trace slows down, aren't done in PseudoColor.
35
36 I originally wrote it for the Apple ][ emulator, and generalized it
37 here for use with a rewrite of xteevee and possibly others.
38
39 A maxim of technology is that failures reveal underlying mechanism.
40 A good way to learn how something works is to push it to failure.
41 The way it fails will usually tell you a lot about how it works. The
42 corollary for this piece of software is that in order to emulate
43 realistic failures of a TV set, it has to work just like a TV set.
44 So there is lots of DSP-style emulation of analog circuitry for
45 things like color decoding, H and V sync following, and more. In
46 2003, computers are just fast enough to do this at television signal
47 rates. We use a 14 MHz sample rate here, so we can do on the order
48 of a couple hundred instructions per sample and keep a good frame
49 rate.
50
51 Trevor Blackwell <tlb@tlb.org>
52 */
53
54 /*
55 2014-04-20, Dave Odell <dmo2118@gmail.com>:
56 API change: Folded analogtv_init_signal and *_add_signal into *_draw().
57 Added SMP support.
58 Replaced doubles with floats, including constants and transcendental functions.
59 Fixed a bug or two.
60 */
61
62 /* 2015-02-27, Tomasz Sulej <tomeksul@gmail.com>:
63 - tint_control variable is used now
64 - removed unusable hashnoise code
65 */
66
67 /*
68 2016-10-09, Dave Odell <dmo2118@gmail.com>:
69 Updated for new xshm.c.
70 */
71
72 #ifdef HAVE_JWXYZ
73 # include "jwxyz.h"
74 #else /* !HAVE_JWXYZ */
75 # include <X11/Xlib.h>
76 # include <X11/Xutil.h>
77 #endif
78 #include <limits.h>
79
80 #include <assert.h>
81 #include <errno.h>
82 #include "utils.h"
83 #include "resources.h"
84 #include "analogtv.h"
85 #include "yarandom.h"
86 #include "grabscreen.h"
87 #include "visual.h"
88 #include "font-retry.h"
89 #include "ximage-loader.h"
90
91 /* #define DEBUG 1 */
92
93 #if defined(DEBUG) && (defined(__linux) || defined(__FreeBSD__))
94 /* only works on linux + freebsd */
95 #include <machine/cpufunc.h>
96
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) \
101 do { \
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); \
106 printf("\n"); \
107 } while (0)
108
109 #else
110
111 #define DTIME_DECL
112 #define DTIME_START do { } while (0)
113 #define DTIME do { } while (0)
114 #define DTIME_SHOW(DIV) do { } while (0)
115
116 #endif
117
118
119 #define FASTRND_A 1103515245
120 #define FASTRND_C 12345
121 #define FASTRND (fastrnd = fastrnd*FASTRND_A+FASTRND_C)
122
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);
125
puramp(const analogtv * it,float tc,float start,float over)126 static float puramp(const analogtv *it, float tc, float start, float over)
127 {
128 float pt=it->powerup-start;
129 float ret;
130 if (pt<0.0f) return 0.0f;
131 if (pt>900.0f || pt/tc>8.0f) return 1.0f;
132
133 ret=(1.0f-expf(-pt/tc))*over;
134 if (ret>1.0f) return 1.0f;
135 return ret*ret;
136 }
137
138 /*
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:
144
145 http://www.ee.washington.edu/conselec/CE/kuhn/ntsc/95x4.htm
146 http://www.ntsc-tv.com/ntsc-index-02.htm
147
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.
153
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
158 looks too slow.
159 */
160
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;
165
166 typedef union {
167 float f;
168 int i;
169 } float_extract_t;
170
171 static void
analogtv_init(void)172 analogtv_init(void)
173 {
174 int i;
175 {
176 unsigned int localbyteorder_loc = (MSBFirst<<24) | (LSBFirst<<0);
177 localbyteorder=*(char *)&localbyteorder_loc;
178 }
179
180 if (1) {
181 float_extract_t fe;
182 int ans;
183
184 float_extraction_works=1;
185 for (i=0; i<256*4; i++) {
186 fe.f=float_low8_ofs+(double)i;
187 ans=fe.i&0x3ff;
188 if (ans != i) {
189 #ifdef DEBUG
190 printf("Float extraction failed for %d => %d\n",i,ans);
191 #endif
192 float_extraction_works=0;
193 break;
194 }
195 }
196 }
197
198 }
199
200 void
analogtv_set_defaults(analogtv * it,char * prefix)201 analogtv_set_defaults(analogtv *it, char *prefix)
202 {
203 char buf[256];
204
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;
216 it->powerup=1000.0;
217
218 it->hashnoise_rpm=0;
219 it->hashnoise_on=0;
220 it->hashnoise_enable=1;
221
222 it->horiz_desync=frand(10.0)-5.0;
223 it->squeezebottom=frand(5.0)-1.0;
224
225 #ifdef DEBUG
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",
237 it->hashnoise_rpm);
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);
247
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);
261
262 #endif
263
264 }
265
266 extern Bool mono_p; /* shoot me */
267
268 static void
analogtv_free_image(analogtv * it)269 analogtv_free_image(analogtv *it)
270 {
271 if (it->image) {
272 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
273 it->image=NULL;
274 }
275 }
276
277 static void
analogtv_alloc_image(analogtv * it)278 analogtv_alloc_image(analogtv *it)
279 {
280 /* On failure, it->image is NULL. */
281
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;
286
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);
290
291 if (it->image) {
292 memset (it->image->data, 0, it->image->height * it->image->bytes_per_line);
293 } else {
294 /* Not enough memory. Maybe try a smaller window. */
295 fprintf(stderr, "analogtv: %s\n", strerror(ENOMEM));
296 }
297 }
298
299
300 static void
analogtv_configure(analogtv * it)301 analogtv_configure(analogtv *it)
302 {
303 int oldwidth=it->usewidth;
304 int oldheight=it->useheight;
305 int wlim,hlim,height_diff;
306
307 /* If the window is very small, don't let the image we draw get lower
308 than the actual TV resolution (266x200.)
309
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.
313
314 Otherwise, center the image either horizontally or vertically,
315 letterboxing or pillarboxing (but not both).
316
317 If it's very close (2.5%) to a multiple of VISLINES, make it exact
318 For example, it maps 1024 => 1000.
319 */
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;
325 float ratio;
326 float height_snap=0.025;
327
328 hlim = it->xgwa.height;
329 wlim = it->xgwa.width;
330 ratio = wlim / (float) hlim;
331
332 #if defined(HAVE_MOBILE) || defined(NO_CONSTRAIN_RATIO)
333 /* Fill the whole iPhone screen, even though that distorts the image. */
334 min_ratio = 0;
335 max_ratio = 10;
336 #endif
337
338 if (wlim < 266 || hlim < 200)
339 {
340 wlim = 266;
341 hlim = 200;
342 # ifdef DEBUG
343 fprintf (stderr,
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);
347 # endif
348 }
349 else if (ratio > min_ratio && ratio < max_ratio)
350 {
351 # ifdef DEBUG
352 fprintf (stderr,
353 "size: close enough: %dx%d (%.3f < %.3f < %.3f)\n",
354 wlim, hlim, min_ratio, ratio, max_ratio);
355 # endif
356 }
357 else if (ratio >= max_ratio)
358 {
359 wlim = hlim*max_ratio;
360 # ifdef DEBUG
361 fprintf (stderr,
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);
365 # endif
366 }
367 else /* ratio <= min_ratio */
368 {
369 hlim = wlim/min_ratio;
370 # ifdef DEBUG
371 fprintf (stderr,
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);
375 # endif
376 }
377
378 if (ratio < crazy_min_ratio || ratio > crazy_max_ratio)
379 {
380 if (ratio < crazy_min_ratio)
381 hlim = it->xgwa.height;
382 else
383 wlim = it->xgwa.width;
384 # ifdef DEBUG
385 fprintf (stderr,
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);
389 # endif
390 }
391
392
393 height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
394 if (height_diff != 0 && abs(height_diff) < hlim * height_snap)
395 {
396 hlim -= height_diff;
397 }
398
399
400 /* Most times this doesn't change */
401 if (wlim != oldwidth || hlim != oldheight) {
402
403 it->usewidth=wlim;
404 it->useheight=hlim;
405
406 it->xrepl=1+it->usewidth/640;
407 if (it->xrepl>2) it->xrepl=2;
408 it->subwidth=it->usewidth/it->xrepl;
409
410 analogtv_free_image(it);
411 analogtv_alloc_image(it);
412 }
413
414 it->screen_xo = (it->xgwa.width-it->usewidth)/2;
415 it->screen_yo = (it->xgwa.height-it->useheight)/2;
416 it->need_clear=1;
417 }
418
419 void
analogtv_reconfigure(analogtv * it)420 analogtv_reconfigure(analogtv *it)
421 {
422 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
423 analogtv_configure(it);
424 }
425
426 /* Can be any power-of-two <= 32. 16 a slightly better choice for 2-3 threads. */
427 #define ANALOGTV_SUBTOTAL_LEN 32
428
429 typedef struct analogtv_thread_s
430 {
431 analogtv *it;
432 unsigned thread_id;
433 size_t signal_start, signal_end;
434 } analogtv_thread;
435
436 #define SIGNAL_OFFSET(thread_id) \
437 ((ANALOGTV_SIGNAL_LEN * (thread_id) / threads->count) & align)
438
analogtv_thread_create(void * thread_raw,struct threadpool * threads,unsigned thread_id)439 static int analogtv_thread_create(void *thread_raw, struct threadpool *threads,
440 unsigned thread_id)
441 {
442 analogtv_thread *thread = (analogtv_thread *)thread_raw;
443 unsigned align;
444
445 thread->it = GET_PARENT_OBJ(analogtv, threads, threads);
446 thread->thread_id = thread_id;
447
448 align = thread_memory_alignment(thread->it->dpy) /
449 sizeof(thread->it->signal_subtotals[0]);
450 if (!align)
451 align = 1;
452 align = ~(align * ANALOGTV_SUBTOTAL_LEN - 1);
453
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);
458
459 return 0;
460 }
461
analogtv_thread_destroy(void * thread_raw)462 static void analogtv_thread_destroy(void *thread_raw)
463 {
464 }
465
466 analogtv *
analogtv_allocate(Display * dpy,Window window)467 analogtv_allocate(Display *dpy, Window window)
468 {
469 static const struct threadpool_class cls = {
470 sizeof(analogtv_thread),
471 analogtv_thread_create,
472 analogtv_thread_destroy
473 };
474
475 XGCValues gcv;
476 analogtv *it=NULL;
477 int i;
478 const size_t rx_signal_len = ANALOGTV_SIGNAL_LEN + 2*ANALOGTV_H;
479
480 analogtv_init();
481
482 it=(analogtv *)calloc(1,sizeof(analogtv));
483 if (!it) return 0;
484 it->threads.count=0;
485 it->rx_signal=NULL;
486 it->signal_subtotals=NULL;
487
488 it->dpy=dpy;
489 it->window=window;
490
491 if (thread_malloc((void **)&it->rx_signal, dpy,
492 sizeof(it->rx_signal[0]) * rx_signal_len))
493 goto fail;
494
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)))
499 goto fail;
500
501 if (threadpool_create(&it->threads, &cls, dpy, hardware_concurrency(dpy)))
502 goto fail;
503
504 assert(it->threads.count);
505
506 it->shrinkpulse=-1;
507
508 it->n_colors=0;
509
510 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
511
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")) {
518 it->use_cmap=1;
519 } else {
520 it->use_cmap=0;
521 }
522 it->use_color=!mono_p;
523 }
524 else if (it->visclass == PseudoColor || it->visclass == StaticColor) {
525 it->use_cmap=1;
526 it->use_color=!mono_p;
527 }
528 else {
529 it->use_cmap=1;
530 it->use_color=0;
531 }
532
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;
538 if (!it->use_cmap) {
539 /* Is there a standard way to do this? Does this handle all cases? */
540 int shift, prec;
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;
550 }
551 }
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");
554 goto fail;
555 }
556
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);
563 }
564
565 }
566
567 gcv.background=get_pixel_resource(it->dpy, it->colormap,
568 "background", "Background");
569
570 it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
571 # ifdef HAVE_JWXYZ
572 jwxyz_XSetAntiAliasing (it->dpy, it->gc, False);
573 # endif
574 XSetWindowBackground(it->dpy, it->window, gcv.background);
575 XClearWindow(dpy,window);
576
577 analogtv_configure(it);
578
579 return it;
580
581 fail:
582 if (it) {
583 if(it->threads.count)
584 threadpool_destroy(&it->threads);
585 thread_free(it->signal_subtotals);
586 thread_free(it->rx_signal);
587 free(it);
588 }
589 return NULL;
590 }
591
592 void
analogtv_release(analogtv * it)593 analogtv_release(analogtv *it)
594 {
595 if (it->image) {
596 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
597 it->image=NULL;
598 }
599 if (it->gc) XFreeGC(it->dpy, it->gc);
600 it->gc=NULL;
601 if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
602 it->n_colors=0;
603 threadpool_destroy(&it->threads);
604 thread_free(it->rx_signal);
605 thread_free(it->signal_subtotals);
606 free(it);
607 }
608
609
610 /*
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.
615
616 The tint control, FWIW, just adds a phase shift to the chroma signal,
617 and the color control controls the amplitude.
618
619 In text modes (colormode==0) the system disabled the color burst, and no
620 color was detected by the monitor.
621
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.
629
630 If we're using a colormap, set it up.
631 */
632 int
analogtv_set_demod(analogtv * it)633 analogtv_set_demod(analogtv *it)
634 {
635 int y_levels=10,i_levels=5,q_levels=5;
636
637 /*
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.
641
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
645 so. */
646
647 cmap_again:
648 if (it->use_cmap && !it->n_colors) {
649
650 if (it->n_colors) {
651 XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
652 it->n_colors=0;
653 }
654
655 {
656 int yli,qli,ili;
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;
662 int r,g,b;
663 XColor col;
664
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);
668
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);
672 if (r<0) r=0;
673 if (r>65535) r=65535;
674 if (g<0) g=0;
675 if (g>65535) g=65535;
676 if (b<0) b=0;
677 if (b>65535) b=65535;
678
679 #ifdef DEBUG
680 printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n",
681 interpy, interpi, interpq,
682 r/256,g/256,b/256);
683 #endif
684
685 col.red=r;
686 col.green=g;
687 col.blue=b;
688 col.pixel=0;
689 if (!XAllocColor(it->dpy, it->colormap, &col)) {
690 if (q_levels > y_levels*4/12)
691 q_levels--;
692 else if (i_levels > y_levels*5/12)
693 i_levels--;
694 else
695 y_levels--;
696
697 if (y_levels<2)
698 return -1;
699 goto cmap_again;
700 }
701 it->colors[it->n_colors++]=col.pixel;
702 }
703 }
704 }
705
706 it->cmap_y_levels=y_levels;
707 it->cmap_i_levels=i_levels;
708 it->cmap_q_levels=q_levels;
709 }
710 }
711
712 return 0;
713 }
714
715 #if 0
716 unsigned int
717 analogtv_line_signature(analogtv_input *input, int lineno)
718 {
719 int i;
720 char *origsignal=&input->signal[(lineno+input->vsync)
721 %ANALOGTV_V][input->line_hsync[lineno]];
722 unsigned int hash=0;
723
724 /* probably lame */
725 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
726 int c=origsignal[i];
727 hash = hash + (hash<<17) + c;
728 }
729
730 hash += input->line_hsync[lineno];
731 hash ^= hash >> 2;
732 /*
733 hash += input->hashnoise_times[lineno];
734 hash ^= hash >> 2;
735 */
736
737 return hash;
738 }
739 #endif
740
741
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.
746
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.
754
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.
758
759 */
760
761 static void
analogtv_ntsc_to_yiq(const analogtv * it,int lineno,const float * signal,int start,int end,struct analogtv_yiq_s * it_yiq)762 analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
763 int start, int end, struct analogtv_yiq_s *it_yiq)
764 {
765 enum {MAXDELAY=32};
766 int i;
767 const float *sp;
768 int phasecorr=(signal-it->rx_signal)&3;
769 struct analogtv_yiq_s *yiq;
770 int colormode;
771 float agclevel=it->agclevel;
772 float brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
773 float delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
774 float multiq2[4];
775
776 {
777
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;
782
783 colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
784
785 if (colormode) {
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];
790 }
791 }
792
793 #if 0
794 if (lineno==100) {
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]);
802 }
803 #endif
804
805 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
806 for (i=0; i<5; i++) dp[i]=0.0f;
807
808 assert(start>=0);
809 assert(end < ANALOGTV_PIC_LEN+10);
810
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;
814 i<end;
815 i++, dp--, yiq++, sp++) {
816
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
826 precision errors. */
827
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
831 Delay about 2 */
832
833 dp[0] = sp[0] * 0.0469904257251935f * agclevel;
834 dp[8] = (+1.0f*(dp[6]+dp[0])
835 +4.0f*(dp[5]+dp[1])
836 +7.0f*(dp[4]+dp[2])
837 +8.0f*(dp[3])
838 -0.0176648f*dp[12]
839 -0.4860288f*dp[10]);
840 yiq->y = dp[8] + brightadd;
841 }
842
843 if (colormode) {
844 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
845 for (i=0; i<27; i++) dp[i]=0.0;
846
847 for (i=start, yiq=it_yiq+start, sp=signal+start;
848 i<end;
849 i++, dp--, yiq++, sp++) {
850 float sig=*sp;
851
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
855 Delay about 3.
856 */
857
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]);
863
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]);
869 }
870 } else {
871 for (i=start, yiq=it_yiq+start; i<end; i++, yiq++) {
872 yiq->i = yiq->q = 0.0f;
873 }
874 }
875 }
876
877 void
analogtv_setup_teletext(analogtv_input * input)878 analogtv_setup_teletext(analogtv_input *input)
879 {
880 int x,y;
881 int teletext=ANALOGTV_BLACK_LEVEL;
882
883 /* Teletext goes in line 21. But I suspect there are other things
884 in the vertical retrace interval */
885
886 for (y=19; y<22; y++) {
887 for (x=ANALOGTV_PIC_START; x<ANALOGTV_PIC_END; x++) {
888 if ((x&7)==0) {
889 teletext=(random()&1) ? ANALOGTV_WHITE_LEVEL : ANALOGTV_BLACK_LEVEL;
890 }
891 input->signal[y][x]=teletext;
892 }
893 }
894 }
895
896 void
analogtv_setup_frame(analogtv * it)897 analogtv_setup_frame(analogtv *it)
898 {
899 /* int i,x,y;*/
900
901 it->redraw_all=0;
902
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;
909 }
910
911 /* it wasn't used
912 for (i=0; i<ANALOGTV_V; i++) {
913 it->hashnoise_times[i]=0;
914 }
915 */
916
917 /* let's leave it to process shrinkpulse */
918 if (it->hashnoise_enable && !it->hashnoise_on) {
919 if (random()%10000==0) {
920 it->hashnoise_on=1;
921 it->shrinkpulse=random()%ANALOGTV_V;
922 }
923 }
924 if (random()%1000==0) {
925 it->hashnoise_on=0;
926 }
927
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;
932 } else {
933 it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
934 if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
935 }
936 if (it->hashnoise_rpm > 0.0) {
937 int hni;
938 double hni_double;
939 int hnc=it->hashnoise_counter; /* in 24.8 format */
940
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;
945
946 while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
947 y=(hnc>>8)/ANALOGTV_H;
948 x=(hnc>>8)%ANALOGTV_H;
949
950 if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
951 it->hashnoise_times[y]=x;
952 }
953 /* hnc += hni + (int)(random()%65536)-32768; */
954 {
955 hnc += (int)(random()%65536)-32768;
956 if ((hnc >= 0) && (INT_MAX - hnc < hni)) break;
957 hnc += hni;
958 }
959 }
960 }
961 #endif /* 0 */
962
963 /* hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;*/
964
965
966 if (it->rx_signal_level != 0.0)
967 it->agclevel = 1.0/it->rx_signal_level;
968
969
970 #ifdef DEBUG2
971 printf("filter: ");
972 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
973 printf(" %0.3f",it->ghostfir[i]);
974 }
975 printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel);
976 #endif
977 }
978
979 void
analogtv_setup_sync(analogtv_input * input,int do_cb,int do_ssavi)980 analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
981 {
982 int i,lineno,vsync;
983 signed char *sig;
984
985 int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
986
987 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
988 vsync=lineno>=3 && lineno<7;
989
990 sig=input->signal[lineno];
991
992 i=ANALOGTV_SYNC_START;
993 if (vsync) {
994 while (i<ANALOGTV_BP_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
995 while (i<ANALOGTV_H) sig[i++]=synclevel;
996 } else {
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;
1000 }
1001 while (i<ANALOGTV_H) sig[i++]=ANALOGTV_BLANK_LEVEL;
1002
1003 if (do_cb) {
1004 /* 9 cycles of colorburst */
1005 for (i=ANALOGTV_CB_START; i<ANALOGTV_CB_START+36*ANALOGTV_SCALE; i+=4*ANALOGTV_SCALE) {
1006 sig[i+1] += ANALOGTV_CB_LEVEL;
1007 sig[i+3] -= ANALOGTV_CB_LEVEL;
1008 }
1009 }
1010 }
1011 }
1012
1013 static void
analogtv_sync(analogtv * it)1014 analogtv_sync(analogtv *it)
1015 {
1016 int cur_hsync=it->cur_hsync;
1017 int cur_vsync=it->cur_vsync;
1018 int lineno = 0;
1019 int i,j;
1020 float osc,filt;
1021 float *sp;
1022 float cbfc=1.0f/128.0f;
1023
1024 /* sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;*/
1025 for (i=-32*ANALOGTV_SCALE; i<32*ANALOGTV_SCALE; i++) {
1026 lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1027 sp = it->rx_signal + lineno*ANALOGTV_H;
1028 filt=0.0f;
1029 for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/(16*ANALOGTV_SCALE)) {
1030 filt += sp[j];
1031 }
1032 filt *= it->agclevel;
1033
1034 osc = (float)(ANALOGTV_V+i)/(float)ANALOGTV_V;
1035
1036 if (osc >= 1.05f+0.0002f * filt) break;
1037 }
1038 cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1039
1040 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
1041
1042 if (lineno>5*ANALOGTV_SCALE && lineno<ANALOGTV_V-3*ANALOGTV_SCALE) { /* 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*ANALOGTV_SCALE; i<8*ANALOGTV_SCALE; 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;
1049
1050 if (osc >= 1.005f + 0.0001f*filt) break;
1051 }
1052 cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
1053 }
1054
1055 it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START +
1056 ANALOGTV_H) % ANALOGTV_H;
1057
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
1061 cycles.
1062 */
1063
1064 if (lineno>15*ANALOGTV_SCALE) {
1065 sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
1066 for (i=ANALOGTV_CB_START+8*ANALOGTV_SCALE; i<ANALOGTV_CB_START+(36-8)*ANALOGTV_SCALE; i++) {
1067 it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0f-cbfc) +
1068 sp[i]*it->agclevel*cbfc;
1069 }
1070 }
1071
1072 {
1073 float tot=0.1f;
1074 float cbgain;
1075
1076 for (i=0; i<4; i++) {
1077 tot += it->cb_phase[i]*it->cb_phase[i];
1078 }
1079 cbgain = 32.0f/sqrtf(tot);
1080
1081 for (i=0; i<4; i++) {
1082 it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
1083 }
1084 }
1085
1086 #ifdef DEBUG
1087 if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n",
1088 cur_hsync,
1089 it->cb_phase[0], it->cb_phase[1],
1090 it->cb_phase[2], it->cb_phase[3]);
1091 #endif
1092
1093 /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */
1094 }
1095
1096 it->cur_hsync = cur_hsync;
1097 it->cur_vsync = cur_vsync;
1098 }
1099
1100 static double
analogtv_levelmult(const analogtv * it,int level)1101 analogtv_levelmult(const analogtv *it, int level)
1102 {
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;
1105 }
1106
1107 static int
analogtv_level(const analogtv * it,int y,int ytop,int ybot)1108 analogtv_level(const analogtv *it, int y, int ytop, int ybot)
1109 {
1110 int level;
1111 if (ybot-ytop>=7) {
1112 if (y==ytop || y==ybot-1) level=0;
1113 else if (y==ytop+1 || y==ybot-2) level=1;
1114 else level=2;
1115 }
1116 else if (ybot-ytop>=5) {
1117 if (y==ytop || y==ybot-1) level=0;
1118 else level=2;
1119 }
1120 else if (ybot-ytop>=3) {
1121 if (y==ytop) level=0;
1122 else level=2;
1123 }
1124 else {
1125 level=2;
1126 }
1127 return level;
1128 }
1129
1130 /*
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
1134 remains the same.
1135 ANALOGTV_MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
1136 it interpolates extra black lines.
1137 */
1138
1139 static void
analogtv_setup_levels(analogtv * it,double avgheight)1140 analogtv_setup_levels(analogtv *it, double avgheight)
1141 {
1142 int i,height;
1143 static const double levelfac[3]={-7.5, 5.5, 24.5};
1144
1145 for (height=0; height<avgheight+2.0 && height<=ANALOGTV_MAX_LINEHEIGHT; height++) {
1146
1147 for (i=0; i<height; i++) {
1148 it->leveltable[height][i].index = 2;
1149 }
1150
1151 if (avgheight>=3) {
1152 it->leveltable[height][0].index=0;
1153 }
1154 if (avgheight>=5) {
1155 if (height >= 1) it->leveltable[height][height-1].index=0;
1156 }
1157 if (avgheight>=7) {
1158 it->leveltable[height][1].index=1;
1159 if (height >= 2) it->leveltable[height][height-2].index=1;
1160 }
1161
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;
1165 }
1166
1167 }
1168 }
1169
rnd_combine(unsigned * a0,unsigned * c0,unsigned a1,unsigned c1)1170 static void rnd_combine(unsigned *a0, unsigned *c0, unsigned a1, unsigned c1)
1171 {
1172 *a0 = (*a0 * a1) & 0xffffffffu;
1173 *c0 = (c1 + a1 * *c0) & 0xffffffffu;
1174 }
1175
rnd_seek_ac(unsigned * a,unsigned * c,unsigned dist)1176 static void rnd_seek_ac(unsigned *a, unsigned *c, unsigned dist)
1177 {
1178 unsigned int a1 = *a, c1 = *c;
1179 *a = 1, *c = 0;
1180
1181 while(dist)
1182 {
1183 if(dist & 1)
1184 rnd_combine(a, c, a1, c1);
1185 dist >>= 1;
1186 rnd_combine(&a1, &c1, a1, c1);
1187 }
1188 }
1189
rnd_seek(unsigned a,unsigned c,unsigned rnd,unsigned dist)1190 static unsigned int rnd_seek(unsigned a, unsigned c, unsigned rnd, unsigned dist)
1191 {
1192 rnd_seek_ac(&a, &c, dist);
1193 return a * rnd + c;
1194 }
1195
analogtv_init_signal(const analogtv * it,double noiselevel,unsigned start,unsigned end)1196 static void analogtv_init_signal(const analogtv *it, double noiselevel, unsigned start, unsigned end)
1197 {
1198 float *ps=it->rx_signal + start;
1199 float *pe=it->rx_signal + end;
1200 float *p=ps;
1201 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random0, start);
1202 unsigned int fastrnd_offset;
1203 float nm1,nm2;
1204 float noisemul = sqrt(noiselevel*150)/(float)0x7fffffff;
1205
1206 fastrnd_offset = fastrnd - 0x7fffffff;
1207 nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
1208 while (p != pe) {
1209 nm2=nm1;
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;
1213 *p++ = nm1*nm2;
1214 }
1215 }
1216
analogtv_add_signal(const analogtv * it,const analogtv_reception * rec,unsigned start,unsigned end,int ec)1217 static void analogtv_add_signal(const analogtv *it, const analogtv_reception *rec, unsigned start, unsigned end, int ec)
1218 {
1219 analogtv_input *inp=rec->input;
1220 float *ps=it->rx_signal + start;
1221 float *pe=it->rx_signal + end;
1222 float *p=ps;
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);
1226 signed char *s2;
1227 int i;
1228 float level=rec->level;
1229 float hfloss=rec->hfloss;
1230 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random1, start);
1231 float dp[5];
1232
1233 const float noise_decay = 0.99995f;
1234 float noise_ampl = 1.3f * powf(noise_decay, start);
1235
1236 if (ec > end)
1237 ec = end;
1238
1239 /* assert((se-ss)%4==0 && (se-s)%4==0); */
1240
1241 for (i = start; i < ec; i++) { /* Sometimes start > ec. */
1242
1243 /* Do a big noisy transition. We can make the transition noise of
1244 high constant strength regardless of signal strength.
1245
1246 There are two separate state machines. here, One is the noise
1247 process and the other is the
1248
1249 We don't bother with the FIR filter here
1250 */
1251
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;
1256
1257 p[0] += sig0 * level * (1.0f - noise_ampl) + noise * noise_ampl;
1258
1259 noise_ampl *= noise_decay;
1260
1261 p++;
1262 s++;
1263 if (s>=se) s=ss;
1264 }
1265
1266 dp[0]=0.0;
1267 s2 = s;
1268 for (i=1; i<5; i++) {
1269 s2 -= 4;
1270 if (s2 < ss)
1271 s2 += ANALOGTV_SIGNAL_LEN;
1272 dp[i] = (float)((int)s2[0]+(int)s2[1]+(int)s2[2]+(int)s2[3]);
1273 }
1274
1275 assert(p <= pe);
1276 assert(!((pe - p) % 4));
1277 while (p != pe) {
1278 float sig0,sig1,sig2,sig3,sigr;
1279
1280 sig0=(float)s[0];
1281 sig1=(float)s[1];
1282 sig2=(float)s[2];
1283 sig3=(float)s[3];
1284
1285 dp[0]=sig0+sig1+sig2+sig3;
1286
1287 /* Get the video out signal, and add some ghosting, typical of RF
1288 monitor cables. This corresponds to a pretty long cable, but
1289 looks right to me.
1290 */
1291
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];
1295
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;
1300
1301 p += 4;
1302 s += 4;
1303 if (s>=se) s = ss + (s-se);
1304 }
1305
1306 assert(p == pe);
1307 }
1308
analogtv_thread_add_signals(void * thread_raw)1309 static void analogtv_thread_add_signals(void *thread_raw)
1310 {
1311 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1312 const analogtv *it = thread->it;
1313 unsigned i, j;
1314 unsigned subtotal_end;
1315
1316 unsigned start = thread->signal_start;
1317 while(start != thread->signal_end)
1318 {
1319 float *p;
1320
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;
1326
1327 analogtv_init_signal (it, it->noiselevel, start, end);
1328
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);
1332 }
1333
1334 assert (!(start % ANALOGTV_SUBTOTAL_LEN));
1335 assert (!(end % ANALOGTV_SUBTOTAL_LEN));
1336
1337 p = it->rx_signal + start;
1338 subtotal_end = end / ANALOGTV_SUBTOTAL_LEN;
1339 for (i = start / ANALOGTV_SUBTOTAL_LEN; i != subtotal_end; ++i) {
1340 float sum = p[0];
1341 for (j = 1; j != ANALOGTV_SUBTOTAL_LEN; ++j)
1342 sum += p[j];
1343 it->signal_subtotals[i] = sum;
1344 p += ANALOGTV_SUBTOTAL_LEN;
1345 }
1346
1347 start = end;
1348 }
1349 }
1350
analogtv_get_line(const analogtv * it,int lineno,int * slineno,int * ytop,int * ybot,unsigned * signal_offset)1351 static int analogtv_get_line(const analogtv *it, int lineno, int *slineno,
1352 int *ytop, int *ybot, unsigned *signal_offset)
1353 {
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;
1359 #if 0
1360 int linesig=analogtv_line_signature(input,lineno)
1361 + it->hashnoise_times[lineno];
1362 #endif
1363 *signal_offset = ((lineno+it->cur_vsync+ANALOGTV_V) % ANALOGTV_V) * ANALOGTV_H +
1364 it->line_hsync[lineno];
1365
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;
1370
1371 if (*ybot > *ytop+ANALOGTV_MAX_LINEHEIGHT) *ybot=*ytop+ANALOGTV_MAX_LINEHEIGHT;
1372 return 1;
1373 }
1374
1375 static void
analogtv_blast_imagerow(const analogtv * it,float * rgbf,float * rgbf_end,int ytop,int ybot)1376 analogtv_blast_imagerow(const analogtv *it,
1377 float *rgbf, float *rgbf_end,
1378 int ytop, int ybot)
1379 {
1380 int i,j,x,y;
1381 float *rpf;
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;
1387
1388 for (y=ytop; y<ybot; y++) {
1389 char *rowdata=it->image->data + y*it->image->bytes_per_line;
1390 unsigned line = y-ytop;
1391
1392 int level=it->leveltable[lineheight][line].index;
1393 float levelmult=it->leveltable[lineheight][line].value;
1394
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
1401 routines. */
1402
1403 if (level_copyfrom[level]) {
1404 memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line);
1405 }
1406 else {
1407 level_copyfrom[level] = rowdata;
1408
1409 if (0) {
1410 }
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;
1417 unsigned int pix;
1418
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]);
1429 pixelptr[0] = pix;
1430 if (xrepl>=2) {
1431 pixelptr[1] = pix;
1432 if (xrepl>=3) pixelptr[2] = pix;
1433 }
1434 pixelptr+=xrepl;
1435 }
1436 }
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;
1443 float r2,g2,b2;
1444 float_extract_t r1,g1,b1;
1445 unsigned short pix;
1446
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]);
1455 pixelptr[0] = pix;
1456 if (xrepl>=2) {
1457 pixelptr[1] = pix;
1458 if (xrepl>=3) pixelptr[2] = pix;
1459 }
1460 pixelptr+=xrepl;
1461 }
1462 }
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;
1468 unsigned short pix;
1469
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];
1478 pixelptr[0] = pix;
1479 if (xrepl>=2) {
1480 pixelptr[1] = pix;
1481 if (xrepl>=3) pixelptr[2] = pix;
1482 }
1483 pixelptr+=xrepl;
1484 }
1485 }
1486 else {
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]);
1498 }
1499 }
1500 }
1501 }
1502 }
1503 }
1504
analogtv_thread_draw_lines(void * thread_raw)1505 static void analogtv_thread_draw_lines(void *thread_raw)
1506 {
1507 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1508 const analogtv *it = thread->it;
1509
1510 int lineno;
1511
1512 float *raw_rgb_start;
1513 float *raw_rgb_end;
1514 raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
1515
1516 if (! raw_rgb_start) return;
1517
1518 raw_rgb_end=raw_rgb_start+3*it->subwidth;
1519
1520 for (lineno=ANALOGTV_TOP + thread->thread_id;
1521 lineno<ANALOGTV_BOT;
1522 lineno += it->threads.count) {
1523 int i,j,x,y;
1524
1525 int slineno, ytop, ybot;
1526 unsigned signal_offset;
1527
1528 const float *signal;
1529
1530 int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
1531 float *rgb_start, *rgb_end;
1532 float pixbright;
1533 int pixmultinc;
1534
1535 float *rrp;
1536
1537 struct analogtv_yiq_s yiq[ANALOGTV_PIC_LEN+10];
1538
1539 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot,
1540 &signal_offset))
1541 continue;
1542
1543 signal = it->rx_signal + signal_offset;
1544
1545 {
1546
1547 float bloomthisrow,shiftthisrow;
1548 float viswidth,middle;
1549 float scanwidth;
1550 int scw,scl,scr;
1551
1552 bloomthisrow = -10.0f * it->crtload[lineno];
1553 if (bloomthisrow<-10.0f) bloomthisrow=-10.0f;
1554 if (bloomthisrow>2.0f) bloomthisrow=2.0f;
1555 if (slineno<16) {
1556 shiftthisrow=it->horiz_desync * (expf(-0.17f*slineno) *
1557 (0.7f+cosf(slineno*0.6f)));
1558 } else {
1559 shiftthisrow=0.0f;
1560 }
1561
1562 viswidth=ANALOGTV_PIC_LEN * 0.79f - 5.0f*bloomthisrow;
1563 middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
1564
1565 scanwidth=it->width_control * puramp(it, 0.5f, 0.3f, 1.0f);
1566
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;
1571
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;
1578
1579 rgb_start=raw_rgb_start+scl*3;
1580 rgb_end=raw_rgb_start+scr*3;
1581
1582 assert(scanstart_i>=0);
1583
1584 #ifdef DEBUG
1585 if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
1586 lineno,
1587 scanstart_i/65536.0f,
1588 squishright_i/65536.0f,
1589 scanend_i/65536.0f,
1590 scl,scr,scw);
1591 #endif
1592 }
1593
1594 if (it->use_cmap) {
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;
1601
1602 analogtv_ntsc_to_yiq(it, lineno, signal,
1603 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1604 pixmultinc=pixrate;
1605
1606 x=0;
1607 i=scanstart_i;
1608 while (i<0 && x<it->usewidth) {
1609 XPutPixel(it->image, x, y, it->colors[0]);
1610 i+=pixmultinc;
1611 x++;
1612 }
1613
1614 while (i<scanend_i && x<it->usewidth) {
1615 float pixfrac=(i&0xffff)/65536.0f;
1616 float invpixfrac=(1.0f-pixfrac);
1617 int pati=i>>16;
1618 int yli,ili,qli,cmi;
1619
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;
1626
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);
1630 if (yli<0) yli=0;
1631 if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
1632 if (ili<0) ili=0;
1633 if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1;
1634 if (qli<0) qli=0;
1635 if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1;
1636
1637 cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli);
1638
1639 #ifdef DEBUG
1640 if ((random()%65536)==0) {
1641 printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n",
1642 interpy, interpi, interpq,
1643 yli, ili, qli,
1644 cmi);
1645 }
1646 #endif
1647
1648 for (j=0; j<it->xrepl; j++) {
1649 XPutPixel(it->image, x, y,
1650 it->colors[cmi]);
1651 x++;
1652 }
1653 if (i >= squishright_i) {
1654 pixmultinc += pixmultinc/squishdiv;
1655 }
1656 i+=pixmultinc;
1657 }
1658 while (x<it->usewidth) {
1659 XPutPixel(it->image, x, y, it->colors[0]);
1660 x++;
1661 }
1662 }
1663 }
1664 else {
1665 analogtv_ntsc_to_yiq(it, lineno, signal,
1666 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1667
1668 pixbright=it->contrast_control * puramp(it, 1.0f, 0.0f, 1.0f)
1669 / (0.5f+0.5f*it->puheight) * 1024.0f/100.0f;
1670 pixmultinc=pixrate;
1671 i=scanstart_i; rrp=rgb_start;
1672 while (i<0 && rrp!=rgb_end) {
1673 rrp[0]=rrp[1]=rrp[2]=0;
1674 i+=pixmultinc;
1675 rrp+=3;
1676 }
1677 while (i<scanend_i && rrp!=rgb_end) {
1678 float pixfrac=(i&0xffff)/65536.0f;
1679 float invpixfrac=1.0f-pixfrac;
1680 int pati=i>>16;
1681 float r,g,b;
1682
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);
1686
1687 /*
1688 According to the NTSC spec, Y,I,Q are generated as:
1689
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
1693
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
1696 CRT -- don't ask):
1697
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
1701 */
1702
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;
1706 if (r<0.0f) r=0.0f;
1707 if (g<0.0f) g=0.0f;
1708 if (b<0.0f) b=0.0f;
1709 rrp[0]=r;
1710 rrp[1]=g;
1711 rrp[2]=b;
1712
1713 if (i>=squishright_i) {
1714 pixmultinc += pixmultinc/squishdiv;
1715 pixbright += pixbright/squishdiv/2;
1716 }
1717 i+=pixmultinc;
1718 rrp+=3;
1719 }
1720 while (rrp != rgb_end) {
1721 rrp[0]=rrp[1]=rrp[2]=0.0f;
1722 rrp+=3;
1723 }
1724
1725 analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end,
1726 ytop,ybot);
1727 }
1728 }
1729
1730 free(raw_rgb_start);
1731 }
1732
1733 void
analogtv_draw(analogtv * it,double noiselevel,const analogtv_reception * const * recs,unsigned rec_count)1734 analogtv_draw(analogtv *it, double noiselevel,
1735 const analogtv_reception *const *recs, unsigned rec_count)
1736 {
1737 int i,lineno;
1738 /* int bigloadchange,drawcount;*/
1739 double baseload;
1740 int overall_top, overall_bot;
1741
1742 /* AnalogTV isn't very interesting if there isn't enough RAM. */
1743 if (!it->image)
1744 return;
1745
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;
1751
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]))));
1756
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]));
1760 }
1761
1762 analogtv_setup_frame(it);
1763 analogtv_set_demod(it);
1764
1765 it->random0 = random();
1766 it->random1 = random();
1767 it->noiselevel = noiselevel;
1768 it->recs = recs;
1769 it->rec_count = rec_count;
1770 threadpool_run(&it->threads, analogtv_thread_add_signals);
1771 threadpool_wait(&it->threads);
1772
1773 it->channel_change_cycles=0;
1774
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],
1779 &it->rx_signal[0],
1780 2*ANALOGTV_H*sizeof(it->rx_signal[0]));
1781
1782 /* Repeat for signal_subtotals. */
1783
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]));
1787
1788 analogtv_sync(it); /* Requires the add_signals be complete. */
1789
1790 baseload=0.5;
1791 /* if (it->hashnoise_on) baseload=0.5; */
1792
1793 /*bigloadchange=1;
1794 drawcount=0;*/
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));
1798
1799 analogtv_setup_levels(it, it->puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
1800
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);
1804
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))
1809 continue;
1810
1811 if (lineno==it->shrinkpulse) {
1812 baseload += 0.4;
1813 /*bigloadchange=1;*/
1814 it->shrinkpulse=-1;
1815 }
1816
1817 #if 0
1818 if (it->hashnoise_rpm>0.0 &&
1819 !(bigloadchange ||
1820 it->redraw_all ||
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])) {
1826 continue;
1827 }
1828 it->onscreen_signature[lineno] = linesig;
1829 #endif
1830 /* drawcount++;*/
1831
1832 /*
1833 Interpolate the 600-dotclock line into however many horizontal
1834 screen pixels we're using, and convert to RGB.
1835
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
1842 (bloomthisrow).
1843
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).
1849
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.
1852
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).
1862 */
1863
1864 {
1865 /* This used to be an int, I suspect by mistake. - Dave */
1866 float totsignal=0;
1867 float ncl/*,diff*/;
1868 unsigned frac;
1869 size_t end0, end1;
1870 const float *p;
1871
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++) {
1875 totsignal -= p[i];
1876 }
1877
1878 end0 = (signal_offset + ANALOGTV_PIC_LEN);
1879
1880 end1 = end0 / ANALOGTV_SUBTOTAL_LEN;
1881 for (i=signal_offset / ANALOGTV_SUBTOTAL_LEN; i<end1; i++) {
1882 totsignal += it->signal_subtotals[i];
1883 }
1884
1885 frac = end0 & (ANALOGTV_SUBTOTAL_LEN - 1);
1886 p = it->rx_signal + (end0 & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1887 for (i=0; i != frac; i++) {
1888 totsignal += p[i];
1889 }
1890
1891 totsignal *= it->agclevel;
1892 ncl = 0.95f * it->crtload[lineno-1] +
1893 0.05f*(baseload +
1894 (totsignal-30000)/100000.0f +
1895 (slineno>184 ? (slineno-184)*(lineno-184)*0.001f * it->squeezebottom
1896 : 0.0f));
1897 /*diff=ncl - it->crtload[lineno];*/
1898 /*bigloadchange = (diff>0.01 || diff<-0.01);*/
1899 it->crtload[lineno]=ncl;
1900 }
1901 }
1902
1903 threadpool_run(&it->threads, analogtv_thread_draw_lines);
1904 threadpool_wait(&it->threads);
1905
1906 #if 0
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;
1914
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);
1919 }
1920 }
1921 #endif
1922
1923 if (it->need_clear) {
1924 XClearWindow(it->dpy, it->window);
1925 it->need_clear=0;
1926 }
1927
1928 /*
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.
1932 */
1933
1934 overall_top=(int)(it->useheight*(1-it->puheight)/2);
1935 overall_bot=(int)(it->useheight*(1+it->puheight)/2);
1936
1937 if (overall_top<0) overall_top=0;
1938 if (overall_bot>it->useheight) overall_bot=it->useheight;
1939
1940 if (overall_top>0) {
1941 XClearArea(it->dpy, it->window,
1942 it->screen_xo, it->screen_yo,
1943 it->usewidth, overall_top, 0);
1944 }
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);
1949 }
1950
1951 if (overall_bot > overall_top) {
1952 put_xshm_image(it->dpy, it->window, it->gc, it->image,
1953 0, overall_top,
1954 it->screen_xo, it->screen_yo+overall_top,
1955 it->usewidth, overall_bot - overall_top,
1956 &it->shm_info);
1957 }
1958
1959 #ifdef DEBUG
1960 if (0) {
1961 struct timeval tv;
1962 double fps;
1963 char buf[256];
1964 gettimeofday(&tv,NULL);
1965
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,
1970 buf, strlen(buf));
1971
1972 it->last_display_time=tv;
1973 }
1974 #endif
1975 }
1976
1977 analogtv_input *
analogtv_input_allocate()1978 analogtv_input_allocate()
1979 {
1980 analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input));
1981
1982 return ret;
1983 }
1984
1985 /*
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.
1989
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)
1993 */
1994
1995 int
analogtv_load_ximage(analogtv * it,analogtv_input * input,XImage * pic_im,XImage * mask_im,int xoff,int yoff,int target_w,int target_h)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)
1999 {
2000 int i,x,y;
2001 int img_w,img_h;
2002 int fyx[7],fyy[7];
2003 int fix[4],fiy[4];
2004 int fqx[4],fqy[4];
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); */
2010
2011 int x_length=ANALOGTV_PIC_LEN;
2012 int y_overscan=5*ANALOGTV_SCALE; /* overscan this much top and bottom */
2013 int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
2014
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;
2017
2018 img_w = pic_im->width;
2019 img_h = pic_im->height;
2020
2021 xoff = ANALOGTV_PIC_LEN * xoff / it->xgwa.width;
2022 yoff = ANALOGTV_VISLINES * yoff / it->xgwa.height;
2023
2024 for (i=0; i<x_length+4; i++) {
2025 double phase=90.0-90.0*i;
2026 double ampl=1.0;
2027 multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
2028 }
2029
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;
2033
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);
2038 if (mask_im)
2039 mask[x] = (XGetPixel(mask_im, picx, picy1) != black);
2040 else
2041 mask[x] = 1;
2042 }
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;
2047
2048 for (x=0; x<x_length; x++) {
2049 int rawy,rawi,rawq;
2050 int filty,filti,filtq;
2051 int composite;
2052
2053 if (!mask[x]) continue;
2054
2055 /* Compute YIQ as:
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 */
2060
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;
2067
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 */
2071
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);
2079 filty = fyy[6];
2080
2081 /* Filter I at 1.5 MHz. 3 pole Butterworth from
2082 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */
2083
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);
2089 filti = fiy[3];
2090
2091 /* Filter Q at 0.5 MHz. 3 pole Butterworth from
2092 mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */
2093
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);
2099 filtq = fqy[3];
2100
2101
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;
2106
2107 input->signal[y-y_overscan+ANALOGTV_TOP+yoff][x+ANALOGTV_PIC_START+xoff] = composite;
2108 }
2109 }
2110
2111 return 1;
2112 }
2113
2114 #if 0
2115 void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
2116 {
2117 int x,y,newsig;
2118 int change=random()%ANALOGTV_V;
2119 unsigned int fastrnd=random();
2120 double hso=(int)(random()%1000)-500;
2121 int yofs=random()%ANALOGTV_V;
2122 int noise;
2123
2124 for (y=change; y<ANALOGTV_V; y++) {
2125 int s2y=(y+yofs)%ANALOGTV_V;
2126 int filt=0;
2127 int noiselevel=60000 / (y-change+100);
2128
2129 it->line_hsync[y] = s2->line_hsync[y] + (int)hso;
2130 hso *= 0.9;
2131 for (x=0; x<ANALOGTV_H; x++) {
2132 FASTRND;
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;
2139 }
2140 }
2141 s2->vsync=yofs;
2142 }
2143 #endif
2144
2145
2146 #ifdef FIXME
2147 /* add hash */
2148 if (it->hashnoise_times[lineno]) {
2149 int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno];
2150
2151 if (hnt>=0 && hnt<ANALOGTV_PIC_LEN) {
2152 double maxampl=1.0;
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];
2158
2159 sig += cur*maxampl;
2160 cur += frand(5.0)-5.0;
2161 maxampl = maxampl*0.9;
2162
2163 signal[hnt]=sig;
2164 hnt++;
2165 }
2166 }
2167 }
2168 #endif
2169
2170
2171 void
analogtv_reception_update(analogtv_reception * rec)2172 analogtv_reception_update(analogtv_reception *rec)
2173 {
2174 int i;
2175
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);
2180 }
2181 if (random()%20==0) {
2182 rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)]
2183 = rec->multipath * (frand(0.08)-0.04);
2184 }
2185 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2186 rec->ghostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i];
2187 }
2188
2189 if (0) {
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;
2192 }
2193
2194 } else {
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
2197 : 0.0;
2198 }
2199 }
2200 }
2201
2202
2203 /* jwz: since MacOS doesn't have "6x10", I dumped this font to a PNG...
2204 */
2205
2206 #include "images/gen/6x10font_png.h"
2207
2208 void
analogtv_make_font(Display * dpy,Window window,analogtv_font * f,int w,int h,char * fontname)2209 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
2210 int w, int h, char *fontname)
2211 {
2212 int i;
2213 XFontStruct *font;
2214 Pixmap text_pm;
2215 GC gc;
2216 XGCValues gcv;
2217 XWindowAttributes xgwa;
2218
2219 f->char_w = w;
2220 f->char_h = h;
2221
2222 XGetWindowAttributes (dpy, window, &xgwa);
2223
2224 if (fontname && !strcmp (fontname, "6x10")) {
2225
2226 int pix_w, pix_h;
2227 XWindowAttributes xgwa;
2228 Pixmap m = 0;
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));
2235 int x, y;
2236
2237 XFreePixmap (dpy, p);
2238 XFreePixmap (dpy, m);
2239 if (pix_w != 256*7) abort();
2240 if (pix_h != 10) abort();
2241
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);
2246
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
2253 : 0));
2254 XDestroyImage (im);
2255 XDestroyImage (mm);
2256
2257 } else if (fontname) {
2258
2259 font = load_font_retry (dpy, fontname);
2260 if (!font) {
2261 fprintf(stderr, "analogtv: can't load font %s\n", fontname);
2262 abort();
2263 }
2264
2265 text_pm=XCreatePixmap(dpy, window, 256*f->char_w, f->char_h, 1);
2266
2267 memset(&gcv, 0, sizeof(gcv));
2268 gcv.foreground=1;
2269 gcv.background=0;
2270 gcv.font=font->fid;
2271 gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
2272 # ifdef HAVE_JWXYZ
2273 jwxyz_XSetAntiAliasing (dpy, gc, False);
2274 # endif
2275
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++) {
2280 char c=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);
2284 }
2285 f->text_im = XGetImage(dpy, text_pm, 0, 0, 256*f->char_w, f->char_h,
2286 1, XYPixmap);
2287 # if 0
2288 XWriteBitmapFile(dpy, "/tmp/tvfont.xbm", text_pm,
2289 256*f->char_w, f->char_h, -1, -1);
2290 # endif
2291 XFreeGC(dpy, gc);
2292 XFreePixmap(dpy, text_pm);
2293 } else {
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);
2298
2299 }
2300 f->x_mult=4 * ANALOGTV_SCALE;
2301 f->y_mult=2 * ANALOGTV_SCALE;
2302 }
2303
2304 int
analogtv_font_pixel(analogtv_font * f,int c,int x,int y)2305 analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
2306 {
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;
2311 }
2312
2313 void
analogtv_font_set_pixel(analogtv_font * f,int c,int x,int y,int value)2314 analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
2315 {
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;
2319
2320 XPutPixel(f->text_im, c*f->char_w + x, y, value);
2321 }
2322
2323 void
analogtv_font_set_char(analogtv_font * f,int c,char * s)2324 analogtv_font_set_char(analogtv_font *f, int c, char *s)
2325 {
2326 int value,x,y;
2327
2328 if (c<0 || c>=256) return;
2329
2330 for (y=0; y<f->char_h; y++) {
2331 for (x=0; x<f->char_w; x++) {
2332 if (!*s) return;
2333 value=(*s==' ') ? 0 : 1;
2334 analogtv_font_set_pixel(f, c, x, y, value);
2335 s++;
2336 }
2337 }
2338 }
2339
2340 void
analogtv_lcp_to_ntsc(double luma,double chroma,double phase,int ntsc[4])2341 analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
2342 {
2343 int i;
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;
2349 ntsc[i]=(int)val;
2350 }
2351 }
2352
2353 void
analogtv_draw_solid(analogtv_input * input,int left,int right,int top,int bot,int ntsc[4])2354 analogtv_draw_solid(analogtv_input *input,
2355 int left, int right, int top, int bot,
2356 int ntsc[4])
2357 {
2358 int x,y;
2359
2360 if (right-left<4) right=left+4;
2361 if (bot-top<1) bot=top+1;
2362
2363 for (y=top; y<bot; y++) {
2364 for (x=left; x<right; x++) {
2365 input->signal[y][x] = ntsc[x&3];
2366 }
2367 }
2368 }
2369
2370
2371 void
analogtv_draw_solid_rel_lcp(analogtv_input * input,double left,double right,double top,double bot,double luma,double chroma,double phase)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)
2375 {
2376 int ntsc[4];
2377
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);
2382
2383 analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
2384 analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
2385 }
2386
2387
2388 void
analogtv_draw_char(analogtv_input * input,analogtv_font * f,int c,int x,int y,int ntsc[4])2389 analogtv_draw_char(analogtv_input *input, analogtv_font *f,
2390 int c, int x, int y, int ntsc[4])
2391 {
2392 int yc,xc,ys,xs,pix;
2393
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;
2397
2398 for (xc=0; xc<f->char_w; xc++) {
2399 pix=analogtv_font_pixel(f, c, xc, yc);
2400
2401 for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
2402 if (xs<0 || xs>=ANALOGTV_H) continue;
2403 if (pix) {
2404 input->signal[ys][xs] = ntsc[xs&3];
2405 }
2406 }
2407 }
2408 }
2409 }
2410 }
2411
2412 void
analogtv_draw_string(analogtv_input * input,analogtv_font * f,char * s,int x,int y,int ntsc[4])2413 analogtv_draw_string(analogtv_input *input, analogtv_font *f,
2414 char *s, int x, int y, int ntsc[4])
2415 {
2416 while (*s) {
2417 analogtv_draw_char(input, f, *s, x, y, ntsc);
2418 x += f->char_w * f->x_mult;
2419 s++;
2420 }
2421 }
2422
2423 void
analogtv_draw_string_centered(analogtv_input * input,analogtv_font * f,char * s,int x,int y,int ntsc[4])2424 analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
2425 char *s, int x, int y, int ntsc[4])
2426 {
2427 int width=strlen(s) * f->char_w * f->x_mult;
2428 x -= width/2;
2429
2430 analogtv_draw_string(input, f, s, x, y, ntsc);
2431 }
2432