Ticket #5176: kenburns4.patch

File kenburns4.patch, 19.6 KB (added by joepadmiraal, 11 years ago)

Updated kenburns3.patch to compile with the current trunk (19382)

  • mythgallery/glsingleview.cpp

     
    3131#include <QImage>
    3232#include <QDir>
    3333#include <QPainter>
     34#include <qbuffer.h>
    3435
    3536// MythTV plugin headers
    3637#include <mythtv/mythcontext.h>
     
    3839#include <mythtv/libmythui/mythuihelper.h>
    3940
    4041// MythGallery headers
     42#include "config.h"
    4143#include "glsingleview.h"
    4244#include "galleryutil.h"
    4345
     46#ifdef FDLIB_SUPPORT
     47#include <fdlib.h>
     48#endif
     49
    4450#define LOC QString("GLView: ")
    4551#define LOC_ERR QString("GLView, Error: ")
    4652
     
    94100      // Unshared effect state variables
    95101      m_effect_cube_xrot(0.0f),
    96102      m_effect_cube_yrot(0.0f),
    97       m_effect_cube_zrot(0.0f)
     103      m_effect_cube_zrot(0.0f),
     104      m_effect_kenBurns_image_ready(true),
     105      m_effect_kenBurns_initialized(false),
     106      m_effect_kenBurns_new_image_started(true)
    98107{
    99108    m_scaleMax = (gContext->GetNumSetting("GalleryScaleMax", 0) > 0);
    100109
     
    706715    m_effect_map.insert("slide (gl)",      "EffectSlide");
    707716    m_effect_map.insert("flutter (gl)",    "EffectFlutter");
    708717    m_effect_map.insert("cube (gl)",       "EffectCube");
     718    m_effect_map.insert("Ken Burns (gl)",  "EffectKenBurns");
    709719}
    710720
    711721void GLSingleView::RunEffect(const QString &effect)
     
    728738        EffectFlutter();
    729739    else if (effect == "EffectCube")
    730740        EffectCube();
     741    else if (effect == "EffectKenBurns")
     742        EffectKenBurns();
    731743    else //if (effect == "EffectNone")
    732744        EffectNone();
    733745}
     
    12111223    m_effect_current_frame++;
    12121224}
    12131225
     1226void GLSingleView::EffectKenBurns(void)
     1227{
     1228
     1229    float single_image_pct = 0.75;
     1230    float trans_pct = 1.0 - single_image_pct;
     1231    float scale_max, x_loc, y_loc;
     1232    float scale_factor = 0;
     1233
     1234    //initialize effect   
     1235    if (!m_effect_kenBurns_initialized)
     1236    {
     1237               
     1238        m_effect_kenBurns_initialized = !m_effect_kenBurns_initialized;
     1239        m_effect_kenBurns_item = NULL;
     1240        // Need to load images in the background to keep effect smooth
     1241        m_effect_kenBurns_imageLoadThread = new KenBurnsImageLoader(this, m_itemList, m_texSize, m_screenSize);
     1242        //Since total image time is longer/different than effect time, create image timers
     1243        m_effect_kenBurns_image_time[m_texCur ? 0 : 1].restart();
     1244        // Pan image to a random location
     1245        FindRandXY(m_effect_kenBurns_location_x[0], m_effect_kenBurns_location_y[0]);
     1246        // Since first two images are preloaded, hardcode  them to zoom in
     1247        m_effect_kenBurns_projection[0] = 1;
     1248        m_effect_kenBurns_projection[1] = 1;
     1249        m_effect_kenBurns_image_timeout = m_effect_transition_timeout +
     1250                (m_effect_transition_timeout * trans_pct);
     1251        m_effect_kenBurns_face_x = 0.0;
     1252        m_effect_kenBurns_face_y = 0.0;
     1253    }
     1254
     1255    if (m_effect_frame_time.elapsed() >= m_effect_transition_timeout)
     1256    {
     1257        // Effect timed out, move new image to old image but don't load new image yet...
     1258        m_tex1First = !m_tex1First;
     1259        m_texCur      = (m_texCur) ? 0 : 1;
     1260        m_effect_current_frame  = 0;
     1261        m_effect_frame_time.restart();
     1262
     1263        m_effect_kenBurns_image_ready = false;
     1264        m_effect_kenBurns_face_x = 0.0;
     1265        m_effect_kenBurns_face_y = 0.0;
     1266
     1267        // Find next image to be loaded
     1268        int oldpos = m_pos;
     1269
     1270        while (true)
     1271        {
     1272            m_pos = m_slideshow_sequence->next();
     1273            m_effect_kenBurns_item = m_itemList.at(m_pos);
     1274            if (m_effect_kenBurns_item)
     1275            {
     1276                // Skip movies
     1277                if (QFile::exists(m_effect_kenBurns_item->GetPath()) && !GalleryUtil::IsMovie(m_effect_kenBurns_item->GetPath()))
     1278                {
     1279                    break;
     1280                }
     1281            }
     1282            if (m_pos == oldpos)
     1283            {
     1284                // No valid items!!!
     1285                close();
     1286            }
     1287        }
     1288        m_effect_kenBurns_imageLoadThread->Initialize(m_pos);
     1289        m_effect_kenBurns_imageLoadThread->start();
     1290    }
     1291
     1292    float t[2], elapsed[2], s[2], effect_pct;
     1293    elapsed[m_texCur] = m_effect_kenBurns_image_time[m_texCur].elapsed();
     1294    elapsed[m_texCur ? 0 : 1] = m_effect_kenBurns_image_time[m_texCur ? 0 : 1].elapsed();
     1295    //progress linearly
     1296    t[m_texCur] = elapsed[m_texCur] / m_effect_kenBurns_image_timeout;
     1297    t[m_texCur ? 0 : 1] = elapsed[m_texCur ? 0 : 1] / m_effect_kenBurns_image_timeout;
     1298    //progress faster initially then slowing down- this is needed to ensure images zoom faster than they pan and
     1299    //therefore stay completely on the screen
     1300    s[m_texCur] = sqrt(elapsed[m_texCur]) / sqrt(m_effect_kenBurns_image_timeout);
     1301    s[m_texCur ? 0 : 1] = sqrt(elapsed[m_texCur ? 0 : 1]) / sqrt(m_effect_kenBurns_image_timeout);
     1302   
     1303    effect_pct = m_effect_frame_time.elapsed() *  m_effect_transition_timeout_inv;
     1304
     1305    // Load new image if its ready
     1306    if (effect_pct > single_image_pct && m_effect_kenBurns_image_ready)
     1307    {
     1308        if (!m_effect_kenBurns_new_image_started)
     1309        {                       
     1310            if (m_effect_kenBurns_item) //Do not create textures for first two images, since they are preloaded
     1311            {
     1312                m_texItem[!m_tex1First].SetItem(m_effect_kenBurns_item, m_effect_kenBurns_orig_image_size);
     1313                m_texItem[!m_tex1First].ScaleTo(m_screenSize, m_scaleMax);
     1314                m_texItem[!m_tex1First].Init(m_effect_kenBurns_image);
     1315                UpdateLCD(m_effect_kenBurns_item);
     1316               
     1317                // If there is no face in this image
     1318                if ((m_effect_kenBurns_face_x == 0.0) && (m_effect_kenBurns_face_x == 0.0))
     1319                {
     1320                    //choose the location and projection (zoom in or out) randomly
     1321                    FindRandXY(m_effect_kenBurns_location_x[m_texCur], m_effect_kenBurns_location_y[m_texCur]);
     1322                    m_effect_kenBurns_projection[m_texCur] = 1 + (int)((2.0f * rand() / (RAND_MAX + 1.0f)));
     1323                }
     1324                //else if the face is  close to center
     1325                else if ((m_effect_kenBurns_face_x < 0.25) && (m_effect_kenBurns_face_x > -0.25) &&
     1326                        (m_effect_kenBurns_face_y < 0.25) && (m_effect_kenBurns_face_y > -0.25))
     1327                {                   
     1328                    //start at random location and zoom out to face in center
     1329                    FindRandXY(m_effect_kenBurns_location_x[m_texCur], m_effect_kenBurns_location_y[m_texCur]);
     1330                    m_effect_kenBurns_projection[m_texCur] = 0;
     1331                }
     1332                else
     1333                {
     1334                    //start in center and zoom in to face random location
     1335                    m_effect_kenBurns_location_x[m_texCur] = m_effect_kenBurns_face_x;
     1336                    m_effect_kenBurns_location_y[m_texCur] = m_effect_kenBurns_face_y;
     1337                    m_effect_kenBurns_projection[m_texCur] = 1;               
     1338                }
     1339            }
     1340            else  //No item, must be 1 of the first two preloaded items
     1341            {
     1342                //start at random location and zoom out to face in center
     1343                FindRandXY(m_effect_kenBurns_location_x[m_texCur], m_effect_kenBurns_location_y[m_texCur]);
     1344                m_effect_kenBurns_projection[m_texCur] = 1;
     1345            }
     1346
     1347            m_effect_kenBurns_image_time[m_texCur].restart();
     1348            m_effect_kenBurns_new_image_started = true;
     1349        }
     1350        if (m_effect_kenBurns_projection[m_texCur] == 1) // Zoom in image
     1351        {
     1352            // Start in center and pan out
     1353            x_loc = m_effect_kenBurns_location_x[m_texCur] * t[m_texCur];
     1354            y_loc = m_effect_kenBurns_location_y[m_texCur] * t[m_texCur];               
     1355            scale_max = FindMaxScale(x_loc,y_loc);
     1356            scale_factor =      1.0f + (scale_max * s[m_texCur]);
     1357        }
     1358        else // Zoom out image
     1359        {
     1360            // Start at random location and pan to center
     1361            x_loc = m_effect_kenBurns_location_x[m_texCur] -  m_effect_kenBurns_location_x[m_texCur] * t[m_texCur];
     1362            y_loc = m_effect_kenBurns_location_y[m_texCur] -  m_effect_kenBurns_location_y[m_texCur] * t[m_texCur];
     1363            scale_max = FindMaxScale(x_loc,y_loc);
     1364            scale_factor =      1.0f + scale_max -  (scale_max * t[m_texCur]);
     1365        }
     1366
     1367        glMatrixMode(GL_MODELVIEW);
     1368        glLoadIdentity();
     1369        glTranslatef(x_loc, y_loc, 0.0f);
     1370
     1371        m_texItem[m_texCur].MakeQuad((effect_pct-single_image_pct)*4, scale_factor);
     1372    }
     1373   
     1374    //Load old picture
     1375    if (m_effect_kenBurns_projection[m_texCur ? 0 : 1] == 1)// Zoom in image
     1376    {
     1377        x_loc = m_effect_kenBurns_location_x[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1];
     1378        y_loc = m_effect_kenBurns_location_y[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1];
     1379        scale_max = FindMaxScale(x_loc,y_loc);
     1380        scale_factor =  1.0f + (scale_max * s[m_texCur ? 0 : 1]);
     1381    }
     1382    else // Zoom out image
     1383    {
     1384        x_loc = m_effect_kenBurns_location_x[m_texCur ? 0 : 1] - 
     1385            m_effect_kenBurns_location_x[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1];
     1386        y_loc = m_effect_kenBurns_location_y[m_texCur ? 0 : 1] - 
     1387            m_effect_kenBurns_location_y[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1];
     1388        scale_max = FindMaxScale(x_loc,y_loc);
     1389        scale_factor =  1.0f + scale_max -  (scale_max * t[m_texCur ? 0 : 1]);
     1390    }
     1391
     1392    glMatrixMode(GL_MODELVIEW);
     1393    glLoadIdentity();
     1394    glTranslatef(x_loc, y_loc, 0.0f);
     1395
     1396    if (effect_pct<= single_image_pct)
     1397    {
     1398        m_effect_kenBurns_new_image_started=false;
     1399        m_texItem[m_texCur ? 0 : 1].MakeQuad(1.0f, scale_factor); //
     1400    }
     1401    else // Fade out image
     1402    {
     1403        m_texItem[m_texCur ? 0 : 1].MakeQuad(1.0f - ((effect_pct-single_image_pct)*4), scale_factor);
     1404
     1405    }
     1406   
     1407    m_effect_current_frame++;
     1408}
     1409
    12141410void GLSingleView::SlideTimeout(void)
    12151411{
    12161412    bool wasMovie = false, isMovie = false;
     
    13171513    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    13181514    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    13191515}
     1516
     1517void GLSingleView::LoadImage(QImage image, QSize origSize, Face *face)
     1518{
     1519    m_effect_kenBurns_image = image;
     1520    m_effect_kenBurns_orig_image_size = origSize;
     1521    if (face)
     1522    {
     1523        VERBOSE(VB_IMPORTANT, QString("Found face, set location x '%1' y '%2'").arg(face->getX()).arg(face->getY()));
     1524        m_effect_kenBurns_face_x = face->getX();
     1525        m_effect_kenBurns_face_y = face->getY();
     1526    }
     1527}
     1528
     1529float GLSingleView::FindMaxScale(float x_loc, float y_loc)
     1530{
     1531    // Zoom big enough to keep the entire image on screen when we pan
     1532    if (abs(x_loc) > abs(y_loc))
     1533        return abs(x_loc) * 2;
     1534    else
     1535        return abs(y_loc) * 2;
     1536}
     1537
     1538void GLSingleView::FindRandXY(float &x_loc, float &y_loc)
     1539{
     1540    x_loc = (0.5 * rand() / (RAND_MAX + 1.0f)) + 0.25;  //Random number between .25 and .75
     1541    if ((int)(2.0 * rand() / (RAND_MAX + 1.0f)) == 0)
     1542        x_loc = -1 * x_loc;
     1543    y_loc = (0.5 * rand() / (RAND_MAX + 1.0f)) + 0.25;  //Random number between .25 and .75
     1544    if ((int)(2.0 * rand() / (RAND_MAX + 1.0f)) == 0)
     1545        y_loc = -1 * y_loc;   
     1546}
     1547
     1548KenBurnsImageLoader::KenBurnsImageLoader(GLSingleView *singleView, ThumbList &itemList, QSize texSize, QSize screenSize)
     1549{
     1550    m_singleView = singleView;
     1551    m_itemList = itemList;
     1552    m_texSize = texSize;
     1553    m_screenSize = screenSize;
     1554}
     1555
     1556void KenBurnsImageLoader::Initialize(int pos)
     1557{
     1558    m_pos = pos;
     1559}
     1560
     1561void KenBurnsImageLoader::run()
     1562{
     1563    ThumbItem *item = m_itemList.at(m_pos);
     1564    if (!item)
     1565    {
     1566        VERBOSE(VB_IMPORTANT, LOC_ERR + "No item at "<<m_pos);
     1567        return;
     1568    }
     1569    QImage image(item->GetPath());
     1570    if (image.isNull())
     1571        return;
     1572   
     1573#ifdef FDLIB_SUPPORT
     1574   
     1575    int i, n, x[256], y[256], size[256], w, h, threshold;
     1576    float loc_x, loc_y;
     1577    unsigned char *bgrdata, *graydata;
     1578   
     1579    w = image.width();
     1580    h = image.height();
     1581    bgrdata = image.bits();
     1582 
     1583    graydata = new unsigned char[w*h];
     1584    for (i=0; i<w*h; i++)
     1585        graydata[i] = (unsigned char) ((.11*bgrdata[4*i] + .59*bgrdata[4*i+1] + .3*bgrdata[4*i+2]));
     1586   
     1587   
     1588    threshold = 0;
     1589    fdlib_detectfaces(graydata, w, h, threshold);   
     1590    delete[] graydata;       
     1591    n = fdlib_getndetections();
     1592    if (n==0)
     1593    {
     1594        VERBOSE(VB_IMPORTANT, QString("%1 face found.").arg(n));
     1595        m_singleView->LoadImage(QGLWidget::convertToGLFormat(image.scaled(m_texSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)), image.size());
     1596    }
     1597    else if (n==1)
     1598    {
     1599        VERBOSE(VB_IMPORTANT, QString("%1 faces found.").arg(n));
     1600
     1601        fdlib_getdetection(0, x, y, size);
     1602        loc_x = (x[0]-(float)w/2)/((float)w/2)*(-1);
     1603        loc_y = (y[0]-(float)h/2)/((float)h/2)*(-1);
     1604        VERBOSE(VB_IMPORTANT, QString("x:%1 y:%2 size:%3").arg(x[0]).arg(y[0]).arg(size[0]));
     1605        m_singleView->LoadImage(QGLWidget::convertToGLFormat(image.scaled(m_texSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)), image.size(), new Face(loc_x, loc_y));
     1606    }
     1607    else
     1608    {
     1609        VERBOSE(VB_IMPORTANT, QString("%1 faces found.").arg(n));
     1610        QList<Face*> faces;
     1611        int faceIndex;
     1612   
     1613        for (i=0; i<n; i++)
     1614        {
     1615            fdlib_getdetection(i, x+i, y+i, size+i);
     1616            loc_x = (x[i]-(float)w/2)/((float)w/2)*(-1);
     1617            loc_y = (y[i]-(float)h/2)/((float)h/2)*(-1);
     1618            VERBOSE(VB_IMPORTANT, QString("x:%1 y:%2 size:%3").arg(x[i]).arg(y[i]).arg(size[i]));
     1619            faces.append(new Face(loc_x, loc_y));
     1620        }
     1621        if (faces.count() > 1)
     1622            faceIndex = (int) ((faces.count()+1) * rand() / (RAND_MAX + 1.0f)); //
     1623        else
     1624            faceIndex = 0;
     1625           
     1626        m_singleView->LoadImage(QGLWidget::convertToGLFormat(image.scaled(m_texSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)), image.size(), faces.at(faceIndex));
     1627    }
     1628   
     1629#else  //END FDLIB_SUPPORT
     1630    m_singleView->LoadImage(QGLWidget::convertToGLFormat(image.scaled(m_texSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)), image.size());   
     1631#endif
     1632    m_singleView->Ready();
     1633
     1634}
     1635
     1636Face::Face(float x, float y)
     1637{
     1638    m_x = x;
     1639    m_y = y;
     1640}
  • mythgallery/glsingleview.h

     
    2323#define GLSINGLEVIEW_H
    2424#ifdef USING_OPENGL
    2525
     26
    2627// MythTV plugin headers
    2728#include <mythtv/util.h>
    2829#include <mythtv/mythdialogs.h>
     
    3738class QTimer;
    3839
    3940class GLSingleView;
     41class KenBurnsImageLoader;
     42class Face;
    4043
    4144class GLSDialog : public MythDialog
    4245{
     
    6265    ~GLSingleView();
    6366
    6467    void CleanUp(void);
     68    void Ready(){m_effect_kenBurns_image_ready = true;}
     69    void LoadImage(QImage image, QSize origSize, Face *face = NULL);
     70   
    6571
    6672  protected:
    6773    void initializeGL(void);
     
    99105    void EffectSlide(void);
    100106    void EffectFlutter(void);
    101107    void EffectCube(void);
    102 
     108    void EffectKenBurns(void);
     109 
     110  private:
     111        float FindMaxScale(float x_loc, float y_loc);
     112        void FindRandXY(float &x_loc, float &y_loc);
     113   
    103114  private slots:
    104115    void SlideTimeout(void);
    105116
     
    130141    float         m_effect_cube_xrot;
    131142    float         m_effect_cube_yrot;
    132143    float         m_effect_cube_zrot;
     144    float         m_effect_kenBurns_location_x[2];
     145    float         m_effect_kenBurns_location_y[2];
     146    int           m_effect_kenBurns_projection[2];
     147    MythTimer     m_effect_kenBurns_image_time[2];
     148    float         m_effect_kenBurns_image_timeout;
     149    KenBurnsImageLoader *m_effect_kenBurns_imageLoadThread;
     150    bool          m_effect_kenBurns_image_ready;
     151    QImage        m_effect_kenBurns_image;
     152    QSize         m_effect_kenBurns_orig_image_size;
     153    float         m_effect_kenBurns_face_x;
     154    float         m_effect_kenBurns_face_y;
     155    ThumbItem     *m_effect_kenBurns_item;
     156    bool          m_effect_kenBurns_initialized;
     157    bool          m_effect_kenBurns_new_image_started;
     158   
    133159};
    134160
     161class KenBurnsImageLoader : public QThread
     162{
     163public:
     164    KenBurnsImageLoader(GLSingleView *singleView, ThumbList &itemList, QSize m_texSize, QSize m_screenSize);
     165    void Initialize(int pos);
     166    void run();
     167private:
     168        GLSingleView *m_singleView;
     169    ThumbList     m_itemList;
     170    int           m_pos;
     171    bool          m_tex1First;
     172    QSize         m_screenSize;
     173    QSize         m_texSize;
     174
     175};
     176
     177class Face
     178{
     179  public:
     180    Face(float x, float y);
     181    float getX() { return m_x; }
     182    float getY() { return m_y; }
     183  private:
     184      float m_x;
     185      float m_y;
     186};
     187
    135188#endif // USING_OPENGL
    136189#endif // GLSINGLEVIEW_H
  • mythgallery/imageview.cpp

     
    139139{
    140140    QMap<QString,QString> tmpMap = m_effect_map;
    141141    tmpMap.remove("none");
     142    tmpMap.remove("Ken Burns (gl)");
    142143    QStringList t = tmpMap.keys();
    143144    int i = (int) ( (float)(t.count()) * rand() / (RAND_MAX + 1.0f) );
    144145    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 "
  • README

     
    3535Current, EXIF support only consists of auto-rotating images if the camera
    3636sets the orientation tag (My Canon S400 does)
    3737
     38You can also enable fdlib support using
     39./configure --enable-fdlib
     40fdlib - is a facial recognition library that is used as part of the ken
     41burns slide show effect. It tries to ensure that slide shows pan/zoom
     42to peoples faces rather than their feet or off to the background.
     43fdlib can be downloaded from:
     44http://www.kyb.mpg.de/bs/people/kienzle/fdlib/fdlib.htm
     45It will require the gcc compatibility packages. Once downloaded
     46fdlib.h should be installed in your headers directory (/usr/include)
     47libfd.so should be installed in your libs directory (/usr/lib)
     48fdlib.dat should be installed in your current working directory when
     49mythfrontend is started for me it was (/home/myth). Please note that
     50this is not probably not the same directory where mythfrontend actually
     51resides. If fdlib does not find fdlib.dat, it will fail silently and just
     52not find any faces.
     53
    38542) Next type 'qmake mythgallery.pro' then 'make' in the
    3955main distribution directory.
    4056