summaryrefslogtreecommitdiffstats
path: root/hacks/analogtv.h
blob: e3170f1d2590d1eb029fc953b1987df3eb9bdc96 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
/* analogtv, Copyright (c) 2003-2018 Trevor Blackwell <tlb@tlb.org>
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or
 * implied warranty.
 */

#ifndef _XSCREENSAVER_ANALOGTV_H
#define _XSCREENSAVER_ANALOGTV_H

#include "thread_util.h"
#include "xshm.h"

#if defined(USE_IPHONE) || defined(HAVE_ANDROID)
# define HAVE_MOBILE
#endif

/*
  You'll need these to generate standard NTSC TV signals
 */
enum {
  /* We don't handle interlace here */
  ANALOGTV_V=262,
  ANALOGTV_TOP=30,
  ANALOGTV_VISLINES=200,
  ANALOGTV_BOT=ANALOGTV_TOP + ANALOGTV_VISLINES,

  /* This really defines our sampling rate, 4x the colorburst
     frequency. Handily equal to the Apple II's dot clock.
     You could also make a case for using 3x the colorburst freq,
     but 4x isn't hard to deal with. */
  ANALOGTV_H=912,

  /* Each line is 63500 nS long. The sync pulse is 4700 nS long, etc.
     Define sync, back porch, colorburst, picture, and front porch
     positions */
  ANALOGTV_SYNC_START=0,
  ANALOGTV_BP_START=4700*ANALOGTV_H/63500,
  ANALOGTV_CB_START=5800*ANALOGTV_H/63500,
  /* signal[row][ANALOGTV_PIC_START] is the first displayed pixel */
  ANALOGTV_PIC_START=9400*ANALOGTV_H/63500,
  ANALOGTV_PIC_LEN=52600*ANALOGTV_H/63500,
  ANALOGTV_FP_START=62000*ANALOGTV_H/63500,
  ANALOGTV_PIC_END=ANALOGTV_FP_START,

  /* TVs scan past the edges of the picture tube, so normally you only
     want to use about the middle 3/4 of the nominal scan line.
  */
  ANALOGTV_VIS_START=ANALOGTV_PIC_START + (ANALOGTV_PIC_LEN*1/8),
  ANALOGTV_VIS_END=ANALOGTV_PIC_START + (ANALOGTV_PIC_LEN*7/8),
  ANALOGTV_VIS_LEN=ANALOGTV_VIS_END-ANALOGTV_VIS_START,

  ANALOGTV_HASHNOISE_LEN=6,

  ANALOGTV_GHOSTFIR_LEN=4,

  /* analogtv.signal is in IRE units, as defined below: */
  ANALOGTV_WHITE_LEVEL=100,
  ANALOGTV_GRAY50_LEVEL=55,
  ANALOGTV_GRAY30_LEVEL=35,
  ANALOGTV_BLACK_LEVEL=10,
  ANALOGTV_BLANK_LEVEL=0,
  ANALOGTV_SYNC_LEVEL=-40,
  ANALOGTV_CB_LEVEL=20,

  ANALOGTV_SIGNAL_LEN=ANALOGTV_V*ANALOGTV_H,

  /* The number of intensity levels we deal with for gamma correction &c */
  ANALOGTV_CV_MAX=1024,

  /* MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
     it interpolates extra black lines. */
  ANALOGTV_MAX_LINEHEIGHT=12

};

typedef struct analogtv_input_s {
  signed char signal[ANALOGTV_V+1][ANALOGTV_H];

  int do_teletext;

  /* for client use */
  void (*updater)(struct analogtv_input_s *inp);
  void *client_data;
  double next_update_time;

} analogtv_input;

typedef struct analogtv_font_s {
  XImage *text_im;
  int char_w, char_h;
  int x_mult, y_mult;
} analogtv_font;

typedef struct analogtv_reception_s {

  analogtv_input *input;
  double ofs;
  double level;
  double multipath;
  double freqerr;

  double ghostfir[ANALOGTV_GHOSTFIR_LEN];
  double ghostfir2[ANALOGTV_GHOSTFIR_LEN];

  double hfloss;
  double hfloss2;

} analogtv_reception;

/*
  The rest of this should be considered mostly opaque to the analogtv module.
 */

struct analogtv_yiq_s {
  float y,i,q;
} /*yiq[ANALOGTV_PIC_LEN+10] */;

typedef struct analogtv_s {

  Display *dpy;
  Window window;
  Screen *screen;
  XWindowAttributes xgwa;

  struct threadpool threads;

#if 0
  unsigned int onscreen_signature[ANALOGTV_V];
#endif

  int n_colors;

  int interlace;
  int interlace_counter;

  float agclevel;

  /* If you change these, call analogtv_set_demod */
  float tint_control,color_control,brightness_control,contrast_control;
  float height_control, width_control, squish_control;
  float horiz_desync;
  float squeezebottom;
  float powerup;

  /* internal cache */
  int blur_mult;

  /* For fast display, set fakeit_top, fakeit_bot to
     the scanlines (0..ANALOGTV_V) that can be preserved on screen.
     fakeit_scroll is the number of scan lines to scroll it up,
     or 0 to not scroll at all. It will DTRT if asked to scroll from
     an offscreen region.
  */
  int fakeit_top;
  int fakeit_bot;
  int fakeit_scroll;
  int redraw_all;

  int use_cmap,use_color;
  int bilevel_signal;

  XShmSegmentInfo shm_info;
  int visdepth,visclass,visbits;
  int red_invprec, red_shift;
  int green_invprec, green_shift;
  int blue_invprec, blue_shift;
  unsigned long red_mask, green_mask, blue_mask;

  Colormap colormap;
  int usewidth,useheight,xrepl,subwidth;
  XImage *image; /* usewidth * useheight */
  GC gc;
  int screen_xo,screen_yo; /* centers image in window */

  int flutter_horiz_desync;
  int flutter_tint;

  struct timeval last_display_time;
  int need_clear;


  /* Add hash (in the radio sense, not the programming sense.) These
     are the small white streaks that appear in quasi-regular patterns
     all over the screen when someone is running the vacuum cleaner or
     the blender. We also set shrinkpulse for one period which
     squishes the image horizontally to simulate the temporary line
     voltate drop when someone turns on a big motor */
  double hashnoise_rpm;
  int hashnoise_counter;
  int hashnoise_times[ANALOGTV_V];
  int hashnoise_signal[ANALOGTV_V];
  int hashnoise_on;
  int hashnoise_enable;
  int shrinkpulse;

  float crtload[ANALOGTV_V];

  unsigned int red_values[ANALOGTV_CV_MAX];
  unsigned int green_values[ANALOGTV_CV_MAX];
  unsigned int blue_values[ANALOGTV_CV_MAX];

  unsigned long colors[256];
  int cmap_y_levels;
  int cmap_i_levels;
  int cmap_q_levels;

  float tint_i, tint_q;
  
  int cur_hsync;
  int line_hsync[ANALOGTV_V];
  int cur_vsync;
  double cb_phase[4];
  double line_cb_phase[ANALOGTV_V][4];

  int channel_change_cycles;
  double rx_signal_level;
  float *rx_signal;

  struct {
    int index;
    double value;
  } leveltable[ANALOGTV_MAX_LINEHEIGHT+1][ANALOGTV_MAX_LINEHEIGHT+1];

  /* Only valid during draw. */
  unsigned random0, random1;
  double noiselevel;
  const analogtv_reception *const *recs;
  unsigned rec_count;

  float *signal_subtotals;

  float puheight;
} analogtv;


analogtv *analogtv_allocate(Display *dpy, Window window);
analogtv_input *analogtv_input_allocate(void);

/* call if window size changes */
void analogtv_reconfigure(analogtv *it);

void analogtv_set_defaults(analogtv *it, char *prefix);
void analogtv_release(analogtv *it);
int analogtv_set_demod(analogtv *it);
void analogtv_setup_frame(analogtv *it);
void analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi);
void analogtv_draw(analogtv *it, double noiselevel,
                   const analogtv_reception *const *recs, unsigned rec_count);

int analogtv_load_ximage(analogtv *it, analogtv_input *input,
                         XImage *pic_im, XImage *mask_im,
                         int xoff, int yoff, int width, int height);

void analogtv_reception_update(analogtv_reception *inp);

void analogtv_setup_teletext(analogtv_input *input);


/* Functions for rendering content into an analogtv_input */

void analogtv_make_font(Display *dpy, Window window,
                        analogtv_font *f, int w, int h, char *fontname);
int analogtv_font_pixel(analogtv_font *f, int c, int x, int y);
void analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value);
void analogtv_font_set_char(analogtv_font *f, int c, char *s);
void analogtv_lcp_to_ntsc(double luma, double chroma, double phase,
                          int ntsc[4]);


void analogtv_draw_solid(analogtv_input *input,
                         int left, int right, int top, int bot,
                         int ntsc[4]);

void analogtv_draw_solid_rel_lcp(analogtv_input *input,
                                 double left, double right,
                                 double top, double bot,
                                 double luma, double chroma, double phase);

void analogtv_draw_char(analogtv_input *input, analogtv_font *f,
                        int c, int x, int y, int ntsc[4]);
void analogtv_draw_string(analogtv_input *input, analogtv_font *f,
                          char *s, int x, int y, int ntsc[4]);
void analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
                                   char *s, int x, int y, int ntsc[4]);

int analogtv_handle_events (analogtv *it);

#ifdef HAVE_XSHM_EXTENSION
#define ANALOGTV_DEFAULTS_SHM "*useSHM:           True",
#else
#define ANALOGTV_DEFAULTS_SHM
#endif

#ifndef HAVE_MOBILE
# define ANALOGTV_DEF_BRIGHTNESS "2"
# define ANALOGTV_DEF_CONTRAST "150"
#else
  /* Need to really crank this up for it to look good on the iPhone screen. */
# define ANALOGTV_DEF_BRIGHTNESS "3"
# define ANALOGTV_DEF_CONTRAST "400"
#endif

/* Brightness: useful range is around -75 to 100.
   Contrast:   useful range is around 0 - 500.
   Color:      useful range is around +/- 500.
   Tint:       range is mod 360.

   The values in the 'analogtv' struct are the resource divided by 100.0,
   except for tint, which is exact.
 */

#define ANALOGTV_DEFAULTS \
  "*TVColor:         70", \
  "*TVTint:          5",  \
  "*TVBrightness:  " ANALOGTV_DEF_BRIGHTNESS,  \
  "*TVContrast:    " ANALOGTV_DEF_CONTRAST, \
  "*Background:      Black", \
  "*use_cmap:        0",  \
  "*geometry:	     800x600", \
  "*fpsSolid:	     True", \
  "*lowrez:	     True", \
  THREAD_DEFAULTS \
  ANALOGTV_DEFAULTS_SHM

#define ANALOGTV_OPTIONS \
  THREAD_OPTIONS \
  { "-use-cmap",        ".use_cmap",     XrmoptionSepArg, 0 }, \
  { "-tv-color",        ".TVColor",      XrmoptionSepArg, 0 }, \
  { "-tv-tint",         ".TVTint",       XrmoptionSepArg, 0 }, \
  { "-tv-brightness",   ".TVBrightness", XrmoptionSepArg, 0 }, \
  { "-tv-contrast",     ".TVContrast",   XrmoptionSepArg, 0 },

#endif /* _XSCREENSAVER_ANALOGTV_H */