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 #ifndef _XSCREENSAVER_ANALOGTV_H
13 #define _XSCREENSAVER_ANALOGTV_H
14 
15 #include "thread_util.h"
16 #include "xshm.h"
17 
18 #if defined(USE_IPHONE) || defined(HAVE_ANDROID)
19 # define HAVE_MOBILE
20 #endif
21 
22 /* To simulate an NTSC CRT monitor with way more scanlines, and thus
23    apply an ahistorical tv-like effect to a larger image, increase
24    this resolution multiplier.
25  */
26 #ifndef ANALOGTV_SCALE
27 # define ANALOGTV_SCALE 1
28 #endif
29 
30 /*
31   You'll need these to generate standard NTSC TV signals
32  */
33 enum {
34   /* We don't handle interlace here */
35   ANALOGTV_V=262*ANALOGTV_SCALE,
36   ANALOGTV_TOP=30*ANALOGTV_SCALE,
37   ANALOGTV_VISLINES=200*ANALOGTV_SCALE,
38   ANALOGTV_BOT=ANALOGTV_TOP + ANALOGTV_VISLINES,
39 
40   /* This really defines our sampling rate, 4x the colorburst
41      frequency. Handily equal to the Apple II's dot clock.
42      You could also make a case for using 3x the colorburst freq,
43      but 4x isn't hard to deal with. */
44   ANALOGTV_H=912*ANALOGTV_SCALE,
45 
46   /* Each line is 63500 nS long. The sync pulse is 4700 nS long, etc.
47      Define sync, back porch, colorburst, picture, and front porch
48      positions */
49   ANALOGTV_SYNC_START=0,
50   ANALOGTV_BP_START=4700*ANALOGTV_H/63500,
51   ANALOGTV_CB_START=5800*ANALOGTV_H/63500,
52   /* signal[row][ANALOGTV_PIC_START] is the first displayed pixel */
53   ANALOGTV_PIC_START=9400*ANALOGTV_H/63500,
54   ANALOGTV_PIC_LEN=52600*ANALOGTV_H/63500,
55   ANALOGTV_FP_START=62000*ANALOGTV_H/63500,
56   ANALOGTV_PIC_END=ANALOGTV_FP_START,
57 
58   /* TVs scan past the edges of the picture tube, so normally you only
59      want to use about the middle 3/4 of the nominal scan line.
60   */
61   ANALOGTV_VIS_START=ANALOGTV_PIC_START + (ANALOGTV_PIC_LEN*1/8),
62   ANALOGTV_VIS_END=ANALOGTV_PIC_START + (ANALOGTV_PIC_LEN*7/8),
63   ANALOGTV_VIS_LEN=ANALOGTV_VIS_END-ANALOGTV_VIS_START,
64 
65   ANALOGTV_HASHNOISE_LEN=6*ANALOGTV_SCALE,
66 
67   ANALOGTV_GHOSTFIR_LEN=4,
68 
69   /* analogtv.signal is in IRE units, as defined below: */
70   ANALOGTV_WHITE_LEVEL=100,
71   ANALOGTV_GRAY50_LEVEL=55,
72   ANALOGTV_GRAY30_LEVEL=35,
73   ANALOGTV_BLACK_LEVEL=10,
74   ANALOGTV_BLANK_LEVEL=0,
75   ANALOGTV_SYNC_LEVEL=-40,
76   ANALOGTV_CB_LEVEL=20,
77 
78   ANALOGTV_SIGNAL_LEN=ANALOGTV_V*ANALOGTV_H,
79 
80   /* The number of intensity levels we deal with for gamma correction &c */
81   ANALOGTV_CV_MAX=1024,
82 
83   /* MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
84      it interpolates extra black lines. */
85   ANALOGTV_MAX_LINEHEIGHT=12
86 
87 };
88 
89 typedef struct analogtv_input_s {
90   signed char signal[ANALOGTV_V+1][ANALOGTV_H];
91 
92   int do_teletext;
93 
94   /* for client use */
95   void (*updater)(struct analogtv_input_s *inp);
96   void *client_data;
97   double next_update_time;
98 
99 } analogtv_input;
100 
101 typedef struct analogtv_font_s {
102   XImage *text_im;
103   int char_w, char_h;
104   int x_mult, y_mult;
105 } analogtv_font;
106 
107 typedef struct analogtv_reception_s {
108 
109   analogtv_input *input;
110   double ofs;
111   double level;
112   double multipath;
113   double freqerr;
114 
115   double ghostfir[ANALOGTV_GHOSTFIR_LEN];
116   double ghostfir2[ANALOGTV_GHOSTFIR_LEN];
117 
118   double hfloss;
119   double hfloss2;
120 
121 } analogtv_reception;
122 
123 /*
124   The rest of this should be considered mostly opaque to the analogtv module.
125  */
126 
127 struct analogtv_yiq_s {
128   float y,i,q;
129 } /*yiq[ANALOGTV_PIC_LEN+10] */;
130 
131 typedef struct analogtv_s {
132 
133   Display *dpy;
134   Window window;
135   Screen *screen;
136   XWindowAttributes xgwa;
137 
138   struct threadpool threads;
139 
140 #if 0
141   unsigned int onscreen_signature[ANALOGTV_V];
142 #endif
143 
144   int n_colors;
145 
146   int interlace;
147   int interlace_counter;
148 
149   float agclevel;
150 
151   /* If you change these, call analogtv_set_demod */
152   float tint_control,color_control,brightness_control,contrast_control;
153   float height_control, width_control, squish_control;
154   float horiz_desync;
155   float squeezebottom;
156   float powerup;
157 
158   /* internal cache */
159   int blur_mult;
160 
161   /* For fast display, set fakeit_top, fakeit_bot to
162      the scanlines (0..ANALOGTV_V) that can be preserved on screen.
163      fakeit_scroll is the number of scan lines to scroll it up,
164      or 0 to not scroll at all. It will DTRT if asked to scroll from
165      an offscreen region.
166   */
167   int fakeit_top;
168   int fakeit_bot;
169   int fakeit_scroll;
170   int redraw_all;
171 
172   int use_cmap,use_color;
173   int bilevel_signal;
174 
175   XShmSegmentInfo shm_info;
176   int visdepth,visclass,visbits;
177   int red_invprec, red_shift;
178   int green_invprec, green_shift;
179   int blue_invprec, blue_shift;
180   unsigned long red_mask, green_mask, blue_mask;
181 
182   Colormap colormap;
183   int usewidth,useheight,xrepl,subwidth;
184   XImage *image; /* usewidth * useheight */
185   GC gc;
186   int screen_xo,screen_yo; /* centers image in window */
187 
188   int flutter_horiz_desync;
189   int flutter_tint;
190 
191   struct timeval last_display_time;
192   int need_clear;
193 
194 
195   /* Add hash (in the radio sense, not the programming sense.) These
196      are the small white streaks that appear in quasi-regular patterns
197      all over the screen when someone is running the vacuum cleaner or
198      the blender. We also set shrinkpulse for one period which
199      squishes the image horizontally to simulate the temporary line
200      voltate drop when someone turns on a big motor */
201   double hashnoise_rpm;
202   int hashnoise_counter;
203   int hashnoise_times[ANALOGTV_V];
204   int hashnoise_signal[ANALOGTV_V];
205   int hashnoise_on;
206   int hashnoise_enable;
207   int shrinkpulse;
208 
209   float crtload[ANALOGTV_V];
210 
211   unsigned int red_values[ANALOGTV_CV_MAX];
212   unsigned int green_values[ANALOGTV_CV_MAX];
213   unsigned int blue_values[ANALOGTV_CV_MAX];
214 
215   unsigned long colors[256];
216   int cmap_y_levels;
217   int cmap_i_levels;
218   int cmap_q_levels;
219 
220   float tint_i, tint_q;
221 
222   int cur_hsync;
223   int line_hsync[ANALOGTV_V];
224   int cur_vsync;
225   double cb_phase[4];
226   double line_cb_phase[ANALOGTV_V][4];
227 
228   int channel_change_cycles;
229   double rx_signal_level;
230   float *rx_signal;
231 
232   struct {
233     int index;
234     double value;
235   } leveltable[ANALOGTV_MAX_LINEHEIGHT+1][ANALOGTV_MAX_LINEHEIGHT+1];
236 
237   /* Only valid during draw. */
238   unsigned random0, random1;
239   double noiselevel;
240   const analogtv_reception *const *recs;
241   unsigned rec_count;
242 
243   float *signal_subtotals;
244 
245   float puheight;
246 } analogtv;
247 
248 
249 analogtv *analogtv_allocate(Display *dpy, Window window);
250 analogtv_input *analogtv_input_allocate(void);
251 
252 /* call if window size changes */
253 void analogtv_reconfigure(analogtv *it);
254 
255 void analogtv_set_defaults(analogtv *it, char *prefix);
256 void analogtv_release(analogtv *it);
257 int analogtv_set_demod(analogtv *it);
258 void analogtv_setup_frame(analogtv *it);
259 void analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi);
260 void analogtv_draw(analogtv *it, double noiselevel,
261                    const analogtv_reception *const *recs, unsigned rec_count);
262 
263 int analogtv_load_ximage(analogtv *it, analogtv_input *input,
264                          XImage *pic_im, XImage *mask_im,
265                          int xoff, int yoff, int width, int height);
266 
267 void analogtv_reception_update(analogtv_reception *inp);
268 
269 void analogtv_setup_teletext(analogtv_input *input);
270 
271 
272 /* Functions for rendering content into an analogtv_input */
273 
274 void analogtv_make_font(Display *dpy, Window window,
275                         analogtv_font *f, int w, int h, char *fontname);
276 int analogtv_font_pixel(analogtv_font *f, int c, int x, int y);
277 void analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value);
278 void analogtv_font_set_char(analogtv_font *f, int c, char *s);
279 void analogtv_lcp_to_ntsc(double luma, double chroma, double phase,
280                           int ntsc[4]);
281 
282 
283 void analogtv_draw_solid(analogtv_input *input,
284                          int left, int right, int top, int bot,
285                          int ntsc[4]);
286 
287 void analogtv_draw_solid_rel_lcp(analogtv_input *input,
288                                  double left, double right,
289                                  double top, double bot,
290                                  double luma, double chroma, double phase);
291 
292 void analogtv_draw_char(analogtv_input *input, analogtv_font *f,
293                         int c, int x, int y, int ntsc[4]);
294 void analogtv_draw_string(analogtv_input *input, analogtv_font *f,
295                           char *s, int x, int y, int ntsc[4]);
296 void analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
297                                    char *s, int x, int y, int ntsc[4]);
298 
299 int analogtv_handle_events (analogtv *it);
300 
301 #ifdef HAVE_XSHM_EXTENSION
302 #define ANALOGTV_DEFAULTS_SHM "*useSHM:           True",
303 #else
304 #define ANALOGTV_DEFAULTS_SHM
305 #endif
306 
307 #ifndef HAVE_MOBILE
308 # define ANALOGTV_DEF_BRIGHTNESS "2"
309 # define ANALOGTV_DEF_CONTRAST "150"
310 #else
311   /* Need to really crank this up for it to look good on the iPhone screen. */
312 # define ANALOGTV_DEF_BRIGHTNESS "3"
313 # define ANALOGTV_DEF_CONTRAST "400"
314 #endif
315 
316 /* Brightness: useful range is around -75 to 100.
317    Contrast:   useful range is around 0 - 500.
318    Color:      useful range is around +/- 500.
319    Tint:       range is mod 360.
320 
321    The values in the 'analogtv' struct are the resource divided by 100.0,
322    except for tint, which is exact.
323  */
324 
325 #define ANALOGTV_DEFAULTS \
326   "*TVColor:         70", \
327   "*TVTint:          5",  \
328   "*TVBrightness:  " ANALOGTV_DEF_BRIGHTNESS,  \
329   "*TVContrast:    " ANALOGTV_DEF_CONTRAST, \
330   "*Background:      Black", \
331   "*use_cmap:        0",  \
332   "*geometry:	     800x600", \
333   "*fpsSolid:	     True", \
334   "*lowrez:	     True", \
335   THREAD_DEFAULTS \
336   ANALOGTV_DEFAULTS_SHM
337 
338 #define ANALOGTV_OPTIONS \
339   THREAD_OPTIONS \
340   { "-use-cmap",        ".use_cmap",     XrmoptionSepArg, 0 }, \
341   { "-tv-color",        ".TVColor",      XrmoptionSepArg, 0 }, \
342   { "-tv-tint",         ".TVTint",       XrmoptionSepArg, 0 }, \
343   { "-tv-brightness",   ".TVBrightness", XrmoptionSepArg, 0 }, \
344   { "-tv-contrast",     ".TVContrast",   XrmoptionSepArg, 0 },
345 
346 #endif /* _XSCREENSAVER_ANALOGTV_H */
347