From http://www.jwz.org/xscreensaver/xscreensaver-5.40.tar.gz
[xscreensaver] / hacks / analogtv.c
index 642037de54d6365f05055089efb0f9c26f694b8f..fd7f504c68d705614c57552405044932694b7b6b 100644 (file)
@@ -1,4 +1,4 @@
-/* analogtv, Copyright (c) 2003, 2004 Trevor Blackwell <tlb@tlb.org>
+/* 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
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
@@ -85,6 +85,8 @@
 #include "yarandom.h"
 #include "grabscreen.h"
 #include "visual.h"
 #include "yarandom.h"
 #include "grabscreen.h"
 #include "visual.h"
+#include "font-retry.h"
+#include "ximage-loader.h"
 
 /* #define DEBUG 1 */
 
 
 /* #define DEBUG 1 */
 
@@ -305,7 +307,8 @@ analogtv_configure(analogtv *it)
   /* If the window is very small, don't let the image we draw get lower
      than the actual TV resolution (266x200.)
 
   /* If the window is very small, don't let the image we draw get lower
      than the actual TV resolution (266x200.)
 
-     If the aspect ratio of the window is close to a 4:3 or 16:9 ratio,
+     If the aspect ratio of the window is close to a 4:3 or 16:9 ratio --
+     or if it is a completely weird aspect ratio --
      then scale the image to exactly fill the window.
 
      Otherwise, center the image either horizontally or vertically,
      then scale the image to exactly fill the window.
 
      Otherwise, center the image either horizontally or vertically,
@@ -317,6 +320,8 @@ analogtv_configure(analogtv *it)
   float percent = 0.15;
   float min_ratio =  4.0 / 3.0 * (1 - percent);
   float max_ratio = 16.0 / 9.0 * (1 + percent);
   float percent = 0.15;
   float min_ratio =  4.0 / 3.0 * (1 - percent);
   float max_ratio = 16.0 / 9.0 * (1 + percent);
+  float crazy_min_ratio = 10;
+  float crazy_max_ratio = 1/crazy_min_ratio;
   float ratio;
   float height_snap=0.025;
 
   float ratio;
   float height_snap=0.025;
 
@@ -370,6 +375,20 @@ analogtv_configure(analogtv *it)
 # endif
     }
 
 # endif
     }
 
+  if (ratio < crazy_min_ratio || ratio > crazy_max_ratio)
+    {
+      if (ratio < crazy_min_ratio)
+        hlim = it->xgwa.height;
+      else
+        wlim = it->xgwa.width;
+# ifdef DEBUG
+      fprintf (stderr,
+               "size: aspect: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
+               wlim, hlim, it->xgwa.width, it->xgwa.height,
+               min_ratio, ratio, max_ratio);
+# endif
+    }
+
 
   height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
   if (height_diff != 0 && abs(height_diff) < hlim * height_snap)
 
   height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
   if (height_diff != 0 && abs(height_diff) < hlim * height_snap)
@@ -549,6 +568,9 @@ analogtv_allocate(Display *dpy, Window window)
                                     "background", "Background");
 
   it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
                                     "background", "Background");
 
   it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
+# ifdef HAVE_JWXYZ
+  jwxyz_XSetAntiAliasing (it->dpy, it->gc, False);
+# endif
   XSetWindowBackground(it->dpy, it->window, gcv.background);
   XClearWindow(dpy,window);
 
   XSetWindowBackground(it->dpy, it->window, gcv.background);
   XClearWindow(dpy,window);
 
@@ -1964,10 +1986,16 @@ analogtv_input_allocate()
   This takes a screen image and encodes it as a video camera would,
   including all the bandlimiting and YIQ modulation.
   This isn't especially tuned for speed.
   This takes a screen image and encodes it as a video camera would,
   including all the bandlimiting and YIQ modulation.
   This isn't especially tuned for speed.
+
+  xoff, yoff: top left corner of rendered image, in window pixels.
+  w, h: scaled size of rendered image, in window pixels.
+  mask: BlackPixel means don't render (it's not full alpha)
 */
 
 int
 */
 
 int
-analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
+analogtv_load_ximage(analogtv *it, analogtv_input *input,
+                     XImage *pic_im, XImage *mask_im,
+                     int xoff, int yoff, int target_w, int target_h)
 {
   int i,x,y;
   int img_w,img_h;
 {
   int i,x,y;
   int img_w,img_h;
@@ -1976,14 +2004,24 @@ analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
   int fqx[4],fqy[4];
   XColor col1[ANALOGTV_PIC_LEN];
   XColor col2[ANALOGTV_PIC_LEN];
   int fqx[4],fqy[4];
   XColor col1[ANALOGTV_PIC_LEN];
   XColor col2[ANALOGTV_PIC_LEN];
+  char mask[ANALOGTV_PIC_LEN];
   int multiq[ANALOGTV_PIC_LEN+4];
   int multiq[ANALOGTV_PIC_LEN+4];
+  unsigned long black = 0; /* not BlackPixelOfScreen (it->xgwa.screen); */
+
+  int x_length=ANALOGTV_PIC_LEN;
   int y_overscan=5; /* overscan this much top and bottom */
   int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
 
   int y_overscan=5; /* overscan this much top and bottom */
   int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
 
-  img_w=pic_im->width;
-  img_h=pic_im->height;
+  if (target_w > 0) x_length     = x_length     * target_w / it->xgwa.width;
+  if (target_h > 0) y_scanlength = y_scanlength * target_h / it->xgwa.height;
+
+  img_w = pic_im->width;
+  img_h = pic_im->height;
   
   
-  for (i=0; i<ANALOGTV_PIC_LEN+4; i++) {
+  xoff = ANALOGTV_PIC_LEN  * xoff / it->xgwa.width;
+  yoff = ANALOGTV_VISLINES * yoff / it->xgwa.height;
+
+  for (i=0; i<x_length+4; i++) {
     double phase=90.0-90.0*i;
     double ampl=1.0;
     multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
     double phase=90.0-90.0*i;
     double ampl=1.0;
     multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
@@ -1993,21 +2031,27 @@ analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
     int picy1=(y*img_h)/y_scanlength;
     int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
 
     int picy1=(y*img_h)/y_scanlength;
     int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
 
-    for (x=0; x<ANALOGTV_PIC_LEN; x++) {
-      int picx=(x*img_w)/ANALOGTV_PIC_LEN;
+    for (x=0; x<x_length; x++) {
+      int picx=(x*img_w)/x_length;
       col1[x].pixel=XGetPixel(pic_im, picx, picy1);
       col2[x].pixel=XGetPixel(pic_im, picx, picy2);
       col1[x].pixel=XGetPixel(pic_im, picx, picy1);
       col2[x].pixel=XGetPixel(pic_im, picx, picy2);
+      if (mask_im)
+        mask[x] = (XGetPixel(mask_im, picx, picy1) != black);
+      else
+        mask[x] = 1;
     }
     }
-    XQueryColors(it->dpy, it->colormap, col1, ANALOGTV_PIC_LEN);
-    XQueryColors(it->dpy, it->colormap, col2, ANALOGTV_PIC_LEN);
-
+    XQueryColors(it->dpy, it->colormap, col1, x_length);
+    XQueryColors(it->dpy, it->colormap, col2, x_length);
     for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
     for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
 
     for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
     for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
 
-    for (x=0; x<ANALOGTV_PIC_LEN; x++) {
+    for (x=0; x<x_length; x++) {
       int rawy,rawi,rawq;
       int filty,filti,filtq;
       int composite;
       int rawy,rawi,rawq;
       int filty,filti,filtq;
       int composite;
+
+      if (!mask[x]) continue;
+
       /* Compute YIQ as:
            y=0.30 r + 0.59 g + 0.11 b
            i=0.60 r - 0.28 g - 0.32 b
       /* Compute YIQ as:
            y=0.30 r + 0.59 g + 0.11 b
            i=0.60 r - 0.28 g - 0.32 b
@@ -2059,7 +2103,8 @@ analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
       composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
       if (composite>125) composite=125;
       if (composite<0) composite=0;
       composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
       if (composite>125) composite=125;
       if (composite<0) composite=0;
-      input->signal[y-y_overscan+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite;
+
+      input->signal[y-y_overscan+ANALOGTV_TOP+yoff][x+ANALOGTV_PIC_START+xoff] = composite;
     }
   }
 
     }
   }
 
@@ -2155,10 +2200,10 @@ analogtv_reception_update(analogtv_reception *rec)
 }
 
 
 }
 
 
-/* jwz: since MacOS doesn't have "6x10", I dumped this font to an XBM...
+/* jwz: since MacOS doesn't have "6x10", I dumped this font to a PNG...
  */
 
  */
 
-#include "images/6x10font.xbm"
+#include "images/gen/6x10font_png.h"
 
 void
 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
 
 void
 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
@@ -2178,18 +2223,40 @@ analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
 
   if (fontname && !strcmp (fontname, "6x10")) {
 
 
   if (fontname && !strcmp (fontname, "6x10")) {
 
-    text_pm = XCreatePixmapFromBitmapData (dpy, window,
-                                           (char *) font6x10_bits,
-                                           font6x10_width,
-                                           font6x10_height,
-                                           1, 0, 1);
-    f->text_im = XGetImage(dpy, text_pm, 0, 0, font6x10_width, font6x10_height,
-                           1, XYPixmap);
-    XFreePixmap(dpy, text_pm);
+    int pix_w, pix_h;
+    XWindowAttributes xgwa;
+    Pixmap m = 0;
+    Pixmap p = image_data_to_pixmap (dpy, window,
+                                     _6x10font_png, sizeof(_6x10font_png),
+                                     &pix_w, &pix_h, &m);
+    XImage *im = XGetImage (dpy, p, 0, 0, pix_w, pix_h, ~0L, ZPixmap);
+    XImage *mm = XGetImage (dpy, m, 0, 0, pix_w, pix_h, 1, XYPixmap);
+    unsigned long black = BlackPixelOfScreen (DefaultScreenOfDisplay (dpy));
+    int x, y;
+
+    XFreePixmap (dpy, p);
+    XFreePixmap (dpy, m);
+    if (pix_w != 256*7) abort();
+    if (pix_h != 10) abort();
+
+    XGetWindowAttributes (dpy, window, &xgwa);
+    f->text_im = XCreateImage (dpy, xgwa.visual, 1, XYBitmap, 0, 0,
+                               pix_w, pix_h, 8, 0);
+    f->text_im->data = malloc (f->text_im->bytes_per_line * f->text_im->height);
+
+    /* Convert deep image to 1 bit */
+    for (y = 0; y < pix_h; y++)
+      for (x = 0; x < pix_w; x++)
+        XPutPixel (f->text_im, x, y,
+                   (XGetPixel (mm, x, y)
+                    ? XGetPixel (im, x, y) == black
+                    : 0));
+    XDestroyImage (im);
+    XDestroyImage (mm);
 
   } else if (fontname) {
 
 
   } else if (fontname) {
 
-    font = XLoadQueryFont (dpy, fontname);
+    font = load_font_retry (dpy, fontname);
     if (!font) {
       fprintf(stderr, "analogtv: can't load font %s\n", fontname);
       abort();
     if (!font) {
       fprintf(stderr, "analogtv: can't load font %s\n", fontname);
       abort();
@@ -2202,6 +2269,9 @@ analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
     gcv.background=0;
     gcv.font=font->fid;
     gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
     gcv.background=0;
     gcv.font=font->fid;
     gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
+# ifdef HAVE_JWXYZ
+  jwxyz_XSetAntiAliasing (dpy, gc, False);
+# endif
 
     XSetForeground(dpy, gc, 0);
     XFillRectangle(dpy, text_pm, gc, 0, 0, 256*f->char_w, f->char_h);
 
     XSetForeground(dpy, gc, 0);
     XFillRectangle(dpy, text_pm, gc, 0, 0, 256*f->char_w, f->char_h);
@@ -2359,115 +2429,3 @@ analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
 
   analogtv_draw_string(input, f, s, x, y, ntsc);
 }
 
   analogtv_draw_string(input, f, s, x, y, ntsc);
 }
-
-
-static const char hextonib[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-                                   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
-                                   0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
-                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-                                   0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
-                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
-/*
-  Much of this function was adapted from logo.c
- */
-void
-analogtv_draw_xpm(analogtv *tv, analogtv_input *input,
-                  const char * const *xpm, int left, int top)
-{
-  int xpmw,xpmh;
-  int x,y,tvx,tvy,i;
-  int rawy,rawi,rawq;
-  int ncolors, nbytes;
-  char dummyc;
-  struct {
-    int r; int g; int b;
-  } cmap[256];
-
-
-  if (4 != sscanf ((const char *) *xpm,
-                   "%d %d %d %d %c",
-                   &xpmw, &xpmh, &ncolors, &nbytes, &dummyc))
-    abort();
-  if (ncolors < 1 || ncolors > 255)
-    abort();
-  if (nbytes != 1) /* a serious limitation */
-    abort();
-  xpm++;
-
-  for (i = 0; i < ncolors; i++) {
-    const char *line = *xpm;
-    int colori = ((unsigned char)*line++)&0xff;
-    while (*line)
-      {
-        int r, g, b;
-        char which;
-        while (*line == ' ' || *line == '\t')
-          line++;
-        which = *line++;
-        if (which != 'c' && which != 'm')
-          abort();
-        while (*line == ' ' || *line == '\t')
-          line++;
-        if (!strncasecmp(line, "None", 4))
-          {
-            r = g = b = -1;
-            line += 4;
-          }
-        else
-          {
-            if (*line == '#')
-              line++;
-            r = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
-            line += 2;
-            g = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
-            line += 2;
-            b = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
-            line += 2;
-          }
-
-        if (which == 'c')
-          {
-            cmap[colori].r = r;
-            cmap[colori].g = g;
-            cmap[colori].b = b;
-          }
-      }
-
-    xpm++;
-  }
-
-  for (y=0; y<xpmh; y++) {
-    const char *line = *xpm++;
-    tvy=y+top;
-    if (tvy<ANALOGTV_TOP || tvy>=ANALOGTV_BOT) continue;
-
-    for (x=0; x<xpmw; x++) {
-      int cbyte=((unsigned char)line[x])&0xff;
-      int ntsc[4];
-      tvx=x*4+left;
-      if (tvx<ANALOGTV_PIC_START || tvx+4>ANALOGTV_PIC_END) continue;
-
-      rawy=( 5*cmap[cbyte].r + 11*cmap[cbyte].g + 2*cmap[cbyte].b) / 64;
-      rawi=(10*cmap[cbyte].r -  4*cmap[cbyte].g - 5*cmap[cbyte].b) / 64;
-      rawq=( 3*cmap[cbyte].r -  8*cmap[cbyte].g + 5*cmap[cbyte].b) / 64;
-
-      ntsc[0]=rawy+rawq;
-      ntsc[1]=rawy-rawi;
-      ntsc[2]=rawy-rawq;
-      ntsc[3]=rawy+rawi;
-
-      for (i=0; i<4; i++) {
-        if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
-        if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
-      }
-
-      input->signal[tvy][tvx+0]= ntsc[(tvx+0)&3];
-      input->signal[tvy][tvx+1]= ntsc[(tvx+1)&3];
-      input->signal[tvy][tvx+2]= ntsc[(tvx+2)&3];
-      input->signal[tvy][tvx+3]= ntsc[(tvx+3)&3];
-    }
-  }
-}