Ticket #5176: kenburns.patch

File kenburns.patch, 12.5 KB (added by jshoor, 12 years ago)

ken burns effect patch

  • mythgallery/glsingleview.cpp

     
    9393      // Unshared effect state variables
    9494      m_effect_cube_xrot(0.0f),
    9595      m_effect_cube_yrot(0.0f),
    96       m_effect_cube_zrot(0.0f)
     96      m_effect_cube_zrot(0.0f),
     97      m_effect_kenBurns_image_ready(true),
     98      m_effect_kenBurns_initialized(false),
     99      m_effect_kenBurns_new_image_started(true)
    97100{
    98101    m_scaleMax = (gContext->GetNumSetting("GalleryScaleMax", 0) > 0);
    99102
     
    661664    m_effect_map.insert("slide (gl)",      "EffectSlide");
    662665    m_effect_map.insert("flutter (gl)",    "EffectFlutter");
    663666    m_effect_map.insert("cube (gl)",       "EffectCube");
     667    m_effect_map.insert("Ken Burns (gl)",  "EffectKenBurns");
    664668}
    665669
    666670void GLSingleView::RunEffect(const QString &effect)
     
    683687        EffectFlutter();
    684688    else if (effect == "EffectCube")
    685689        EffectCube();
     690    else if (effect == "EffectKenBurns")
     691        EffectKenBurns();
    686692    else //if (effect == "EffectNone")
    687693        EffectNone();
    688694}
     
    11661172    m_effect_current_frame++;
    11671173}
    11681174
     1175void GLSingleView::EffectKenBurns(void)
     1176{
     1177
     1178    float single_image_pct = 0.75;
     1179    float trans_pct = 1.0 - single_image_pct;
     1180    float scale_max, x_loc, y_loc;
     1181    float scale_factor = 0;
     1182
     1183    //initialize effect   
     1184    if (!m_effect_kenBurns_initialized)
     1185    {
     1186        m_effect_kenBurns_initialized = !m_effect_kenBurns_initialized;
     1187        m_effect_kenBurns_item = NULL;
     1188        // Need to load images in the background to keep effect smooth
     1189        m_effect_kenBurns_imageLoadThread = new KenBurnsImageLoader(this, m_itemList, m_texSize, m_screenSize);
     1190        //Since total image time is longer/different than effect time, create image timers
     1191        m_effect_kenBurns_image_time[m_texCur ? 0 : 1].restart();
     1192        // Pan image to a random location
     1193        m_effect_kenBurns_location_x[0] = (2.0 * rand() / (RAND_MAX + 1.0f)) - 1;
     1194        m_effect_kenBurns_location_y[0] = (2.0 * rand() / (RAND_MAX + 1.0f)) - 1;
     1195        // Since first two images are preloaded, harcode  them to zoom in
     1196        m_effect_kenBurns_projection[0] = 1;
     1197        m_effect_kenBurns_projection[1] = 1;
     1198        m_effect_kenBurns_image_timeout_inv = 1 / (m_effect_transition_timeout +
     1199                (m_effect_transition_timeout * trans_pct));
     1200    }
     1201
     1202    if (m_effect_frame_time.elapsed() >= m_effect_transition_timeout)
     1203    {
     1204        // Effect timed out, move new image to old image but don't load new image yet...
     1205        m_tex1First = !m_tex1First;
     1206        m_texCur      = (m_texCur) ? 0 : 1;
     1207        m_effect_current_frame  = 0;
     1208        m_effect_frame_time.restart();
     1209
     1210        if (m_effect_kenBurns_item) // Since first two images are preloaded dont randomly select sizefactor
     1211            // Randomly determine whether to zoom in or zoom out
     1212            m_effect_kenBurns_projection[m_texCur] = 1 + (int)((2.0f * rand() / (RAND_MAX + 1.0f)));
     1213
     1214        m_effect_kenBurns_image_ready = false;
     1215
     1216        // Find next image to be loaded
     1217        int oldpos = m_pos;
     1218
     1219        while (true)
     1220        {
     1221            m_pos = m_slideshow_sequence->next();
     1222            m_effect_kenBurns_item = m_itemList.at(m_pos);
     1223            if (m_effect_kenBurns_item)
     1224            {
     1225                // Skip movies
     1226                if (QFile::exists(m_effect_kenBurns_item->GetPath()) && !GalleryUtil::isMovie(m_effect_kenBurns_item->GetPath()))
     1227                {
     1228                    break;
     1229                }
     1230            }
     1231            if (m_pos == oldpos)
     1232            {
     1233                // No valid items!!!
     1234                close();
     1235            }
     1236        }
     1237        m_effect_kenBurns_imageLoadThread->Initialize(m_pos);
     1238        m_effect_kenBurns_imageLoadThread->start();
     1239    }
     1240
     1241    float t[2];
     1242    t[m_texCur] = m_effect_kenBurns_image_time[m_texCur].elapsed() * m_effect_kenBurns_image_timeout_inv;
     1243    t[m_texCur ? 0 : 1] = m_effect_kenBurns_image_time[m_texCur ? 0 : 1].elapsed() *
     1244    m_effect_kenBurns_image_timeout_inv;
     1245    float effect_pct = m_effect_frame_time.elapsed() *  m_effect_transition_timeout_inv;
     1246
     1247    // Load new image if its ready
     1248    if (effect_pct > single_image_pct && m_effect_kenBurns_image_ready)
     1249    {
     1250        if (!m_effect_kenBurns_new_image_started)
     1251        {                       
     1252            if (m_effect_kenBurns_item) //Do not create textures for first two images, since they are preloaded
     1253            {
     1254                m_texItem[!m_tex1First].SetItem(m_effect_kenBurns_item, m_effect_kenBurns_orig_image_size);
     1255                m_texItem[!m_tex1First].ScaleTo(m_screenSize);
     1256                m_texItem[!m_tex1First].Init(m_effect_kenBurns_image);
     1257                UpdateLCD(m_effect_kenBurns_item);
     1258            }
     1259
     1260            m_effect_kenBurns_location_x[m_texCur] = (2.0 * rand() / (RAND_MAX + 1.0f)) - 1;
     1261            m_effect_kenBurns_location_y[m_texCur] = (2.0 * rand() / (RAND_MAX + 1.0f)) - 1;
     1262            m_effect_kenBurns_image_time[m_texCur].restart();
     1263            m_effect_kenBurns_new_image_started = true;
     1264        }
     1265        if (m_effect_kenBurns_projection[m_texCur] == 1) // Zoom in image
     1266        {
     1267            // Start in center and pan out
     1268            x_loc = m_effect_kenBurns_location_x[m_texCur] * t[m_texCur];
     1269            y_loc = m_effect_kenBurns_location_y[m_texCur] * t[m_texCur];               
     1270            scale_max = FindMaxScale(x_loc,y_loc);
     1271            scale_factor =      1.0f + (scale_max * t[m_texCur]);
     1272        }
     1273        else // Zoom out image
     1274        {
     1275            // Start at random location and pan to center
     1276            x_loc = m_effect_kenBurns_location_x[m_texCur] -  m_effect_kenBurns_location_x[m_texCur] * t[m_texCur];
     1277            y_loc = m_effect_kenBurns_location_y[m_texCur] -  m_effect_kenBurns_location_y[m_texCur] * t[m_texCur];
     1278            scale_max = FindMaxScale(x_loc,y_loc);
     1279            scale_factor =      1.0f + scale_max -  (scale_max * t[m_texCur]);
     1280        }
     1281
     1282        glMatrixMode(GL_MODELVIEW);
     1283        glLoadIdentity();
     1284        glTranslatef(x_loc, y_loc, 0.0f);
     1285
     1286        m_texItem[m_texCur].MakeQuad((effect_pct-single_image_pct)*4, scale_factor);
     1287    }
     1288
     1289    //Load old picture
     1290    if (m_effect_kenBurns_projection[m_texCur ? 0 : 1] == 1)
     1291    {
     1292        x_loc = m_effect_kenBurns_location_x[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1];
     1293        y_loc = m_effect_kenBurns_location_y[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1];
     1294        scale_max = FindMaxScale(x_loc,y_loc);
     1295        scale_factor =  1.0f + (scale_max * t[m_texCur ? 0 : 1]);
     1296    }
     1297    else
     1298    {
     1299        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];
     1300        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];
     1301        scale_max = FindMaxScale(x_loc,y_loc);
     1302        scale_factor =  1.0f + scale_max -  (scale_max * t[m_texCur ? 0 : 1]);
     1303    }
     1304
     1305    glMatrixMode(GL_MODELVIEW);
     1306    glLoadIdentity();
     1307    glTranslatef(x_loc, y_loc, 0.0f);
     1308
     1309    if (effect_pct<= single_image_pct)
     1310    {
     1311        m_effect_kenBurns_new_image_started=false;
     1312        m_texItem[m_texCur ? 0 : 1].MakeQuad(1.0f, scale_factor); //
     1313    }
     1314    else // Fade out image
     1315    {
     1316        m_texItem[m_texCur ? 0 : 1].MakeQuad(1.0f - ((effect_pct-single_image_pct)*4), scale_factor);
     1317
     1318    }
     1319
     1320    m_effect_current_frame++;
     1321}
     1322
    11691323void GLSingleView::SlideTimeout(void)
    11701324{
    11711325    bool wasMovie = false, isMovie = false;
     
    12691423    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    12701424    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    12711425}
     1426
     1427void GLSingleView::LoadImage(QImage image, QSize origSize)
     1428{
     1429    m_effect_kenBurns_image = image;
     1430    m_effect_kenBurns_orig_image_size = origSize;
     1431}
     1432
     1433float GLSingleView::FindMaxScale(float x_loc, float y_loc)
     1434{
     1435    // Zoom big enough to keep the entire image on screen when we pan
     1436    if (abs(x_loc) > abs(y_loc))
     1437        return abs(x_loc) * 2;
     1438    else
     1439        return abs(y_loc) * 2;
     1440}
     1441
     1442KenBurnsImageLoader::KenBurnsImageLoader(GLSingleView *singleView, ThumbList &itemList, QSize texSize, QSize screenSize)
     1443{
     1444    m_singleView = singleView;
     1445    m_itemList = itemList;
     1446    m_texSize = texSize;
     1447    m_screenSize = screenSize;
     1448}
     1449
     1450void KenBurnsImageLoader::Initialize(int pos)
     1451{
     1452    m_pos = pos;
     1453}
     1454
     1455void KenBurnsImageLoader::run()
     1456{
     1457    ThumbItem *item = m_itemList.at(m_pos);
     1458    if (!item)
     1459    {
     1460        VERBOSE(VB_IMPORTANT, LOC_ERR + "No item at "<<m_pos);
     1461        return;
     1462    }
     1463    QImage image(item->GetPath());
     1464    if (image.isNull())
     1465        return;
     1466
     1467    m_singleView->LoadImage(QGLWidget::convertToGLFormat(image.smoothScale(m_texSize)), image.size());
     1468    m_singleView->Ready();
     1469
     1470}
  • mythgallery/glsingleview.h

     
    4141class QTimer;
    4242
    4343class GLSingleView;
     44class KenBurnsImageLoader;
    4445
    4546class GLSDialog : public MythDialog
    4647{
     
    6667    ~GLSingleView();
    6768
    6869    void CleanUp(void);
     70    void Ready(){m_effect_kenBurns_image_ready = true;}
     71    void LoadImage(QImage image, QSize origSize);
     72   
    6973
    7074  protected:
    7175    void initializeGL(void);
     
    103107    void EffectSlide(void);
    104108    void EffectFlutter(void);
    105109    void EffectCube(void);
    106 
     110    void EffectKenBurns(void);
     111 
     112  private:
     113        float FindMaxScale(float x_loc, float y_loc);
     114   
    107115  private slots:
    108116    void SlideTimeout(void);
    109117
     
    134142    float         m_effect_cube_xrot;
    135143    float         m_effect_cube_yrot;
    136144    float         m_effect_cube_zrot;
     145    float         m_effect_kenBurns_location_x[2];
     146    float         m_effect_kenBurns_location_y[2];
     147    int           m_effect_kenBurns_projection[2];
     148    MythTimer     m_effect_kenBurns_image_time[2];
     149    float         m_effect_kenBurns_image_timeout_inv;
     150    KenBurnsImageLoader *m_effect_kenBurns_imageLoadThread;
     151    bool          m_effect_kenBurns_image_ready;
     152    QImage        m_effect_kenBurns_image;
     153    QSize         m_effect_kenBurns_orig_image_size;
     154    ThumbItem     *m_effect_kenBurns_item;
     155    bool          m_effect_kenBurns_initialized;
     156    bool          m_effect_kenBurns_new_image_started;
     157   
    137158};
    138159
     160class KenBurnsImageLoader : public QThread
     161{
     162public:
     163    KenBurnsImageLoader(GLSingleView *singleView, ThumbList &itemList, QSize m_texSize, QSize m_screenSize);
     164    void Initialize(int pos);
     165    void run();
     166private:
     167        GLSingleView *m_singleView;
     168    ThumbList     m_itemList;
     169    int           m_pos;
     170    bool          m_tex1First;
     171    QSize         m_screenSize;
     172    QSize         m_texSize;
     173
     174};
     175
    139176#endif // USING_OPENGL
    140177#endif // GLSINGLEVIEW_H
  • mythgallery/imageview.cpp

     
    136136{
    137137    QMap<QString,QString> tmpMap = m_effect_map;
    138138    tmpMap.remove("none");
     139    tmpMap.remove("Ken Burns (gl)");
    139140    QStringList t = tmpMap.keys();
    140141    int i = (int) ( (float)(t.count()) * rand() / (RAND_MAX + 1.0f) );
    141142    return tmpMap[t[i]];
  • mythgallery/gallerysettings.cpp

     
    105105    gc->addSelection("flutter (gl)");
    106106    gc->addSelection("cube (gl)");
    107107    gc->addSelection("random (gl)");
     108    gc->addSelection("Ken Burns (gl)");
    108109    gc->setHelpText(QObject::tr("This is the type of OpenGL transition used "
    109110                    "between pictures in slideshow mode."));
    110111    return gc;
     
    113114static HostSpinBox *SlideshowOpenGLTransitionLength()
    114115{
    115116    HostSpinBox *gc = new HostSpinBox(
    116         "SlideshowOpenGLTransitionLength", 500, 10000, 500);
     117        "SlideshowOpenGLTransitionLength", 500, 30000, 500);
    117118    gc->setLabel(QObject::tr("Duration of OpenGL Transition (milliseconds)"));
    118119    gc->setValue(2000);
    119120    return gc;
     
    159160
    160161static HostSpinBox *SlideshowDelay()
    161162{
    162     HostSpinBox *gc = new HostSpinBox("SlideshowDelay", 1, 600, 1);
     163    HostSpinBox *gc = new HostSpinBox("SlideshowDelay", 0, 600, 1);
    163164    gc->setLabel(QObject::tr("Slideshow Delay"));
    164165    gc->setValue(5);
    165166    gc->setHelpText(QObject::tr("This is the number of seconds to display each "