From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / glx / photopile.c
index b4c98d0eb8d932f73bb1150f7c75b75c37e3ae71..0f6312febf6a96b74a288a0b39f4dd5012e9e80f 100644 (file)
@@ -1,4 +1,4 @@
-/* photopile, Copyright (c) 2008 Jens Kilian <jjk@acm.org>
+/* photopile, Copyright (c) 2008-2015 Jens Kilian <jjk@acm.org>
  * Based on carousel, Copyright (c) 2005-2008 Jamie Zawinski <jwz@jwz.org>
  * Loads a sequence of images and shuffles them into a pile.
  *
  * implied warranty.
  */
 
-#define DEF_FONT "-*-helvetica-bold-r-normal-*-240-*"
+#if defined(HAVE_COCOA) || defined(HAVE_ANDROID)
+# define DEF_FONT "OCR A Std 48, Lucida Console 48, Monaco 48"
+#else
+# define DEF_FONT "-*-helvetica-bold-r-normal-*-*-480-*-*-*-*-*-*"
+#endif
+
 #define DEFAULTS  "*count:           7         \n" \
                   "*delay:           10000     \n" \
                   "*wireframe:       False     \n" \
@@ -21,7 +26,8 @@
                   "*font:          " DEF_FONT "\n" \
                   "*desktopGrabber:  xscreensaver-getimage -no-desktop %s\n" \
                   "*grabDesktopImages:   False \n" \
-                  "*chooseRandomImages:  True  \n"
+                  "*chooseRandomImages:  True  \n" \
+                 "*suppressRotationAnimation: True\n" \
 
 # define refresh_photopile 0
 # define release_photopile 0
@@ -30,7 +36,7 @@
 #undef countof
 #define countof(x) (sizeof((x))/sizeof((*x)))
 
-#ifndef HAVE_COCOA
+#ifndef HAVE_JWXYZ
 # include <X11/Intrinsic.h>     /* for XrmDatabase in -debug mode */
 #endif
 #include <math.h>
@@ -38,6 +44,7 @@
 #include "xlockmore.h"
 #include "grab-ximage.h"
 #include "texfont.h"
+#include "dropshadow.h"
 
 #ifdef USE_GL
 
 # define DEF_MAX_TILT       "50"
 # define DEF_SPEED          "1.0"
 # define DEF_DURATION       "5"
-# define DEF_TITLES         "False"
 # define DEF_MIPMAP         "True"
+# define DEF_TITLES         "True"
+# define DEF_POLAROID       "True"
+# define DEF_CLIP           "True"
+# define DEF_SHADOWS        "True"
 # define DEF_DEBUG          "False"
 
 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
@@ -72,7 +82,7 @@ typedef struct {
 } image;
 
 
-typedef enum { EARLY, IN, NORMAL, LOADING, SHUFFLE } fade_mode;
+typedef enum { EARLY, SHUFFLE, NORMAL, LOADING } fade_mode;
 static int fade_ticks = 60;
 
 typedef struct {
@@ -82,6 +92,7 @@ typedef struct {
   image *frames;                /* pointer to array of images */
   int nframe;                   /* image being (resp. next to be) loaded */
 
+  GLuint shadow;
   texture_font_data *texfont;
   int loading_sw, loading_sh;
 
@@ -103,30 +114,42 @@ static GLfloat speed;        /* Animation speed scale factor. */
 static int duration;         /* Reload images after this long. */
 static Bool mipmap_p;        /* Use mipmaps instead of single textures. */
 static Bool titles_p;        /* Display image titles. */
+static Bool polaroid_p;      /* Use instant-film look for images. */
+static Bool clip_p;          /* Clip images instead of scaling for -polaroid. */
+static Bool shadows_p;       /* Draw drop shadows. */
 static Bool debug_p;         /* Be loud and do weird things. */
 
 
 static XrmOptionDescRec opts[] = {
   {"-scale",        ".scale",         XrmoptionSepArg, 0 },
   {"-maxTilt",      ".maxTilt",       XrmoptionSepArg, 0 },
-  {"-titles",       ".titles",        XrmoptionNoArg, "True"  },
-  {"-no-titles",    ".titles",        XrmoptionNoArg, "False" },
+  {"-speed",        ".speed",         XrmoptionSepArg, 0 },
+  {"-duration",     ".duration",      XrmoptionSepArg, 0 },
   {"-mipmaps",      ".mipmap",        XrmoptionNoArg, "True"  },
   {"-no-mipmaps",   ".mipmap",        XrmoptionNoArg, "False" },
-  {"-duration",     ".duration",      XrmoptionSepArg, 0 },
+  {"-titles",       ".titles",        XrmoptionNoArg, "True"  },
+  {"-no-titles",    ".titles",        XrmoptionNoArg, "False" },
+  {"-polaroid",     ".polaroid",      XrmoptionNoArg, "True"  },
+  {"-no-polaroid",  ".polaroid",      XrmoptionNoArg, "False" },
+  {"-clip",         ".clip",          XrmoptionNoArg, "True"  },
+  {"-no-clip",      ".clip",          XrmoptionNoArg, "False" },
+  {"-shadows",      ".shadows",       XrmoptionNoArg, "True"  },
+  {"-no-shadows",   ".shadows",       XrmoptionNoArg, "False" },
   {"-debug",        ".debug",         XrmoptionNoArg, "True"  },
   {"-font",         ".font",          XrmoptionSepArg, 0 },
-  {"-speed",        ".speed",         XrmoptionSepArg, 0 },
 };
 
 static argtype vars[] = {
   { &scale,         "scale",        "Scale",        DEF_SCALE,       t_Float},
   { &max_tilt,      "maxTilt",      "MaxTilt",      DEF_MAX_TILT,    t_Float},
-  { &mipmap_p,      "mipmap",       "Mipmap",       DEF_MIPMAP,      t_Bool},
-  { &debug_p,       "debug",        "Debug",        DEF_DEBUG,       t_Bool},
-  { &titles_p,      "titles",       "Titles",       DEF_TITLES,      t_Bool},
   { &speed,         "speed",        "Speed",        DEF_SPEED,       t_Float},
   { &duration,      "duration",     "Duration",     DEF_DURATION,    t_Int},
+  { &mipmap_p,      "mipmap",       "Mipmap",       DEF_MIPMAP,      t_Bool},
+  { &titles_p,      "titles",       "Titles",       DEF_TITLES,      t_Bool},
+  { &polaroid_p,    "polaroid",     "Polaroid",     DEF_POLAROID,    t_Bool},
+  { &clip_p,        "clip",         "Clip",         DEF_CLIP,        t_Bool},
+  { &shadows_p,     "shadows",      "Shadows",      DEF_SHADOWS,     t_Bool},
+  { &debug_p,       "debug",        "Debug",        DEF_DEBUG,       t_Bool},
 };
 
 ENTRYPOINT ModeSpecOpt photopile_opts = {countof(opts), opts, countof(vars), vars, NULL};
@@ -192,7 +215,9 @@ set_new_positions(photopile_state *ss)
   for (i = 0; i < MI_COUNT(mi)+1; ++i)
     {
       image *frame = ss->frames + i;
-      GLfloat d = sqrt(frame->w*frame->w + frame->h*frame->h);
+      GLfloat w = frame->w;
+      GLfloat h = frame->h;
+      GLfloat d = sqrt(w*w + h*h);
       GLfloat leave = frand(M_PI * 2.0);
       GLfloat enter = frand(M_PI * 2.0);
 
@@ -204,6 +229,10 @@ set_new_positions(photopile_state *ss)
       frame->pos[3].y = BELLRAND(MI_HEIGHT(mi));
       frame->pos[3].angle = (frand(2.0) - 1.0) * max_tilt;
 
+      /* Try to keep the images mostly inside the screen bounds */
+      frame->pos[3].x = MAX(0.5*w, MIN(MI_WIDTH(mi)-0.5*w, frame->pos[3].x));
+      frame->pos[3].y = MAX(0.5*h, MIN(MI_HEIGHT(mi)-0.5*h, frame->pos[3].y));
+
       /* intermediate points */
       frame->pos[1] = offset_pos(frame->pos[0], leave, d * (0.5 + frand(1.0)));
       frame->pos[2] = offset_pos(frame->pos[3], enter, d * (0.5 + frand(1.0)));
@@ -225,8 +254,16 @@ image_loaded_cb (const char *filename, XRectangle *geom,
 
   if (wire)
     {
-      frame->w = (int)(MI_WIDTH(mi)  * scale) - 1;
-      frame->h = (int)(MI_HEIGHT(mi) * scale) - 1;
+      if (random() % 2)
+        {
+          frame->w = (int)(MI_WIDTH(mi)  * scale) - 1;
+          frame->h = (int)(MI_HEIGHT(mi) * scale) - 1;
+        }
+      else
+        {
+          frame->w = (int)(MI_HEIGHT(mi) * scale) - 1;
+          frame->h = (int)(MI_WIDTH(mi)  * scale) - 1;
+        }
       if (frame->w <= 10) frame->w = 10;
       if (frame->h <= 10) frame->h = 10;
       frame->geom.width  = frame->w;
@@ -254,8 +291,12 @@ image_loaded_cb (const char *filename, XRectangle *geom,
     free (frame->title);
   frame->title = (filename ? strdup (filename) : 0);
 
-  if (frame->title)   /* strip filename to part after last /. */
+  /* xscreensaver-getimage returns paths relative to the image directory
+     now, so leave the sub-directory part in.  Unless it's an absolute path.
+  */
+  if (frame->title && frame->title[0] == '/')
     {
+      /* strip filename to part after last /. */
       char *s = strrchr (frame->title, '/');
       if (s) strcpy (frame->title, s+1);
     }
@@ -309,25 +350,21 @@ load_image (ModeInfo *mi)
 
 
 static void
-loading_msg (ModeInfo *mi, int n)
+loading_msg (ModeInfo *mi)
 {
   photopile_state *ss = &sss[MI_SCREEN(mi)];
   int wire = MI_IS_WIREFRAME(mi);
-  char text[100];
-  GLfloat scale;
+  const char text[] = "Loading...";
 
   if (wire) return;
 
-  if (n == 0)
-    sprintf (text, "Loading images...");
-  else
-    sprintf (text, "Loading images...  (%d%%)",
-             (int) (n * 100 / MI_COUNT(mi)));
-
-  if (ss->loading_sw == 0)    /* only do this once, so that the string doesn't move. */
-    ss->loading_sw = texture_string_width (ss->texfont, text, &ss->loading_sh);
-
-  scale = ss->loading_sh / (GLfloat) MI_HEIGHT(mi);
+  if (ss->loading_sw == 0)    /* only do this once */
+    {
+      XCharStruct e;
+      texture_string_metrics (ss->texfont, text, &e, 0, 0);
+      ss->loading_sw = e.width;
+      ss->loading_sh = e.ascent + e.descent;
+    }
 
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
@@ -338,7 +375,7 @@ loading_msg (ModeInfo *mi, int n)
   glMatrixMode(GL_MODELVIEW);
   glPushMatrix();
   glLoadIdentity();
-  gluOrtho2D(0, MI_WIDTH(mi), 0, MI_HEIGHT(mi));
+  glOrtho(0, MI_WIDTH(mi), 0, MI_HEIGHT(mi), -1, 1);
 
   glTranslatef ((MI_WIDTH(mi)  - ss->loading_sw) / 2,
                 (MI_HEIGHT(mi) - ss->loading_sh) / 2,
@@ -361,40 +398,32 @@ loading_msg (ModeInfo *mi, int n)
 
 
 static Bool
-load_initial_images (ModeInfo *mi)
+loading_initial_image (ModeInfo *mi)
 {
   photopile_state *ss = &sss[MI_SCREEN(mi)];
 
   if (ss->frames[ss->nframe].loaded_p)
     {
-      /* The current image has been fully loaded. */
-      if (++ss->nframe < MI_COUNT(mi))
-        {
-          /* Start the next one loading.  (We run the image loader
-           * asynchronously, but we load them one at a time.)
-           */
-          load_image(mi);
-        }
-      else
+      /* The initial image has been fully loaded, start fading it in. */
+      int i;
+
+      for (i = 0; i < ss->nframe; ++i)
         {
-          /* loaded all initial images, start fading them in */
-          int i;
-
-          for (i = 0; i < ss->nframe; ++i)
-            {
-              ss->frames[i].pos[3].x = MI_WIDTH(mi) * 0.5;
-              ss->frames[i].pos[3].y = MI_HEIGHT(mi) * 0.5;
-              ss->frames[i].pos[3].angle = 0.0;
-            }
-          set_new_positions(ss);
-
-          ss->mode = IN;
-          ss->mode_tick = fade_ticks / speed;
+          ss->frames[i].pos[3].x = MI_WIDTH(mi) * 0.5;
+          ss->frames[i].pos[3].y = MI_HEIGHT(mi) * 0.5;
+          ss->frames[i].pos[3].angle = 0.0;
         }
+      set_new_positions(ss);
+
+      ss->mode = SHUFFLE;
+      ss->mode_tick = fade_ticks / speed;
+    }
+  else
+    {
+      loading_msg(mi);
     }
 
-  loading_msg(mi, ss->nframe);
-  return (ss->mode != EARLY);
+  return (ss->mode == EARLY);
 }
 
 
@@ -408,7 +437,16 @@ reshape_photopile (ModeInfo *mi, int width, int height)
 
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
-  gluOrtho2D(0, MI_WIDTH(mi), 0, MI_HEIGHT(mi));
+  glOrtho(0, MI_WIDTH(mi), 0, MI_HEIGHT(mi), -1, 1);
+
+# ifdef HAVE_MOBILE    /* Keep it the same relative size when rotated. */
+  {
+    GLfloat h = MI_HEIGHT(mi) / (GLfloat) MI_WIDTH(mi);
+    int o = (int) current_device_rotation();
+    if (o != 0 && o != 180 && o != -180)
+      glScalef (1/h, h, 1);
+  }
+# endif
 
   glClear(GL_COLOR_BUFFER_BIT);
 }
@@ -419,7 +457,7 @@ reshape_photopile (ModeInfo *mi, int width, int height)
 static void
 hack_resources (Display *dpy)
 {
-# ifndef HAVE_COCOA
+# ifndef HAVE_JWXYZ
   char *res = "desktopGrabber";
   char *val = get_string_resource (dpy, res, "DesktopGrabber");
   char buf1[255];
@@ -431,7 +469,7 @@ hack_resources (Display *dpy)
   value.addr = buf2;
   value.size = strlen(buf2);
   XrmPutResource (&db, buf1, "String", &value);
-# endif /* !HAVE_COCOA */
+# endif /* !HAVE_JWXYZ */
 }
 
 
@@ -442,38 +480,18 @@ init_photopile (ModeInfo *mi)
   photopile_state *ss;
   int wire = MI_IS_WIREFRAME(mi);
 
-  if (sss == NULL) {
-    if ((sss = (photopile_state *)
-         calloc (MI_NUM_SCREENS(mi), sizeof(photopile_state))) == NULL)
-      return;
-  }
+  MI_INIT (mi, sss, NULL);
   ss = &sss[screen];
   ss->mi = mi;
 
   if ((ss->glx_context = init_GL(mi)) != NULL) {
     reshape_photopile (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+    clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
   } else {
     MI_CLEARWINDOW(mi);
   }
 
-  glDisable (GL_LIGHTING);
-  glEnable (GL_DEPTH_TEST);
-  glDisable (GL_CULL_FACE);
-
-  if (! wire)
-    {
-      glShadeModel (GL_SMOOTH);
-      glEnable (GL_LINE_SMOOTH);
-      /* This gives us a transparent diagonal slice through each image! */
-      /* glEnable (GL_POLYGON_SMOOTH); */
-      glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
-      glEnable (GL_BLEND);
-      glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
-      glEnable (GL_POLYGON_OFFSET_FILL);
-      glPolygonOffset (1.0, 1.0);
-    }
-
+  ss->shadow = init_drop_shadow();
   ss->texfont = load_texture_font (MI_DISPLAY(mi), "font");
 
   if (debug_p)
@@ -486,7 +504,7 @@ init_photopile (ModeInfo *mi)
       int i;
       for (i = 0; i < MI_COUNT(mi) + 1; ++i)
         {
-          glGenTextures(1, &(ss->frames[i].texid));
+          glGenTextures (1, &(ss->frames[i].texid));
           if (ss->frames[i].texid <= 0) abort();
         }
     }
@@ -503,18 +521,38 @@ draw_image (ModeInfo *mi, int i, GLfloat t, GLfloat s, GLfloat z)
   photopile_state *ss = &sss[MI_SCREEN(mi)];
   image *frame = ss->frames + i;
 
-  GLfloat texw  = frame->geom.width  / (GLfloat) frame->tw;
-  GLfloat texh  = frame->geom.height / (GLfloat) frame->th;
-  GLfloat texx1 = frame->geom.x / (GLfloat) frame->tw;
-  GLfloat texy1 = frame->geom.y / (GLfloat) frame->th;
-  GLfloat texx2 = texx1 + texw;
-  GLfloat texy2 = texy1 + texh;
-
   position pos = interpolate(t, frame->pos);
   GLfloat w = frame->geom.width * 0.5;
   GLfloat h = frame->geom.height * 0.5;
+  GLfloat z1 = z - 0.25 / (MI_COUNT(mi) + 1);
+  GLfloat z2 = z - 0.5  / (MI_COUNT(mi) + 1); 
+  GLfloat w1 = w;
+  GLfloat h1 = h;
+  GLfloat h2 = h;
+
+  if (polaroid_p)
+    {
+      GLfloat minSize = MIN(w, h);
+      GLfloat maxSize = MAX(w, h);
+
+      /* Clip or scale image to fit in the frame.
+       */
+      if (clip_p)
+        {
+          w = h = minSize;
+        }
+      else
+        {
+          GLfloat scale = minSize / maxSize;
+          w *= scale;
+          h *= scale;
+        }
 
-  glBindTexture (GL_TEXTURE_2D, frame->texid);
+      w1 = minSize * 1.16;      /* enlarge frame border */
+      h1 = minSize * 1.5;
+      h2 = w1;
+      s /= 1.5;                 /* compensate for border size */
+    }
 
   glPushMatrix();
 
@@ -524,24 +562,78 @@ draw_image (ModeInfo *mi, int i, GLfloat t, GLfloat s, GLfloat z)
   glRotatef (pos.angle, 0, 0, 1);
   glScalef (s, s, 1);
 
-  /* Draw the image quad. */
+  /* Draw the drop shadow. */
+  if (shadows_p && !wire)
+    {
+      glColor3f (0, 0, 0);
+      draw_drop_shadow(ss->shadow, -w1, -h1, z2, 2.0 * w1, h1 + h2, 20.0);
+      glDisable (GL_BLEND);
+    }
+
+  glDisable (GL_LIGHTING);
+  glEnable (GL_DEPTH_TEST);
+  glDisable (GL_CULL_FACE);
+
+  /* Draw the retro instant-film frame.
+   */
+  if (polaroid_p)
+    {
+      if (! wire)
+        {
+          glShadeModel (GL_SMOOTH);
+          glEnable (GL_LINE_SMOOTH);
+          glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
+
+          glColor3f (1, 1, 1);
+          glBegin (GL_QUADS);
+          glVertex3f (-w1, -h1, z2);
+          glVertex3f ( w1, -h1, z2);
+          glVertex3f ( w1,  h2, z2);
+          glVertex3f (-w1,  h2, z2);
+          glEnd();
+        }
+
+      glLineWidth (1.0);
+      glColor3f (0.5, 0.5, 0.5);
+      glBegin (GL_LINE_LOOP);
+      glVertex3f (-w1, -h1, z);
+      glVertex3f ( w1, -h1, z);
+      glVertex3f ( w1,  h2, z);
+      glVertex3f (-w1,  h2, z);
+      glEnd();
+    }
+
+  /* Draw the image quad.
+   */
   if (! wire)
     {
-      glColor3f (1, 1, 1);
+      GLfloat texw = w / frame->tw;
+      GLfloat texh = h / frame->th;
+      GLfloat texx = (frame->geom.x + 0.5 * frame->geom.width)  / frame->tw;
+      GLfloat texy = (frame->geom.y + 0.5 * frame->geom.height) / frame->th;
+
+      glBindTexture (GL_TEXTURE_2D, frame->texid);
       glEnable (GL_TEXTURE_2D);
+      glColor3f (1, 1, 1);
       glBegin (GL_QUADS);
-      glTexCoord2f (texx1, texy2); glVertex3f (-w, -h, z);
-      glTexCoord2f (texx2, texy2); glVertex3f ( w, -h, z);
-      glTexCoord2f (texx2, texy1); glVertex3f ( w,  h, z);
-      glTexCoord2f (texx1, texy1); glVertex3f (-w,  h, z);
+      glTexCoord2f (texx - texw, texy + texh); glVertex3f (-w, -h, z1);
+      glTexCoord2f (texx + texw, texy + texh); glVertex3f ( w, -h, z1);
+      glTexCoord2f (texx + texw, texy - texh); glVertex3f ( w,  h, z1);
+      glTexCoord2f (texx - texw, texy - texh); glVertex3f (-w,  h, z1);
       glEnd();
+      glDisable (GL_TEXTURE_2D);
     }
 
   /* Draw a box around it.
    */
-  glLineWidth (2.0);
+  if (! wire)
+    {
+      glShadeModel (GL_SMOOTH);
+      glEnable (GL_LINE_SMOOTH);
+      glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
+    }
+  glLineWidth (1.0);
   glColor3f (0.5, 0.5, 0.5);
-  glDisable (GL_TEXTURE_2D);
   glBegin (GL_LINE_LOOP);
   glVertex3f (-w, -h, z);
   glVertex3f ( w, -h, z);
@@ -553,20 +645,62 @@ draw_image (ModeInfo *mi, int i, GLfloat t, GLfloat s, GLfloat z)
    */
   if (titles_p)
     {
-      int sw, sh;
-      GLfloat scale = 0.5;
-      char *title = frame->title ? frame->title : "(untitled)";
-      sw = texture_string_width (ss->texfont, title, &sh);
+      int sw = 0, sh = 0;
+      int ascent, descent;
+      GLfloat tw = w * 2;
+      GLfloat th = h1 - h;
+      GLfloat scale = 1;
+      const char *title = frame->title ? frame->title : "(untitled)";
+      XCharStruct e;
+
+      texture_string_metrics (ss->texfont, title, &e, &ascent, &descent);
+      sw = e.width;
+      sh = ascent; /* + descent; */
+
+      /* Scale the text to match the pixel size of the photo */
+      scale *= w / 150.0;
+
+# if defined(HAVE_COCOA)
+      scale /= 2;
+# endif
+
+# if defined(HAVE_MOBILE)
+      scale /= 2;
+# endif
+
+      /* Clip characters off the left end of the string until it fits. */
+      if (clip_p || polaroid_p)
+        while (sw * scale > tw && strlen (title) > 10)
+          {
+            title++;
+            texture_string_metrics (ss->texfont, title, &e, &ascent, &descent);
+            sw = e.width;
+          }
+
+      if (th <= 0)  /* Non-polaroid */
+        th = -sh * 1.2;
+
+      glTranslatef (-w, -h1, 0);
+      glTranslatef ((tw - sw*scale) / 2, (th - sh*scale) / 2, 0);
 
-      glTranslatef (-sw*scale*0.5, -h - sh*scale, z);
       glScalef (scale, scale, 1);
 
-      glColor3f (1, 1, 1);
+      if (wire || !polaroid_p)
+        {
+          glColor3f (1, 1, 1);
+        }
+      else
+        {
+          glColor3f (0.5, 0.5, 0.5);
+        }
 
       if (!wire)
         {
           glEnable (GL_TEXTURE_2D);
+          glEnable (GL_BLEND);
+          glDisable (GL_DEPTH_TEST);
           print_texture_string (ss->texfont, title);
+          glEnable (GL_DEPTH_TEST);
         }
       else
         {
@@ -577,6 +711,7 @@ draw_image (ModeInfo *mi, int i, GLfloat t, GLfloat s, GLfloat z)
           glVertex3f (0,  sh, 0);
           glEnd();
         }
+
     }
 
   glPopMatrix();
@@ -595,7 +730,7 @@ draw_photopile (ModeInfo *mi)
   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(ss->glx_context));
 
   if (ss->mode == EARLY)
-    if (!load_initial_images (mi))
+    if (loading_initial_image (mi))
       return;
 
   /* Only check the wall clock every 10 frames */
@@ -610,12 +745,19 @@ draw_photopile (ModeInfo *mi)
   {
     GLfloat t;
 
+    glPushMatrix();
+    glTranslatef (MI_WIDTH(mi)/2, MI_HEIGHT(mi)/2, 0);
+    glRotatef(current_device_rotation(), 0, 0, 1);
+    glTranslatef (-MI_WIDTH(mi)/2, -MI_HEIGHT(mi)/2, 0);
+
     /* Handle state transitions. */
     switch (ss->mode)
       {
-      case IN:
+      case SHUFFLE:
         if (--ss->mode_tick <= 0)
           {
+            ss->nframe = (ss->nframe+1) % (MI_COUNT(mi)+1);
+
             ss->mode = NORMAL;
             ss->last_time = time((time_t *) 0);
           }
@@ -635,15 +777,6 @@ draw_photopile (ModeInfo *mi)
             ss->mode_tick = fade_ticks / speed;
           }
         break;
-      case SHUFFLE:
-        if (--ss->mode_tick <= 0)
-          {
-            ss->nframe = (ss->nframe+1) % (MI_COUNT(mi)+1);
-
-            ss->mode = NORMAL;
-            ss->last_time = time((time_t *) 0);
-          }
-        break;
       default:
         abort();
       }
@@ -655,34 +788,36 @@ draw_photopile (ModeInfo *mi)
     for (i = 0; i < MI_COUNT(mi) + (ss->mode == SHUFFLE); ++i)
       {
         int j = (ss->nframe + i + 1) % (MI_COUNT(mi) + 1);
-        GLfloat s = 1.0;
-        GLfloat z = (GLfloat)i / (MI_COUNT(mi) + 1);
 
-        switch (ss->mode)
+        if (ss->frames[j].loaded_p)
           {
-          case IN:
-            s *= t;
-            break;
-          case NORMAL:
-          case LOADING:
-            t = 1.0;
-            break;
-          case SHUFFLE:
-            if (i == MI_COUNT(mi))
-              {
-                s *= t;
-              }
-            else if (i == 0)
+            GLfloat s = 1.0;
+            GLfloat z = (GLfloat)i / (MI_COUNT(mi) + 1);
+
+            switch (ss->mode)
               {
-                s *= 1.0 - t;
+              case SHUFFLE:
+                if (i == MI_COUNT(mi))
+                  {
+                    s *= t;
+                  }
+                else if (i == 0)
+                  {
+                    s *= 1.0 - t;
+                  }
+                break;
+              case NORMAL:
+              case LOADING:
+                t = 1.0;
+                break;
+              default:
+                abort();
               }
-            break;
-          default:
-            abort();
-          }
 
-        draw_image(mi, j, t, s, z);
+            draw_image(mi, j, t, s, z);
+          }
       }
+    glPopMatrix();
   }
 
   if (mi->fps_p) do_fps (mi);