Index: mythgallery/glsingleview.cpp
===================================================================
--- mythgallery/glsingleview.cpp	(revision 16983)
+++ mythgallery/glsingleview.cpp	(working copy)
@@ -93,7 +93,10 @@
       // Unshared effect state variables
       m_effect_cube_xrot(0.0f),
       m_effect_cube_yrot(0.0f),
-      m_effect_cube_zrot(0.0f)
+      m_effect_cube_zrot(0.0f),
+      m_effect_kenBurns_image_ready(true),
+      m_effect_kenBurns_initialized(false),
+      m_effect_kenBurns_new_image_started(true)
 {
     m_scaleMax = (gContext->GetNumSetting("GalleryScaleMax", 0) > 0);
 
@@ -661,6 +664,7 @@
     m_effect_map.insert("slide (gl)",      "EffectSlide");
     m_effect_map.insert("flutter (gl)",    "EffectFlutter");
     m_effect_map.insert("cube (gl)",       "EffectCube");
+    m_effect_map.insert("Ken Burns (gl)",  "EffectKenBurns");
 }
 
 void GLSingleView::RunEffect(const QString &effect)
@@ -683,6 +687,8 @@
         EffectFlutter();
     else if (effect == "EffectCube")
         EffectCube();
+    else if (effect == "EffectKenBurns")
+        EffectKenBurns();
     else //if (effect == "EffectNone")
         EffectNone();
 }
@@ -1166,6 +1172,154 @@
     m_effect_current_frame++;
 }
 
+void GLSingleView::EffectKenBurns(void)
+{
+
+    float single_image_pct = 0.75;
+    float trans_pct = 1.0 - single_image_pct;
+    float scale_max, x_loc, y_loc;
+    float scale_factor = 0;
+
+    //initialize effect   
+    if (!m_effect_kenBurns_initialized)
+    {
+        m_effect_kenBurns_initialized = !m_effect_kenBurns_initialized;
+        m_effect_kenBurns_item = NULL;
+        // Need to load images in the background to keep effect smooth
+        m_effect_kenBurns_imageLoadThread = new KenBurnsImageLoader(this, m_itemList, m_texSize, m_screenSize);
+        //Since total image time is longer/different than effect time, create image timers
+        m_effect_kenBurns_image_time[m_texCur ? 0 : 1].restart();
+        // Pan image to a random location
+        m_effect_kenBurns_location_x[0] = (2.0 * rand() / (RAND_MAX + 1.0f)) - 1;
+        m_effect_kenBurns_location_y[0] = (2.0 * rand() / (RAND_MAX + 1.0f)) - 1;
+        // Since first two images are preloaded, harcode  them to zoom in
+        m_effect_kenBurns_projection[0] = 1;
+        m_effect_kenBurns_projection[1] = 1;
+        m_effect_kenBurns_image_timeout_inv = 1 / (m_effect_transition_timeout + 
+                (m_effect_transition_timeout * trans_pct));
+    }
+
+    if (m_effect_frame_time.elapsed() >= m_effect_transition_timeout)
+    {
+        // Effect timed out, move new image to old image but don't load new image yet...
+        m_tex1First = !m_tex1First;
+        m_texCur      = (m_texCur) ? 0 : 1;
+        m_effect_current_frame  = 0;
+        m_effect_frame_time.restart(); 
+
+        if (m_effect_kenBurns_item) // Since first two images are preloaded dont randomly select sizefactor
+            // Randomly determine whether to zoom in or zoom out 
+            m_effect_kenBurns_projection[m_texCur] = 1 + (int)((2.0f * rand() / (RAND_MAX + 1.0f))); 
+
+        m_effect_kenBurns_image_ready = false;
+
+        // Find next image to be loaded
+        int oldpos = m_pos;
+
+        while (true)
+        {
+            m_pos = m_slideshow_sequence->next();
+            m_effect_kenBurns_item = m_itemList.at(m_pos);
+            if (m_effect_kenBurns_item)
+            {
+                // Skip movies
+                if (QFile::exists(m_effect_kenBurns_item->GetPath()) && !GalleryUtil::isMovie(m_effect_kenBurns_item->GetPath()))
+                {
+                    break;
+                }
+            }
+            if (m_pos == oldpos)
+            {
+                // No valid items!!!
+                close();
+            }
+        }
+        m_effect_kenBurns_imageLoadThread->Initialize(m_pos);
+        m_effect_kenBurns_imageLoadThread->start();
+    }
+
+    float t[2];
+    t[m_texCur] = m_effect_kenBurns_image_time[m_texCur].elapsed() * m_effect_kenBurns_image_timeout_inv;
+    t[m_texCur ? 0 : 1] = m_effect_kenBurns_image_time[m_texCur ? 0 : 1].elapsed() *
+    m_effect_kenBurns_image_timeout_inv;
+    float effect_pct = m_effect_frame_time.elapsed() *  m_effect_transition_timeout_inv;
+
+    // Load new image if its ready
+    if (effect_pct > single_image_pct && m_effect_kenBurns_image_ready) 
+    {
+        if (!m_effect_kenBurns_new_image_started)
+        { 			
+            if (m_effect_kenBurns_item) //Do not create textures for first two images, since they are preloaded
+            {
+                m_texItem[!m_tex1First].SetItem(m_effect_kenBurns_item, m_effect_kenBurns_orig_image_size);
+                m_texItem[!m_tex1First].ScaleTo(m_screenSize);
+                m_texItem[!m_tex1First].Init(m_effect_kenBurns_image);
+                UpdateLCD(m_effect_kenBurns_item);
+            }
+
+            m_effect_kenBurns_location_x[m_texCur] = (2.0 * rand() / (RAND_MAX + 1.0f)) - 1; 
+            m_effect_kenBurns_location_y[m_texCur] = (2.0 * rand() / (RAND_MAX + 1.0f)) - 1; 
+            m_effect_kenBurns_image_time[m_texCur].restart();
+            m_effect_kenBurns_new_image_started = true;
+        }
+        if (m_effect_kenBurns_projection[m_texCur] == 1) // Zoom in image
+        {
+            // Start in center and pan out
+            x_loc = m_effect_kenBurns_location_x[m_texCur] * t[m_texCur]; 
+            y_loc = m_effect_kenBurns_location_y[m_texCur] * t[m_texCur]; 		
+            scale_max = FindMaxScale(x_loc,y_loc);
+            scale_factor = 	1.0f + (scale_max * t[m_texCur]); 
+        }
+        else // Zoom out image
+        {
+            // Start at random location and pan to center
+            x_loc = m_effect_kenBurns_location_x[m_texCur] -  m_effect_kenBurns_location_x[m_texCur] * t[m_texCur]; 
+            y_loc = m_effect_kenBurns_location_y[m_texCur] -  m_effect_kenBurns_location_y[m_texCur] * t[m_texCur];
+            scale_max = FindMaxScale(x_loc,y_loc);
+            scale_factor = 	1.0f + scale_max -  (scale_max * t[m_texCur]);
+        }
+
+        glMatrixMode(GL_MODELVIEW);
+        glLoadIdentity();
+        glTranslatef(x_loc, y_loc, 0.0f);
+
+        m_texItem[m_texCur].MakeQuad((effect_pct-single_image_pct)*4, scale_factor); 
+    }
+
+    //Load old picture
+    if (m_effect_kenBurns_projection[m_texCur ? 0 : 1] == 1)
+    {
+        x_loc = m_effect_kenBurns_location_x[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1]; 
+        y_loc = m_effect_kenBurns_location_y[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1];
+        scale_max = FindMaxScale(x_loc,y_loc);
+        scale_factor = 	1.0f + (scale_max * t[m_texCur ? 0 : 1]);
+    }
+    else 
+    {
+        x_loc = m_effect_kenBurns_location_x[m_texCur ? 0 : 1] -  m_effect_kenBurns_location_x[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1]; 
+        y_loc = m_effect_kenBurns_location_y[m_texCur ? 0 : 1] -  m_effect_kenBurns_location_y[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1];
+        scale_max = FindMaxScale(x_loc,y_loc);
+        scale_factor = 	1.0f + scale_max -  (scale_max * t[m_texCur ? 0 : 1]);
+    } 
+
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    glTranslatef(x_loc, y_loc, 0.0f);
+
+    if (effect_pct<= single_image_pct) 
+    {
+        m_effect_kenBurns_new_image_started=false;
+        m_texItem[m_texCur ? 0 : 1].MakeQuad(1.0f, scale_factor); //
+    }
+    else // Fade out image
+    {
+        m_texItem[m_texCur ? 0 : 1].MakeQuad(1.0f - ((effect_pct-single_image_pct)*4), scale_factor); 
+
+    }
+
+    m_effect_current_frame++;
+}
+
 void GLSingleView::SlideTimeout(void)
 {
     bool wasMovie = false, isMovie = false;
@@ -1269,3 +1423,48 @@
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 }
+
+void GLSingleView::LoadImage(QImage image, QSize origSize)
+{
+    m_effect_kenBurns_image = image;
+    m_effect_kenBurns_orig_image_size = origSize;
+}
+
+float GLSingleView::FindMaxScale(float x_loc, float y_loc)
+{
+    // Zoom big enough to keep the entire image on screen when we pan
+    if (abs(x_loc) > abs(y_loc))
+        return abs(x_loc) * 2;
+    else
+        return abs(y_loc) * 2;
+}
+
+KenBurnsImageLoader::KenBurnsImageLoader(GLSingleView *singleView, ThumbList &itemList, QSize texSize, QSize screenSize)
+{
+    m_singleView = singleView;
+    m_itemList = itemList;
+    m_texSize = texSize;
+    m_screenSize = screenSize;
+}
+
+void KenBurnsImageLoader::Initialize(int pos)
+{
+    m_pos = pos;
+}
+
+void KenBurnsImageLoader::run() 
+{
+    ThumbItem *item = m_itemList.at(m_pos);
+    if (!item)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR + "No item at "<<m_pos);
+        return;
+    }
+    QImage image(item->GetPath());
+    if (image.isNull())
+        return;
+
+    m_singleView->LoadImage(QGLWidget::convertToGLFormat(image.smoothScale(m_texSize)), image.size());
+    m_singleView->Ready();
+
+}
Index: mythgallery/glsingleview.h
===================================================================
--- mythgallery/glsingleview.h	(revision 16983)
+++ mythgallery/glsingleview.h	(working copy)
@@ -41,6 +41,7 @@
 class QTimer;
 
 class GLSingleView;
+class KenBurnsImageLoader;
 
 class GLSDialog : public MythDialog
 {
@@ -66,6 +67,9 @@
     ~GLSingleView();
 
     void CleanUp(void);
+    void Ready(){m_effect_kenBurns_image_ready = true;}
+    void LoadImage(QImage image, QSize origSize);
+    
 
   protected:
     void initializeGL(void);
@@ -103,7 +107,11 @@
     void EffectSlide(void);
     void EffectFlutter(void);
     void EffectCube(void);
-
+    void EffectKenBurns(void);
+  
+  private:
+	float FindMaxScale(float x_loc, float y_loc);
+    
   private slots:
     void SlideTimeout(void);
 
@@ -134,7 +142,36 @@
     float         m_effect_cube_xrot;
     float         m_effect_cube_yrot;
     float         m_effect_cube_zrot;
+    float         m_effect_kenBurns_location_x[2];
+    float         m_effect_kenBurns_location_y[2];
+    int           m_effect_kenBurns_projection[2];
+    MythTimer 	  m_effect_kenBurns_image_time[2];
+    float         m_effect_kenBurns_image_timeout_inv;
+    KenBurnsImageLoader *m_effect_kenBurns_imageLoadThread;
+    bool          m_effect_kenBurns_image_ready;
+    QImage        m_effect_kenBurns_image;
+    QSize         m_effect_kenBurns_orig_image_size;
+    ThumbItem     *m_effect_kenBurns_item;
+    bool          m_effect_kenBurns_initialized;
+    bool          m_effect_kenBurns_new_image_started;
+    
 };
 
+class KenBurnsImageLoader : public QThread
+{
+public:
+    KenBurnsImageLoader(GLSingleView *singleView, ThumbList &itemList, QSize m_texSize, QSize m_screenSize);
+    void Initialize(int pos);
+    void run();
+private:
+	GLSingleView *m_singleView;
+    ThumbList     m_itemList;
+    int           m_pos;
+    bool          m_tex1First;
+    QSize         m_screenSize;
+    QSize         m_texSize;
+
+};
+
 #endif // USING_OPENGL
 #endif // GLSINGLEVIEW_H
Index: mythgallery/imageview.cpp
===================================================================
--- mythgallery/imageview.cpp	(revision 16983)
+++ mythgallery/imageview.cpp	(working copy)
@@ -136,6 +136,7 @@
 {
     QMap<QString,QString> tmpMap = m_effect_map;
     tmpMap.remove("none");
+    tmpMap.remove("Ken Burns (gl)");
     QStringList t = tmpMap.keys();
     int i = (int) ( (float)(t.count()) * rand() / (RAND_MAX + 1.0f) );
     return tmpMap[t[i]];
Index: mythgallery/gallerysettings.cpp
===================================================================
--- mythgallery/gallerysettings.cpp	(revision 16983)
+++ mythgallery/gallerysettings.cpp	(working copy)
@@ -105,6 +105,7 @@
     gc->addSelection("flutter (gl)");
     gc->addSelection("cube (gl)");
     gc->addSelection("random (gl)");
+    gc->addSelection("Ken Burns (gl)");
     gc->setHelpText(QObject::tr("This is the type of OpenGL transition used "
                     "between pictures in slideshow mode."));
     return gc;
@@ -113,7 +114,7 @@
 static HostSpinBox *SlideshowOpenGLTransitionLength()
 {
     HostSpinBox *gc = new HostSpinBox(
-        "SlideshowOpenGLTransitionLength", 500, 10000, 500);
+        "SlideshowOpenGLTransitionLength", 500, 30000, 500);
     gc->setLabel(QObject::tr("Duration of OpenGL Transition (milliseconds)"));
     gc->setValue(2000);
     return gc;
@@ -159,7 +160,7 @@
 
 static HostSpinBox *SlideshowDelay()
 {
-    HostSpinBox *gc = new HostSpinBox("SlideshowDelay", 1, 600, 1);
+    HostSpinBox *gc = new HostSpinBox("SlideshowDelay", 0, 600, 1);
     gc->setLabel(QObject::tr("Slideshow Delay"));
     gc->setValue(5);
     gc->setHelpText(QObject::tr("This is the number of seconds to display each "
